Merge remote-tracking branch 'origin/main' into claude/ios-features-android-port-ebe483

# Conflicts:
#	app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt
This commit is contained in:
callebtc 2026-10-26 12:40:40 +01:00
commit 57bb99c170
81 changed files with 3027 additions and 673 deletions

View File

@ -6,6 +6,9 @@ plugins {
alias(libs.plugins.kotlin.compose)
}
// Opt-in instrumentation of the minified variant; production release signing is unchanged.
val releaseUiTests = providers.gradleProperty("releaseUiTests").orNull == "true"
val githubReleaseCertSha256 = providers
.environmentVariable("BITCHAT_GITHUB_RELEASE_CERT_SHA256")
.orElse(providers.gradleProperty("BITCHAT_GITHUB_RELEASE_CERT_SHA256"))
@ -25,6 +28,7 @@ android {
namespace = "com.bitchat.android"
compileSdk = libs.versions.compileSdk.get().toInt()
buildToolsVersion = libs.versions.buildTools.get()
if (releaseUiTests) testBuildType = "release"
defaultConfig {
applicationId = "com.bitchat.droid"
@ -38,7 +42,11 @@ android {
"\"$normalizedGithubReleaseCertSha256\""
)
testInstrumentationRunner = "com.bitchat.android.ui.PerformanceTestRunner"
testInstrumentationRunner = if (releaseUiTests) {
"com.bitchat.android.ui.ReleaseScannerTestRunner"
} else {
"com.bitchat.android.ui.PerformanceTestRunner"
}
vectorDrawables {
useSupportLibrary = true
}
@ -59,6 +67,10 @@ android {
}
}
release {
if (releaseUiTests) {
signingConfig = signingConfigs.getByName("debug")
proguardFiles("release-instrumentation.pro")
}
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
@ -192,6 +204,8 @@ dependencies {
// Testing
testImplementation(libs.bundles.testing)
testImplementation(platform(libs.androidx.compose.bom))
testImplementation(libs.androidx.compose.ui.test.junit4)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.test.runner)

View File

@ -2,178 +2,178 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
# To regenerate this file, run: ./gradlew :app:dependencies --write-locks
androidx.activity:activity-compose:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.activity:activity-ktx:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.activity:activity:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation-experimental:1.4.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation-jvm:1.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation:1.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.appcompat:appcompat-resources:1.7.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.appcompat:appcompat:1.7.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,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.autofill:autofill:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.activity:activity-compose:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.activity:activity-ktx:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.activity:activity:1.13.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation-experimental:1.4.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation-jvm:1.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.annotation:annotation:1.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.appcompat:appcompat-resources:1.7.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.appcompat:appcompat:1.7.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.arch.core:core-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.arch.core:core-runtime:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.autofill:autofill:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.featurecombinationquery:featurecombinationquery:1.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-compose:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-core:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-compose:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-core:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2-pipe:1.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-compose:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-core:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-lifecycle:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection-jvm:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection-ktx:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-core-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-core:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-layout-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-layout:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material3:material3-android:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material3:material3:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-core-android:1.7.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-compose:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-core:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-lifecycle:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection-jvm:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection-ktx:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-core-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation-core:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.animation:animation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-layout-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation-layout:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.foundation:foundation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material3:material3-android:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material3:material3:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-core-android:1.7.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-core-desktop:1.7.8=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugUnitTestLintChecksClasspath,releaseLintChecksClasspath
androidx.compose.material:material-icons-core:1.7.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-extended-android:1.7.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-core:1.7.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-extended-android:1.7.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-extended-desktop:1.7.8=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugUnitTestLintChecksClasspath,releaseLintChecksClasspath
androidx.compose.material:material-icons-extended:1.7.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-ripple-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-ripple:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-annotation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-annotation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-retain-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-retain:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-saveable-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-saveable:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-geometry-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-geometry:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-graphics-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-graphics:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-test-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-junit4-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-junit4:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-manifest:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-test:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.compose.ui:ui-text-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-text:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-icons-extended:1.7.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-ripple-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.material:material-ripple:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-annotation-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-annotation:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-retain-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-retain:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-saveable-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime-saveable:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.runtime:runtime:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-geometry-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-geometry:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-graphics-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-graphics:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-test-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-junit4-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-junit4:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.compose.ui:ui-test-manifest:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-test:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.compose.ui:ui-text-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-text:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-tooling-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-tooling-data-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-tooling-data:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-tooling-preview-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-tooling-preview:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-tooling-preview-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-tooling-preview:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-tooling:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.compose.ui:ui-unit-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-unit:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-util-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-util:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose:compose-bom:2026.06.01=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-unit-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-unit:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-util-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui-util:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose:compose-bom:2026.06.01=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.1.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.2.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.2.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.core:core-backported-fixes:1.0.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.19.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-viewtree:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core:1.19.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.cursoradapter:cursoradapter:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.customview:customview-poolingcontainer:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.documentfile:documentfile:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.drawerlayout:drawerlayout:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.dynamicanimation:dynamicanimation:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.19.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-viewtree:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core:1.19.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.cursoradapter:cursoradapter:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.customview:customview-poolingcontainer:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.documentfile:documentfile:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.drawerlayout:drawerlayout:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.dynamicanimation:dynamicanimation:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.emoji2:emoji2-views-helper:1.4.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.emoji2:emoji2:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.exifinterface:exifinterface:1.4.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.fragment:fragment:1.5.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.graphics:graphics-path:1.0.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.interpolator:interpolator:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.legacy:legacy-support-core-utils:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.emoji2:emoji2:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.exifinterface:exifinterface:1.4.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.fragment:fragment:1.5.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.graphics:graphics-path:1.0.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.interpolator:interpolator:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.legacy:legacy-support-core-utils:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common-java8:2.11.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common-jvm:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-process:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-compose-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-compose:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-ktx-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common-jvm:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-livedata:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-process:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-compose-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-compose:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-ktx-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-runtime:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-service:2.11.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.11.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.localbroadcastmanager:localbroadcastmanager:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-common-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-common:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-compose-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-compose:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-runtime-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-runtime:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-android:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-compose-android:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-compose:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.print:print:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.profileinstaller:profileinstaller:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-ktx:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.localbroadcastmanager:localbroadcastmanager:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-common-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-common:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-compose-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-compose:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-runtime-android:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation:navigation-runtime:2.9.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-android:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-compose-android:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent-compose:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigationevent:navigationevent:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.print:print:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.profileinstaller:profileinstaller:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.resourceinspection:resourceinspection-annotation:1.0.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.room:room-common:2.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.room:room-ktx:2.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.room:room-runtime:2.6.1=debugAndroidTestLintChecksClasspath,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-compose-android:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-compose: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:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.security:security-crypto:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,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,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-compose-android:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-compose:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-ktx:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.security:security-crypto:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.sqlite:sqlite-framework:2.4.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.sqlite:sqlite:2.4.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.startup:startup-runtime:1.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.startup:startup-runtime:1.2.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.test.espresso:espresso-core:3.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.startup:startup-runtime:1.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath
androidx.startup:startup-runtime:1.2.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.test.espresso:espresso-core:3.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath
androidx.test.espresso:espresso-core:3.7.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test.espresso:espresso-idling-resource:3.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test.espresso:espresso-idling-resource:3.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath
androidx.test.espresso:espresso-idling-resource:3.7.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test.ext:junit:1.1.5=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test.ext:junit:1.1.5=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test.ext:junit:1.3.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test.services:storage:1.4.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test.services:storage:1.4.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test.services:storage:1.6.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test:annotation:1.0.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:core:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:annotation:1.0.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test:core:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test:core:1.7.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test:monitor:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:monitor:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test:monitor:1.8.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.test:runner:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:runner:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
androidx.test:runner:1.7.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
androidx.tracing:tracing-android:1.3.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing-android:1.3.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing-ktx:1.3.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath,releaseAndroidTestCompileClasspath
androidx.tracing:tracing:1.1.0=debugUnitTestCompileClasspath
androidx.tracing:tracing:1.3.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.transition:transition:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.vectordrawable:vectordrawable-animated:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.vectordrawable:vectordrawable:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window-core-android:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window-core:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.work:work-runtime-ktx:2.10.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.work:work-runtime:2.10.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing:1.3.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.transition:transition:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.vectordrawable:vectordrawable-animated:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.vectordrawable:vectordrawable:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window-core-android:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window-core:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.window:window:1.5.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.work:work-runtime-ktx:2.10.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.work:work-runtime:2.10.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.almworks.sqlite4java:sqlite4java:1.0.392=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.android.tools.analytics-library:protos:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.android.tools.analytics-library:shared:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
@ -218,16 +218,16 @@ com.android.tools:play-sdk-proto:32.3.1=androidLintTool
com.android.tools:repository:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.android.tools:sdk-common:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.android.tools:sdklib:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.google.accompanist:accompanist-permissions:0.37.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-api:2.2.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-backend-cct:2.3.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-runtime:2.2.6=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-base:18.9.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-basement:18.9.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-location:21.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-tasks:18.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.odml:image:1.0.0-beta1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.accompanist:accompanist-permissions:0.37.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-api:2.2.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-backend-cct:2.3.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.datatransport:transport-runtime:2.2.6=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-base:18.9.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-basement:18.9.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-location:21.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.gms:play-services-tasks:18.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android.odml:image:1.0.0-beta1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.android:annotations:4.1.1.4=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core
com.google.api.grpc:proto-google-common-protos:2.17.0=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core
com.google.api.grpc:proto-google-common-protos:2.48.0=unified-test-platform-android-test-plugin-host-emulator-control
@ -236,39 +236,39 @@ com.google.auto.service:auto-service:1.1.1=unified-test-platform-android-device-
com.google.auto.value:auto-value-annotations:1.11.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.google.auto.value:auto-value-annotations:1.6.3=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.auto:auto-common:1.2.1=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle
com.google.code.findbugs:jsr305:3.0.2=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
com.google.code.findbugs:jsr305:3.0.2=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
com.google.code.gson:gson:2.10.1=unified-test-platform-core,unified-test-platform-gradle-work-action
com.google.code.gson:gson:2.11.0=androidLintTool,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-result-listener-gradle
com.google.code.gson:gson:2.14.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.gson:gson:2.14.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.gson:gson:2.8.9=unified-test-platform-android-driver-instrumentation,unified-test-platform-launcher
com.google.crypto.tink:tink-android:1.23.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.crypto.tink:tink-android:1.23.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.crypto.tink:tink:1.18.0=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action
com.google.dagger:dagger:2.48=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action
com.google.dagger:dagger:2.59=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.errorprone:error_prone_annotation:2.38.0=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.23.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher
com.google.errorprone:error_prone_annotations:2.36.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
com.google.errorprone:error_prone_annotations:2.48.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-annotations:16.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-components:16.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-encoders-json:17.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-encoders:16.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.48.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-annotations:16.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-components:16.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-encoders-json:17.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.firebase:firebase-encoders:16.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:failureaccess:1.0.1=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher
com.google.guava:failureaccess:1.0.2=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
com.google.guava:failureaccess:1.0.3=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.google.guava:guava:32.0.1-jre=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher
com.google.guava:guava:33.4.0-jre=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
com.google.guava:guava:33.4.8-jre=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.google.guava:listenablefuture:1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:listenablefuture:1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
com.google.j2objc:j2objc-annotations:2.8=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
com.google.jimfs:jimfs:1.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.google.mlkit:barcode-scanning-common:17.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:barcode-scanning:17.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:common:18.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:vision-common:17.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:vision-interfaces:16.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:barcode-scanning-common:17.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:barcode-scanning:17.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:common:18.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:vision-common:17.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.mlkit:vision-interfaces:16.3.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.protobuf:protobuf-java-util:3.22.3=unified-test-platform-core
com.google.protobuf:protobuf-java-util:4.28.3=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher
com.google.protobuf:protobuf-java:3.25.5=androidLintTool
@ -281,14 +281,14 @@ com.google.testing.platform:core-proto:0.0.9-alpha04=unified-test-platform-andro
com.google.testing.platform:core:0.0.9-alpha04=unified-test-platform-core
com.google.testing.platform:launcher:0.0.9-alpha04=unified-test-platform-gradle-work-action,unified-test-platform-launcher
com.google.testparameterinjector:test-parameter-injector:1.18=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.google.zxing:core:3.5.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.zxing:core:3.5.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.ibm.icu:icu4j:77.1=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.squareup.okhttp3:mockwebserver3:5.4.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
com.squareup.okhttp3:okhttp-android:5.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okhttp3:okhttp:5.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okio:okio-jvm:3.17.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okio:okio:3.17.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup:javawriter:2.1.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.squareup.okhttp3:okhttp-android:5.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okhttp3:okhttp:5.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okio:okio-jvm:3.17.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup.okio:okio:3.17.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.squareup:javawriter:2.1.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath
com.sun.istack:istack-commons-runtime:3.0.8=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.sun.xml.fastinfoset:FastInfoset:1.2.16=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
commons-codec:commons-codec:1.17.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
@ -340,14 +340,14 @@ jakarta.activation:jakarta.activation-api:1.2.1=androidLintTool,unified-test-pla
jakarta.inject:jakarta.inject-api:2.0.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
javax.annotation:javax.annotation-api:1.3.2=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,unified-test-platform-android-test-plugin-host-emulator-control
javax.inject:javax.inject:1=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action
junit:junit:4.13.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
javax.inject:javax.inject:1=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action
junit:junit:4.13.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.17.7=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
net.java.dev.jna:jna-platform:5.6.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
net.java.dev.jna:jna:5.6.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
net.sf.kxml:kxml2:2.3.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
no.nordicsemi.android:ble:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
no.nordicsemi.android:ble:2.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.apache.commons:commons-compress:1.27.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.apache.commons:commons-lang3:3.16.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.apache.httpcomponents:httpclient:4.5.6=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
@ -355,7 +355,7 @@ org.apache.httpcomponents:httpcore:4.4.16=androidLintTool,unified-test-platform-
org.apache.httpcomponents:httpmime:4.5.6=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.bouncycastle:bcpkix-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.bouncycastle:bcprov-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.bouncycastle:bcprov-jdk18on:1.85=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.85=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.checkerframework:checker-qual:3.33.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher
org.checkerframework:checker-qual:3.43.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
@ -365,34 +365,34 @@ org.codehaus.mojo:animal-sniffer-annotations:1.24=unified-test-platform-android-
org.conscrypt:conscrypt-openjdk-uber:2.5.2=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.glassfish.jaxb:txw2:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestRuntimeClasspath
org.jetbrains.kotlin:compose-group-mapping:2.4.10=composeMappingProducerClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-build-tools-compat:2.4.10=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-cri-impl:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-cri-impl:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-impl:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-impl:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-compiler-runner:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-compiler-runner:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-compose-compiler-plugin-embeddable:2.4.10=kotlin-extension,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-compose-compiler-plugin-embeddable:2.4.10=kotlin-extension,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-daemon-client:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-daemon-client:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-parcelize-compiler:2.4.10=kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-parcelize-runtime:2.4.10=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugRuntimeClasspathCopy,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseRuntimeClasspathCopy
org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-daemon-embeddable:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-parcelize-compiler:2.4.10=kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-parcelize-runtime:2.4.10=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugRuntimeClasspathCopy,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseRuntimeClasspathCopy
org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-reflect:1.8.21=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlin:kotlin-reflect:2.1.20=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-reflect:2.2.10=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.jetbrains.kotlin:kotlin-script-runtime:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-script-runtime:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlin:kotlin-script-runtime:2.4.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21=unified-test-platform-android-test-plugin,unified-test-platform-core
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-launcher
org.jetbrains.kotlin:kotlin-stdlib-common:2.2.10=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-gradle-work-action
@ -408,35 +408,35 @@ org.jetbrains.kotlin:kotlin-stdlib:1.9.0=unified-test-platform-android-driver-in
org.jetbrains.kotlin:kotlin-stdlib:2.2.10=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=composeMappingProducerClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.10=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugRuntimeClasspathCopy,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseRuntimeClasspathCopy
org.jetbrains.kotlin:kotlin-stdlib:2.4.10=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugRuntimeClasspathCopy,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseRuntimeClasspathCopy
org.jetbrains.kotlin:kotlin-tooling-core:2.4.10=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:atomicfu-jvm:0.22.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlinx:atomicfu-jvm:0.28.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:atomicfu:0.22.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlinx:atomicfu:0.28.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains:annotations:13.0=composeMappingProducerClasspath,debugRuntimeClasspathCopy,kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,releaseRuntimeClasspathCopy
org.jetbrains:annotations:23.0.0=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jspecify:jspecify:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains:annotations:13.0=composeMappingProducerClasspath,debugRuntimeClasspathCopy,kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseAndroidTest,releaseRuntimeClasspathCopy
org.jetbrains:annotations:23.0.0=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher
org.jspecify:jspecify:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseAndroidTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jvnet.staxex:stax-ex:1.8.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
org.mockito.kotlin:mockito-kotlin:6.3.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.mockito:mockito-core:5.23.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.nanohttpd:nanohttpd:2.3.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.nanohttpd:nanohttpd:2.3.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseAndroidTestCompileClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.objenesis:objenesis:3.3=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.ow2.asm:asm-analysis:9.9=androidLintTool
org.ow2.asm:asm-commons:9.8=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
@ -461,4 +461,4 @@ org.robolectric:shadows-framework:4.15=debugUnitTestCompileClasspath,debugUnitTe
org.robolectric:utils-reflector:4.15=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.robolectric:utils:4.15=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.yaml:snakeyaml:2.4=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
empty=androidApis,androidJdkImage,androidTestUtil,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAnnotationProcessorClasspath,debugImplementationDependenciesMetadata,debugReverseMetadataValues,debugUnitTestAnnotationProcessorClasspath,debugUnitTestImplementationDependenciesMetadata,kotlinCompilerPluginClasspath,lintChecks,lintPublish,releaseAnnotationProcessorClasspath,releaseImplementationDependenciesMetadata,releaseReverseMetadataValues
empty=androidApis,androidJdkImage,androidTestUtil,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAnnotationProcessorClasspath,debugImplementationDependenciesMetadata,debugReverseMetadataValues,debugUnitTestAnnotationProcessorClasspath,debugUnitTestImplementationDependenciesMetadata,kotlinCompilerPluginClasspath,lintChecks,lintPublish,releaseAndroidTestAnnotationProcessorClasspath,releaseAnnotationProcessorClasspath,releaseImplementationDependenciesMetadata,releaseReverseMetadataValues

View File

@ -24,6 +24,14 @@
<init>();
}
# ML Kit discovers Firebase component registrars from manifest metadata and calls
# their no-argument constructors reflectively. Keeping the class alone does not
# retain that constructor in R8 full mode, leaving BarcodeScanning without its
# registered components and crashing when the verification scanner opens.
-keepclassmembers class * implements com.google.firebase.components.ComponentRegistrar {
public <init>();
}
# Keep Tor implementation (always included)
-keep class com.bitchat.android.net.RealTorProvider { *; }

View File

@ -0,0 +1,9 @@
# Keep only the real analyzer entry point for tests. Do not keep ML Kit internals:
# doing so would hide regressions in the production dependency-discovery graph.
-keep class com.bitchat.android.ui.QRCodeAnalyzer {
<init>(kotlin.jvm.functions.Function1);
public void analyze(androidx.camera.core.ImageProxy);
}
# CameraX's public frame interfaces are implemented by the synthetic test source.
-keep interface androidx.camera.core.ImageProxy { *; }
-keep interface androidx.camera.core.ImageInfo { *; }

View File

@ -0,0 +1,122 @@
package com.bitchat.android.ui
import android.graphics.ImageFormat
import android.media.ImageReader
import android.media.ImageWriter
import android.os.Handler
import android.os.HandlerThread
import androidx.camera.core.ImageInfo
import androidx.camera.core.ImageProxy
import java.lang.reflect.Proxy
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
/** Calls the same analyzer constructor as ScannerView, with minified ML Kit dependencies. */
class BarcodeScannerInstrumentedTest {
@Test
fun scannerCreationCompletesAfterColdStart() {
QRCodeAnalyzer { }
}
@Test
fun realAnalyzerDecodesSyntheticCameraFrameAndClosesIt() {
val decoded = AtomicReference<String>()
val codeReceived = CountDownLatch(1)
val frameClosed = CountDownLatch(1)
val analyzer = QRCodeAnalyzer {
decoded.set(it)
codeReceived.countDown()
}
val thread = HandlerThread("synthetic-scanner-frame").apply { start() }
// A fixed, synthetic QR encodes "bitchat:test-scanner". No camera or permissions needed.
val modules = java.util.ArrayList<String>()
val rows = java.util.StringTokenizer(QR_MODULES)
while (rows.hasMoreTokens()) modules.add(rows.nextToken())
val scale = 8
val size = modules.size * scale
val reader = ImageReader.newInstance(size, size, ImageFormat.YUV_420_888, 2)
val writer = ImageWriter.newInstance(reader.surface, 2)
try {
reader.setOnImageAvailableListener({ source ->
val image = source.acquireNextImage() ?: return@setOnImageAvailableListener
val info = Proxy.newProxyInstance(ImageInfo::class.java.classLoader,
arrayOf(ImageInfo::class.java)) { _, method, _ ->
when (method.name) {
"getRotationDegrees" -> 0
else -> error("Unexpected frame info: ${method.name}")
}
} as ImageInfo
val proxy = Proxy.newProxyInstance(ImageProxy::class.java.classLoader,
arrayOf(ImageProxy::class.java)) { _, method, _ ->
when (method.name) {
"getImage" -> image
"getImageInfo" -> info
"close" -> { image.close(); frameClosed.countDown(); null }
else -> error("Unexpected frame access: ${method.name}")
}
} as ImageProxy
analyzer.analyze(proxy)
}, Handler(thread.looper))
val input = writer.dequeueInputImage()
input.planes.forEachIndexed { planeIndex, plane ->
val planeSize = if (planeIndex == 0) size else size / 2
for (y in 0 until planeSize) for (x in 0 until planeSize) {
val value = if (planeIndex != 0) 128 else
if (modules[y / scale][x / scale] == '1') 0 else 255
plane.buffer.put(y * plane.rowStride + x * plane.pixelStride, value.toByte())
}
}
writer.queueInputImage(input)
assertTrue("Real release scanner must decode the frame", codeReceived.await(20, TimeUnit.SECONDS))
assertEquals("bitchat:test-scanner", decoded.get())
assertTrue("Analyzer must release the camera frame", frameClosed.await(5, TimeUnit.SECONDS))
} finally {
writer.close()
reader.close()
thread.quitSafely()
thread.join(5_000)
}
}
private companion object {
const val QR_MODULES = """
000000000000000000000000000000000
000000000000000000000000000000000
000000000000000000000000000000000
000000000000000000000000000000000
000011111110110001001011111110000
000010000010000001000010000010000
000010111010100111010010111010000
000010111010110101100010111010000
000010111010111100111010111010000
000010000010010011100010000010000
000011111110101010101011111110000
000000000000010100000000000000000
000011110010100000101100111010000
000011001001110000101001000100000
000011101010000001101000100000000
000010001001000111011100011100000
000010100011110100010111111000000
000000110101011101111011100110000
000001101110100010100001110100000
000010111101011011000100100000000
000000101111110100001111101100000
000000000000101011011000100010000
000011111110000110001010110110000
000010000010010010011000110100000
000010111010010111001111100100000
000010111010100011100111110110000
000010111010101111011010110100000
000010000010110000000101011000000
000011111110101010110011001110000
000000000000000000000000000000000
000000000000000000000000000000000
000000000000000000000000000000000
000000000000000000000000000000000
"""
}
}

View File

@ -0,0 +1,29 @@
package com.bitchat.android.ui
import android.app.Application
import android.app.Instrumentation
import android.content.Context
import android.os.Bundle
import org.junit.runner.JUnitCore
/** Minimal runner avoids instrumentation-only tracing dependencies removed by target R8. */
class ReleaseScannerTestRunner : Instrumentation() {
override fun newApplication(cl: ClassLoader, className: String, context: Context): Application =
super.newApplication(cl, Application::class.java.name, context)
override fun onCreate(arguments: Bundle?) {
super.onCreate(arguments)
start()
}
override fun onStart() {
val result = JUnitCore.runClasses(BarcodeScannerInstrumentedTest::class.java)
val report = Bundle().apply {
putString("stream", buildString {
append("Tests run: ${result.runCount}, failures: ${result.failureCount}\n")
result.failures.forEach { append(it.trace).append('\n') }
})
}
finish(if (result.wasSuccessful()) -1 else 0, report)
}
}

View File

@ -1,398 +1,378 @@
Relay URL,Latitude,Longitude
inbox.scuba323.com,40.8218,-74.45
relay.pornstr.net,53.3498,-6.26031
relay.btcforplebs.com,43.6532,-79.3832
nostr-01.yakihonne.com,1.32123,103.695
relay.agentry.com,42.8864,-78.8784
offchain.pub:443,39.1585,-94.5728
dev.relay.stream,43.6532,-79.3832
cyberspace.nostr1.com,40.7057,-74.0136
relay.shadowbip.com,54.352,18.6466
nip85-staging.nosfabrica.com,39.0997,-94.5786
portal-relay.pareto.space,53.5488,9.98717
relay.ru.ac.th,13.7607,100.627
nostr-relay.corb.net,39.6478,-104.988
nostrelay.circum.space,52.2245,8.826
relay.satsapp.me,50.4754,12.3683
nostr-01.uid.ovh,50.9871,2.12554
nostr.dzo-hadar.ts.net,35.6764,139.65
vault.iris.to:443,43.6532,-79.3832
relay.nmail.li,50.9871,2.12554
nostr.janx.com,43.6532,-79.3832
relay.satmaxt.xyz:443,43.6532,-79.3832
nos.lol:443,50.4754,12.3683
nostr.mad-social.net,50.4754,12.3683
relay.flashapp.me,43.6548,-79.3885
relay.layer.systems,43.6532,-79.3832
relay.novospes.com,43.6532,-79.3832
relay.satmaxt.xyz,43.6532,-79.3832
relay.getvia.xyz,60.1699,24.9384
vidar.nestoris.online,52.3676,4.90414
nostr.yutakobayashi.com,43.6532,-79.3832
dev-relay.nostreon.com,60.1699,24.9384
nostrcity-club.fly.dev:443,38.7946,-106.535
nostr.oxtr.dev,50.4754,12.3683
relay.lacrypta.ar,43.6532,-79.3832
node.kommonzenze.de,49.4521,11.0767
bitchat.nostr1.com,40.7057,-74.0136
nostr.trepechov.com,49.4543,11.0746
articles.layer3.news:443,37.3387,-121.885
relay.ditto.pub,43.6532,-79.3832
treuzkas.branruz.com,48.8575,2.35138
nostr.plantroon.com:443,50.1013,8.62643
nostr.mas-family.eu,60.3478,15.7505
relay.nostrfy.org,35.6916,139.768
relay.nostrmap.net,60.1699,24.9384
woods-leader-permits-yamaha.trycloudflare.com,43.6532,-79.3832
nostr.middling.mydns.jp,35.8099,140.12
adre.su,59.9311,30.3609
relay.s-w.art,43.6532,-79.3832
nostr.thebiglake.org,32.71,-96.6745
relay.satlantis.io,39.0438,-77.4874
relay.routstr.com,59.4016,17.9455
relay.nostr.space,43.6532,-79.3832
relay.wavlake.com:443,41.2619,-95.8608
relay.islandbitcoin.com,20.3898,78.0903
nostr.tabordalab.com,60.1699,24.9384
nostr-relay.cbrx.io,43.6532,-79.3832
testnet.samt.st,43.6532,-79.3832
temp.iris.to,43.6532,-79.3832
freelay.sovbit.host,60.1699,24.9384
relay.grammstr.com,38.7946,-106.535
relay.nostronchain.com,43.6532,-79.3832
relay.hazeem.xyz,43.6532,-79.3832
relayone.geektank.ai,39.0997,-94.5786
nostr-relay.corb.net:443,39.6478,-104.988
relay.olas.app,60.1699,24.9384
relay.44billion.net,43.6532,-79.3832
relay.mitosystem.net,-23.5558,-46.6396
relay.ltgnet.work:8443,51.0511,-114.075
nostr.chaima.info:443,50.1109,8.68213
nostrcity-club.fly.dev,38.7946,-106.535
nostr.sathoarder.com,48.5734,7.75211
relay.fountain.fm:443,43.6532,-79.3832
nostr.txlyre.website,60.1699,24.9384
nostr.planix.org,43.6532,-79.3832
relay.chorus.community,50.1109,8.68213
dev-relay.nostr.space,43.6532,-79.3832
relay.satsmarkt.club,52.3676,4.90414
relay.bullishbounty.com,43.6532,-79.3832
relay.lanacoin-eternity.com,40.8302,-74.1299
social.amanah.eblessing.co,48.1046,11.6002
relay-arg.zombi.cloudrodion.com,1.35208,103.82
relay.primal.net,43.6532,-79.3832
nostr-relay.zimage.com,34.282,-118.439
relay.veganostr.com:443,60.1699,24.9384
relay.hivetalk.org,40.8302,-74.1299
nostr.whitenode45.ddns.net,40.55,-74.4758
nostr-rs-relay-ishosta.phamthanh.me,43.6532,-79.3832
relay.edufeed.org,49.4521,11.0767
nostr.pbfs.io:443,50.4754,12.3683
relay.substation.ninja,43.6532,-79.3832
nostr.liberty.fans,36.8767,-89.5879
nostr-verified.wellorder.net,45.5201,-122.99
relay.hivescope.xyz,40.4332,-3.62431
nostr2.girino.org:443,43.6532,-79.3832
relay.cloistr.xyz,42.337,-77.3178
relay.agora.social,50.7383,15.0648
relayone.soundhsa.com,39.0997,-94.5786
nostr.0x7e.xyz,47.4949,8.71954
nostr-rs-relay.dev.fedibtc.com:443,39.0438,-77.4874
relay-na1.metanomalist.com,43.6532,-79.3832
relay.bitmacro.cloud,43.6532,-79.3832
relay.iyou.me,64.1466,-21.9426
nostr.bitcoiner.social,47.6743,-117.112
relay.wisp.talk:443,49.4543,11.0746
relay.valleirug.nl,52.0274,5.62427
chat-relay.zap-work.com,43.6532,-79.3832
nostr.2b9t.xyz,34.0549,-118.243
groups.beginningend.com,35.2227,-97.4786
relay.edufeed.org:443,49.4521,11.0767
nrs-02.darkcloudarcade.com,39.9526,-75.1652
relay.angor.io,48.1046,11.6002
relay.jmoose.rocks,32.7767,-96.797
nostr.mom,50.4754,12.3683
x.kojira.io,43.6532,-79.3832
cs-relay.nostrdev.com:443,50.4754,12.3683
nostr.computingcache.com,45.5341,-122.956
relay-dev.satlantis.io,39.0438,-77.4874
nostr.jcera.eu,43.6532,-79.3832
relay.aarpia.com,37.3986,-121.964
relay.nostrian-conquest.com,41.223,-111.974
relay.fiatdenier.com,43.7787,-79.5393
relayrs.notoshi.win:443,43.6532,-79.3832
bread.nostrsms.com,40.8218,-74.45
test.relays.nuts.cash,52.3676,4.90414
nostrrelay.taylorperron.com,45.5029,-73.5723
relay.jmoose.rocks:443,32.7767,-96.797
relay.tapestry.ninja,40.8054,-74.0241
nostr.hetuniversum.nl,43.6532,-79.3832
public.crostr.com:443,43.6532,-79.3832
nostr.trevena.info,43.6532,-79.3832
relay.chorus.community:443,50.1109,8.68213
relay.sharegap.net,43.6532,-79.3832
relayone.geektank.ai:443,39.0997,-94.5786
nip85.nosfabrica.com,39.0997,-94.5786
yabu.me,35.6096,139.73
nostr.bitcoiner.social:443,47.6743,-117.112
support.flotilla.social,32.9483,-96.7299
strfry.bonsai.com:443,39.0438,-77.4874
nostr-relayflare.enpitsulin.workers.dev,43.6532,-79.3832
relay.crovaxon.de,53.0777,7.41516
relay.froth.zone,60.1699,24.9384
r.senx.io,50.6924,3.20113
relay2.nostrasia.net,43.6532,-79.3832
relay.decentralia.fr,48.122,11.589
relay.mappingbitcoin.com,43.6532,-79.3832
nosflare-leefcore.leefcore.workers.dev,43.6532,-79.3832
chorus.mikedilger.com:444,-36.8906,174.794
nostr.wild-vibes.ts.net,48.8566,2.35222
spamspamspamspam.rest,43.6532,-79.3832
relay.wisp.talk,49.4543,11.0746
relay.bornheimer.app,50.1109,8.68213
nostr-dev.wellorder.net,45.5201,-122.99
strfry.shock.network:443,39.0438,-77.4874
nostr.mikoshi.de,52.52,13.405
nostr.christiansass.de,50.1109,8.68213
relay.14898.hns.one,42.8864,-78.8784
porchlight.social,43.6532,-79.3832
nostr.twinkle.lol,51.902,7.6657
nostr.pbfs.io,50.4754,12.3683
zencrown.relay2aeon.quest,52.3061,4.6907
21milionidinostr.duckdns.org,45.519,9.08615
test-relay.uid.ovh,48.5734,7.75211
nexus.libernet.app,43.6532,-79.3832
bitcoiner.social,47.6743,-117.112
syb.lol,43.6532,-79.3832
relay.directsponsor.net,42.8864,-78.8784
budabit.nostr1.com,40.7057,-74.0136
relay.budabit.club,49.4543,11.0746
relay.monostr.com,50.4754,12.3683
relay.rebelbase.social,50.965,5.50082
nostr.hifish.org,47.4009,8.57814
nostr.21crypto.ch,47.5356,8.73209
nostr.emanuelemiani.it,45.778,8.79414
relay.kizuna-miki.com,35.694,139.754
relay.binaryrobot.com:443,43.6532,-79.3832
spurline.safebox.dev,43.6532,-79.3832
buzz.cashu.space,43.6532,-79.3832
relay.plebeian.market,50.1109,8.68213
relay.mleku.dev,32.7767,-96.797
nostr.purpura.cloud,43.6532,-79.3832
relay.binaryrobot.com,43.6532,-79.3832
relay.paulstephenborile.com,49.4543,11.0746
relay2.fiatdenier.com,50.1013,8.62643
cs-relay.nostrdev.com,50.4754,12.3683
nostr.davenov.com,50.1109,8.68213
relay.nostrhub.fr,48.1045,11.6004
nostr.spaceshell.xyz,43.6532,-79.3832
relay.gorrdy.cz,50.1109,8.68213
relay.nostrmap.net:443,60.1699,24.9384
relay.getsafebox.app,43.6532,-79.3832
relay.lanavault.space,60.1699,24.9384
relay.veganostr.com,60.1699,24.9384
prl.plus,54.9486,21.7283
relay.nostr.blockhenge.com,39.0438,-77.4874
nostr.quali.chat,60.1699,24.9384
nostr.nodesmap.com,59.3327,18.0656
relay.beginningend.com,35.2227,-97.4786
relay2.0xprivacy.online,43.6532,-79.3832
relay1.privkey.io,25.7617,-80.1918
relay-dev.gulugulu.moe:443,43.6532,-79.3832
relay.lotek-distro.com,43.6532,-79.3832
relay.noeudlibre.fr,50.6924,3.20113
relay.cyberguy.fyi,52.3676,4.90414
nostr-relay.xbytez.io,50.6924,3.20113
relay.kaleidoswap.com,47.3769,8.54169
relay.bnos.space,43.6532,-79.3832
purplerelay.com,43.6532,-79.3832
hasky.chat,40.7862,-74.0743
relay.snort.social,53.3498,-6.26031
nostr.linky.fit,50.1109,8.68213
nostr.plantroon.com,50.1013,8.62643
testr.nymble.world,40.8054,-74.0241
nostr.tac.lol,47.4748,-122.273
cdn.satellite.earth,40.8302,-74.1299
dm-test-strfry-discovery.samt.st,43.6532,-79.3832
wisp.djorivaltech.com,43.6532,-79.3832
nostr.trcjr.xyz,35.3821,-78.5486
offchain.pub,39.1585,-94.5728
nostras.app,38.7946,-106.535
relay.manneken.brussels,49.4543,11.0746
relay.laantungir.net,-19.4692,-42.5315
nostr-relay.xbytez.io:443,50.6924,3.20113
relay.gulugulu.moe,43.6532,-79.3832
relayrs.notoshi.win,43.6532,-79.3832
vault.iris.to,43.6532,-79.3832
relay.getsafebox.app:443,43.6532,-79.3832
top.testrelay.top,43.6532,-79.3832
relay.t6x.host,40.8302,-74.1299
relay.vrtmrz.net:443,43.6532,-79.3832
relay.ohstr.com,43.6532,-79.3832
nostr.wecsats.io,43.6532,-79.3832
ynostr.yael.at,60.1699,24.9384
infinity-signal-relay.digitalforlifeagency.workers.dev,43.6532,-79.3832
nostr.islandarea.net:443,35.4669,-97.6473
relay.favillakey.io,-27.4705,153.026
nostr.self-determined.de,51.4379,14.2355
nostr-pub.wellorder.net,45.5201,-122.99
relay.trotters.cc,43.6532,-79.3832
nostr-rs-relay.dev.fedibtc.com,39.0438,-77.4874
nostr2.girino.org,43.6532,-79.3832
nostr.rtvslawenia.com,49.4543,11.0746
nostr.stakey.net:443,52.6485,4.73787
relay.artx.market,43.6548,-79.3885
nos.lol,50.4754,12.3683
relay.nearhood.co.uk,51.5134,-0.0890675
nostr.pro.aw,43.6532,-79.3832
relay.piazza.today,48.122,11.589
relay.underorion.se,51.4998,-0.011072
nostr.oxtr.dev:443,50.4754,12.3683
relay.bitos.space,43.6532,-79.3832
nostrelay.circum.space:443,52.2245,8.826
relay.klabo.world,47.674,-122.122
nostr.spicyz.io,43.6532,-79.3832
relay.pyramid.li,47.4093,8.46503
strfry.shock.network,39.0438,-77.4874
relay.lightning.pub:443,39.0438,-77.4874
nostr.snowbla.de,50.4754,12.3683
relay.wavlake.com,41.2619,-95.8608
relay2.orangesync.tech,40.7128,-74.006
relay.mostro.network,40.8302,-74.1299
relay-dev.gulugulu.moe,43.6532,-79.3832
relay.orangesync.tech,40.7128,-74.006
chat-relay.zap-work.com:443,43.6532,-79.3832
relay.wasabiwallet.io,43.6532,-79.3832
relay.kilombino.com,43.6532,-79.3832
bridge.tagomago.me,42.3601,-71.0589
relay.loveisbitcoin.com,43.6532,-79.3832
relay.staging.plebeian.market,51.5072,-0.127586
relay.nostrcade.com,37.79,-122.401
relay.nostr.com,50.1109,8.68213
relay02.lnfi.network,35.6764,139.65
nostr.debate.report,50.1109,8.68213
relay.gulugulu.moe:443,43.6532,-79.3832
n.bogs.ccwu.cc,43.6532,-79.3832
relay.twentyone.ist,43.7696,-79.3309
nostr.mom:443,50.4754,12.3683
relay.czas.plus,50.1109,8.68213
relay.bullishbounty.com:443,43.6532,-79.3832
no.str.cr,8.96171,-83.5246
nostr.88mph.life,52.1941,-2.21905
relay.trotters.cc:443,43.6532,-79.3832
nostr.unkn0wn.world,47.4464,8.21073
relay.dyne.org,49.0291,8.35705
nostr.n7ekb.net,47.4941,-122.294
nostr.sathoarder.com:443,48.5734,7.75211
relay.guggero.org,46.5971,9.59652
relay.bowlafterbowl.com,32.9483,-96.7299
wheat.oslim.dev,43.6532,-79.3832
nostr.myshosholoza.co.za,52.3676,4.90414
nostr.tagomago.me,42.3601,-71.0589
relay.snotr.nl:49999,51.9758,4.31389
relay.lightning.pub,39.0438,-77.4874
0x-nostr-relay.fly.dev,38.7946,-106.535
relay.nostrconnect.com,41.5695,-83.9786
relay-can.zombi.cloudrodion.com,43.6532,-79.3832
relay.nostr.dev.br,48.1046,11.6002
relay.mrmave.work,43.6532,-79.3832
nostr.rtvslawenia.com:443,49.4543,11.0746
nostr.data.haus,50.4754,12.3683
nostr.chaima.info,50.1109,8.68213
nostr.relay.hedwig.sh,60.1699,24.9384
social.hns.one,42.8864,-78.8784
relay.pacsarcade.org,32.7767,-96.797
auth.nostr1.com,40.7057,-74.0136
relayone.soundhsa.com:443,39.0997,-94.5786
nostr.azzamo.net,52.2633,21.0283
nostr.eu.freedomrelay.dev,49.0524,14.4347
basspistol.org,49.0291,8.35696
relay.nostrops.com,32.71,-96.6745
dm-test-nostr-rs-42-disabled.samt.st,43.6532,-79.3832
schnorr.me,43.6532,-79.3832
no.str.cr:443,8.96171,-83.5246
nostr.snowbla.de:443,50.4754,12.3683
relay.openspecs.uid.ovh,50.9871,2.12554
nostr.nestoris.online,55.568,37.5853
relay.plebeian.market:443,50.1109,8.68213
relay.mwaters.net,50.9871,2.12554
relay.conduit.market,38.7946,-106.535
relay.wellorder.net,45.5201,-122.99
relay.nostriot.com,41.5695,-83.9786
nostr.tbxnetworx.de,47.8226,10.0064
relay.zone667.com,60.1699,24.9384
public.crostr.com,43.6532,-79.3832
relay.colombiap2p.com,43.6532,-79.3832
soloco.nl,43.6532,-79.3832
relay.orly.dev,32.7767,-96.797
relay01.lnfi.network,35.6764,139.65
relay-fra.zombi.cloudrodion.com,48.8566,2.35222
nostr.notribe.net,40.8302,-74.1299
relay.shychat.lol,39.8965,-80.1792
relay.cypherguide.org,1.32123,103.695
strfry.bonsai.com,39.0438,-77.4874
relay.decentnewsroom.com,50.4754,12.3683
relay.atsocy.com,43.6532,-79.3832
scores.brainstorm.world,39.0997,-94.5786
relay.nostr.bond,40.1885,29.061
nostr.fullstackcash.net,45.5201,-122.99
nostr.islandarea.net,35.4669,-97.6473
relay.illuminodes.com,43.6532,-79.3832
relay.samt.st,40.8302,-74.1299
relay.pprgb.app,52.52,13.405
relay.jcera.eu,43.6532,-79.3832
relay.nostreon.com,60.1699,24.9384
myvoiceourstory.org,44.8484,-123.234
relay.mypathtofire.de,42.8864,-78.8784
nostr.stakey.net,52.6485,4.73787
schnorr.me:443,43.6532,-79.3832
conduit-congee.fly.dev,38.7946,-106.535
offchain.bostr.online,43.6532,-79.3832
staging.yabu.me,35.6096,139.73
nostr.synalysis.com,50.4754,12.3683
relay.scuba323.com,40.8218,-74.45
nostr.hoppe-relay.it.com,42.8864,-78.8784
relay.paulstephenborile.com:443,49.4543,11.0746
memlay.v0l.io,53.3498,-6.26031
nostr-02.uid.ovh,50.9871,2.12554
articles.layer3.news,37.3387,-121.885
nostr.2b9t.xyz:443,34.0549,-118.243
relay.chatbett.de,40.7128,-74.006
relay.staging.plebeian.market:443,51.5072,-0.127586
relay.nostr.black,43.6532,-79.3832
relay.solomonstr.com,43.6532,-79.3832
relay.nostr.net,43.6532,-79.3832
dm-test-strfry-generic.samt.st,43.6532,-79.3832
nostr.girino.org,43.6532,-79.3832
bcast.girino.org,43.6532,-79.3832
n.kojira.io,43.6532,-79.3832
relay.sigit.io,50.4754,12.3683
bruh.samt.st,43.6532,-79.3832
relay.i9.eti.br,43.6532,-79.3832
relay.degmods.com,50.4754,12.3683
pymd.kamp.site,38.9072,-77.0369
relay.internationalright-wing.org,-22.4692,-48.9875
relay.earthly.city,34.1749,-118.54
relay-us.zombi.cloudrodion.com,40.7862,-74.0743
relay.yoinekodo.jp,43.6532,-79.3832
cache.trustr.ing,43.6548,-79.3885
relay2.angor.io,48.1046,11.6002
relay.lanacoin-eternity.com:443,40.8302,-74.1299
relay.nostar.org,43.6532,-79.3832
nostr-kyomu-haskell.onrender.com,37.7775,-122.397
relay.nostx.io,43.6532,-79.3832
relay.sincensura.org,43.6532,-79.3832
relay.qstr.app,50.1109,8.68213
relay2.veganostr.com,60.1699,24.9384
denimroad.feeds.relay.tools,38.6327,-90.1961
relay.nostr-check.me,43.6532,-79.3832
nostr.eu.freedomrelay.dev,49.0524,14.4347
relay.44billion.net,43.6532,-79.3832
nostr.debate.report,50.1109,8.68213
relayrs.notoshi.win:443,43.6532,-79.3832
top.testrelay.top,43.6532,-79.3832
relay.satsmarkt.club,52.3676,4.90414
nostr.plantroon.com:443,50.1013,8.62643
relay.lightning.pub,39.0438,-77.4874
nostr-rs-relay-ishosta.phamthanh.me,43.6532,-79.3832
auth.nostr1.com,40.7057,-74.0136
offchain.pub:443,39.1585,-94.5728
relay.fundstr.me,42.3601,-71.0589
openrelay.ziomc.com,41.8967,12.4822
nostrride.io,37.3986,-121.964
relay.vrtmrz.net,43.6532,-79.3832
relay-testnet.k8s.layer3.news,37.3387,-121.885
relay.keykeeper.world,40.7824,-74.0711
relay.minibolt.info,43.6532,-79.3832
relay.pacsarcade.org,32.7767,-96.797
relay.tapestry.ninja,40.8054,-74.0241
nostr.mom:443,50.4754,12.3683
strfry.bonsai.com,39.0438,-77.4874
relay.zone667.com,60.1699,24.9384
relay.directsponsor.net,42.8864,-78.8784
relay.veganostr.com,60.1699,24.9384
relay.nostrmap.net:443,60.1699,24.9384
relay.nostrconnect.com,41.5695,-83.9786
nostr.hifish.org,47.4009,8.57814
nostr.dzo-hadar.ts.net,35.6764,139.65
relay.openspecs.uid.ovh,50.9871,2.12554
no.str.cr,8.96171,-83.5246
nostr.mikoshi.de,51.2829,6.7827
nostr.chaima.info,50.1109,8.68213
relay.wavlake.com:443,41.2619,-95.8608
nostr-relay.xbytez.io,50.6924,3.20113
relay5.bitransfer.org,43.6532,-79.3832
relay.degmods.com,50.4754,12.3683
nostr-relay.xbytez.io:443,50.6924,3.20113
relay.lanacoin-eternity.com,40.8302,-74.1299
relay.illuminodes.com,43.6532,-79.3832
nostr.overmind.lol,43.6532,-79.3832
nostr.hetuniversum.nl,43.6532,-79.3832
relay.samt.st,40.8302,-74.1299
nos.lol,50.4754,12.3683
relay.valleirug.nl,52.0274,5.62427
relay.nostar.org,43.6532,-79.3832
wheat.oslim.dev,43.6532,-79.3832
nostr.thebiglake.org,32.71,-96.6745
relay.budabit.club,49.4543,11.0746
cdn.satellite.earth,40.8302,-74.1299
n.kojira.io,43.6532,-79.3832
dm-test-strfry-discovery.samt.st,43.6532,-79.3832
relay.nostrfy.org,35.6916,139.768
nostr.janx.com,43.6532,-79.3832
nostr.mad-social.net,50.4754,12.3683
nostr.girino.org,43.6532,-79.3832
relay.jmoose.rocks,32.7767,-96.797
nostr.2b9t.xyz:443,34.0549,-118.243
chat-relay.zap-work.com,43.6532,-79.3832
relayrs.notoshi.win,43.6532,-79.3832
relay1.privkey.io,25.7617,-80.1918
nostr.emanuelemiani.it,45.778,8.79414
relay.snort.social,53.3498,-6.26031
relay.ohstr.com,43.6532,-79.3832
relay.lightning.pub:443,39.0438,-77.4874
nostr.islandarea.net:443,35.4669,-97.6473
nostr.tagomago.me,42.3601,-71.0589
fanfares.nostr1.com:443,40.7057,-74.0136
nostr.snowbla.de:443,50.4754,12.3683
vault.iris.to:443,43.6532,-79.3832
test.relays.nuts.cash,52.3676,4.90414
relay-us.zombi.cloudrodion.com,40.7862,-74.0743
relay.nostrmap.net,60.1699,24.9384
relay.nostrcade.com,37.79,-122.401
relay.crovaxon.de,53.0777,7.41516
relay.flashapp.me,43.6548,-79.3885
nostr.oxtr.dev:443,50.4754,12.3683
relay.decentnewsroom.com,50.4754,12.3683
relay.liberbitworld.org,43.6532,-79.3832
relayone.soundhsa.com:443,39.0997,-94.5786
bucket.coracle.social,37.7775,-122.397
nrl.ceskar.xyz,50.5145,16.0119
relay.lanavault.space,60.1699,24.9384
public.crostr.com,43.6532,-79.3832
relay.thehakon.com,43.6532,-79.3832
relay.wisp.talk,49.4543,11.0746
nostr.trevena.info,43.6532,-79.3832
relay.qstr.app,50.1109,8.68213
relay.chorus.community:443,50.1109,8.68213
relay.plebeian.market:443,50.1109,8.68213
relay.nostrops.com,32.71,-96.6745
nostr.chaima.info:443,50.1109,8.68213
relay.nostr.dev.br,48.1046,11.6002
relay.camelus.app,45.5201,-122.99
nostr-pub.wellorder.net,45.5201,-122.99
cyberspace.nostr1.com,40.7057,-74.0136
relay2.angor.io,48.1046,11.6002
relay.edufeed.org:443,49.4521,11.0767
relay.bnos.space,43.6532,-79.3832
groups.beginningend.com,35.2227,-97.4786
relay.gulugulu.moe:443,43.6532,-79.3832
relayone.soundhsa.com,39.0997,-94.5786
relay.noeudlibre.fr,50.6924,3.20113
nostr.christiansass.de,50.1109,8.68213
chat-relay.zap-work.com:443,43.6532,-79.3832
conduit-congee.fly.dev,38.7946,-106.535
relay.satmaxt.xyz:443,43.6532,-79.3832
relay.rebelbase.social,50.965,5.50082
nostr.relay.hedwig.sh,60.1699,24.9384
dev.relay.stream,43.6532,-79.3832
relay.edufeed.org,49.4521,11.0767
relay.s-w.art,43.6532,-79.3832
nostr.wecsats.io,43.6532,-79.3832
relay.nostreon.com,60.1699,24.9384
relay.agentry.com,42.8864,-78.8784
denimroad.feeds.relay.tools,38.6327,-90.1961
relay.artx.market,43.6548,-79.3885
nostr.unkn0wn.world,47.4464,8.21073
relay.mappingbitcoin.com,43.6532,-79.3832
relay-na1.metanomalist.com,43.6532,-79.3832
nostr.2b9t.xyz,34.0549,-118.243
relay.artio.inf.unibe.ch,46.9501,7.43678
relay.mostro.network,40.8302,-74.1299
relay.nmail.li,50.9871,2.12554
bruh.samt.st,43.6532,-79.3832
relay.conduit.market,38.7946,-106.535
relay.mwaters.net,50.9871,2.12554
nostr.twinkle.lol,51.902,7.6657
relay.selfrule.co.za,43.6532,-79.3832
relay.btcforplebs.com,43.6532,-79.3832
relay.plebeian.market,50.1109,8.68213
nostr.n7ekb.net,47.4941,-122.294
nostr.easycryptosend.it,43.6532,-79.3832
relay.primal.net,43.6532,-79.3832
dev-relay.nostreon.com,60.1699,24.9384
nostr-kyomu-haskell.onrender.com,37.7775,-122.397
relay.kilombino.com,43.6532,-79.3832
nostr.bitcoiner.social:443,47.6743,-117.112
relay.underorion.se,51.4998,-0.011072
relay.paulstephenborile.com:443,49.4543,11.0746
relay.nostr.net,43.6532,-79.3832
syb.lol,43.6532,-79.3832
articles.layer3.news,37.3387,-121.885
ec1.f7z.io,60.1699,24.9384
relay.manneken.brussels,49.4543,11.0746
nostr.tac.lol,47.4748,-122.273
relay.agora.social,50.7383,15.0648
nostr.oxtr.dev,50.4754,12.3683
relay.hivetalk.org,40.8302,-74.1299
income-strongly-active-systematic.trycloudflare.com,43.6532,-79.3832
nostr.jcera.eu,43.6532,-79.3832
relay-dev.satlantis.io,39.0438,-77.4874
nostr.sovereignservices.xyz,43.6532,-79.3832
relay.lacrypta.ar,43.6532,-79.3832
nostr.data.haus,50.4754,12.3683
relay.monostr.com,50.4754,12.3683
relay.nostrhub.fr,48.1045,11.6004
relay.loveisbitcoin.com,43.6532,-79.3832
portal-relay.pareto.space,53.5488,9.98717
relay.minibolt.info,43.6532,-79.3832
relay.czas.plus,50.1109,8.68213
relay2.veganostr.com,60.1699,24.9384
relay.gorrdy.cz,50.1109,8.68213
relay.paulstephenborile.com,49.4543,11.0746
temp.iris.to,43.6532,-79.3832
relay.formstr.app,43.6532,-79.3832
talon.quest,43.6532,-79.3832
relay.ltgnet.work:8443,51.0511,-114.075
relay.guggero.org,46.5971,9.59652
relay.nostr.bond,40.1885,29.061
bitchat.nostr1.com,40.7057,-74.0136
prl.plus,54.9486,21.7283
relay.sharegap.net,43.6532,-79.3832
nostr.21crypto.ch,47.5356,8.73209
relay.jcera.eu,43.6532,-79.3832
r.senx.io,50.6924,3.20113
porchlight.social,43.6532,-79.3832
bread.nostrsms.com,40.8218,-74.45
relay.nostrian-conquest.com,41.223,-111.974
relay.getsafebox.app:443,43.6532,-79.3832
nostr.quali.chat,60.1699,24.9384
relay.laantungir.net,-19.4692,-42.5315
relay.hivescope.xyz,40.4332,-3.62431
nostr.fullstackcash.net,45.5201,-122.99
nip85-staging.nosfabrica.com,39.0997,-94.5786
relay-fra.zombi.cloudrodion.com,48.8566,2.35222
relay.nostr.black,43.6532,-79.3832
nostr.trepechov.com,49.4543,11.0746
relay.binaryrobot.com:443,43.6532,-79.3832
basspistol.org,49.0291,8.35696
free.relayted.de,50.1109,8.68213
relay.beginningend.com,35.2227,-97.4786
nostrelay.circum.space:443,52.2245,8.826
strfry.bonsai.com:443,39.0438,-77.4874
relay.wasabiwallet.io,43.6532,-79.3832
relay.scuba323.com,40.8218,-74.45
vidar.nestoris.online,52.3676,4.90414
nostr.pbfs.io:443,50.4754,12.3683
relay.pyramid.li,47.4093,8.46503
relay.olas.app,60.1699,24.9384
relay.internationalright-wing.org,-22.4692,-48.9875
nostr-relay.corb.net,39.6478,-104.988
relay.plebdevs.com,40.7128,-74.006
relay.ditto.pub,43.6532,-79.3832
relay.i9.eti.br,43.6532,-79.3832
nostr.pro.aw,43.6532,-79.3832
relay.hazeem.xyz,43.6532,-79.3832
zencrown.relay2aeon.quest,52.3061,4.6907
relay.npubhaus.com,43.6532,-79.3832
relay.nostriot.com,41.5695,-83.9786
cs-relay.nostrdev.com,50.4754,12.3683
relay.substation.ninja,43.6532,-79.3832
relay1.nostrchat.io,60.1699,24.9384
nostr-relay.cbrx.io,43.6532,-79.3832
nostr.stakey.net:443,52.6485,4.73787
relayone.geektank.ai:443,39.0997,-94.5786
adre.su,59.9311,30.3609
nostr.islandarea.net,35.4669,-97.6473
relay.cypherguide.org,1.32123,103.695
fanfares.nostr1.com,40.7057,-74.0136
nostrride.io,43.6532,-79.3832
relay.littlebitstudios.com,34.8688,-86.5502
relay.snotr.nl:49999,51.9758,4.31389
relay02.lnfi.network,35.6764,139.65
nostr.rtvslawenia.com,49.4543,11.0746
relay.staging.plebeian.market:443,51.5072,-0.127586
relay.earthly.city,34.1749,-118.54
relay.binaryrobot.com,43.6532,-79.3832
relay.artiostr.ch,43.6532,-79.3832
nostr.nestoris.online,55.568,37.5853
nostr.liberty.fans,36.8767,-89.5879
n.bogs.ccwu.cc,43.6532,-79.3832
relay.t6x.host,40.8302,-74.1299
relay.nostrverse.net,43.6532,-79.3832
relay.bullishbounty.com:443,43.6532,-79.3832
nostr.mom,50.4754,12.3683
scores.brainstorm.world,39.0997,-94.5786
nostr.tabordalab.com,60.1699,24.9384
relay.favillakey.io,-27.4705,153.026
relay.nostr.blockhenge.com,39.0438,-77.4874
relay.bullishbounty.com,43.6532,-79.3832
vm-1734.lnvps.cloud,53.3498,-6.26031
nostr.bitcoiner.social,47.6743,-117.112
budabit.nostr1.com,40.7057,-74.0136
soloco.nl,43.6532,-79.3832
relay.iyou.me,64.1466,-21.9426
dm-test-nostr-rs-42-disabled.samt.st,43.6532,-79.3832
relay.cloistr.xyz,42.337,-77.3178
relay.nearhood.co.uk,51.5134,-0.0890675
offchain.bostr.online,43.6532,-79.3832
relay.keykeeper.world,40.7824,-74.0711
nostras.app,38.7946,-106.535
nostr.wild-vibes.ts.net,48.8566,2.35222
nostr-relayflare.enpitsulin.workers.dev,43.6532,-79.3832
relay.lanacoin-eternity.com:443,40.8302,-74.1299
greenart7c3.feeds.relay.tools,38.6327,-90.1961
relay.mitosystem.net,-23.5558,-46.6396
nostr.snowbla.de,50.4754,12.3683
cs-relay.nostrdev.com:443,50.4754,12.3683
nostrelay.circum.space,52.2245,8.826
0x-nostr-relay.fly.dev,38.7946,-106.535
nip85.nosfabrica.com,39.0997,-94.5786
chorus.mikedilger.com:444,-36.8906,174.794
nostr.rtvslawenia.com:443,49.4543,11.0746
yabu.me,35.6096,139.73
nostr.planix.org,43.6532,-79.3832
relay2.orangesync.tech,40.7128,-74.006
relay.dyne.org,49.0291,8.35705
relay.aarpia.com,37.3986,-121.964
relay.fiatdenier.com,43.7787,-79.5393
relay.wisp.talk:443,49.4543,11.0746
nostr.azzamo.net,52.2633,21.0283
relay.satmaxt.xyz,43.6532,-79.3832
relay.jmoose.rocks:443,32.7767,-96.797
nostr-01.uid.ovh,50.9871,2.12554
nostr-relay.corb.net:443,39.6478,-104.988
buzz.cashu.space,43.6532,-79.3832
dev-relay.nostr.space,43.6532,-79.3832
relay.cyberguy.fyi,52.3676,4.90414
freelay.sovbit.host,60.1699,24.9384
hasky.chat,40.7862,-74.0743
treuzkas.branruz.com,48.8575,2.35138
nostr-verified.wellorder.net,45.5201,-122.99
nexus.libernet.app,43.6532,-79.3832
relay.gulugulu.moe,43.6532,-79.3832
bitcoiner.social,47.6743,-117.112
nostr.stakey.net,52.6485,4.73787
schnorr.me,43.6532,-79.3832
nos.xmark.cc,50.6916,3.20151
vault.iris.to,43.6532,-79.3832
nostr.nodesmap.com,59.3327,18.0656
nostr.88mph.life,52.1941,-2.21905
nostr.yutakobayashi.com,43.6532,-79.3832
relay.layer.systems,43.6532,-79.3832
relay-can.zombi.cloudrodion.com,43.6532,-79.3832
bcast.girino.org,43.6532,-79.3832
relay.chorus.community,50.1109,8.68213
strfry.shock.network,39.0438,-77.4874
ynostr.yael.at,60.1699,24.9384
relay.staging.plebeian.market,51.5072,-0.127586
cache.trustr.ing,43.6548,-79.3885
nos.lol:443,50.4754,12.3683
relay.shadowbip.com,54.352,18.6466
nostr.computingcache.com,45.5341,-122.956
memlay.v0l.io,53.3498,-6.26031
relay.colombiap2p.com,43.6532,-79.3832
dm-test-strfry-generic.samt.st,43.6532,-79.3832
borgar.lol,52.3676,4.90414
relay.wellorder.net,45.5201,-122.99
node.kommonzenze.de,49.4521,11.0767
relay.ru.ac.th,13.7607,100.627
nostr.davenov.com,50.1109,8.68213
nostr.linky.fit,50.1109,8.68213
nostr-relay.zimage.com,34.282,-118.439
relay.nostx.io,43.6532,-79.3832
relay.dwadziesciajeden.pl,52.2297,21.0122
relay.pornstr.net,53.3498,-6.26031
nostr-rs-relay.dev.fedibtc.com,39.0438,-77.4874
schnorr.me:443,43.6532,-79.3832
nostr.plantroon.com,50.1013,8.62643
relay.routstr.com,59.4016,17.9455
relay.shychat.lol,39.8965,-80.1792
nr.yay.so,46.2126,6.1154
pymd.kamp.site,38.9072,-77.0369
relay.decentralia.fr,48.122,11.589
relay.klabo.world,47.674,-122.122
spurline.safebox.dev,43.6532,-79.3832
bridge.tagomago.me,42.3601,-71.0589
inbox.scuba323.com,40.8218,-74.45
no.str.cr:443,8.96171,-83.5246
nostr2.girino.org:443,43.6532,-79.3832
nostr-dev.wellorder.net,45.5201,-122.99
relay.mypathtofire.de,42.8864,-78.8784
nostr-02.uid.ovh,50.9871,2.12554
support.flotilla.social,32.9483,-96.7299
relay.mccormick.cx,52.3563,4.95714
relay.getsafebox.app,43.6532,-79.3832
relay.bitos.space,43.6532,-79.3832
nostrcity-club.fly.dev,38.7946,-106.535
spamspamspamspam.rest,43.6532,-79.3832
relay.solomonstr.com,43.6532,-79.3832
relay.angor.io,48.1046,11.6002
relay.islandbitcoin.com,20.3898,78.0903
relay.bornheimer.app,50.1109,8.68213
relay.atsocy.com,43.6532,-79.3832
relay.wavlake.com,41.2619,-95.8608
relay.nostrcheck.me,43.6532,-79.3832
relay.bowlafterbowl.com,32.9483,-96.7299
relay.novospes.com,43.6532,-79.3832
nostr.spaceshell.xyz,43.6532,-79.3832
purplerelay.com,43.6532,-79.3832
relay.satsapp.me,50.4754,12.3683
nostr.pbfs.io,50.4754,12.3683
relay2.nostrasia.net,43.6532,-79.3832
relay.piazza.today,48.122,11.589
relay.nostr.space,43.6532,-79.3832
relay.duck1123.com,43.6532,-79.3832
relay.kizuna-miki.com,35.694,139.754
relay.dreamith.to,43.6532,-79.3832
relay.chatbett.de,40.7128,-74.006
public.crostr.com:443,43.6532,-79.3832
relay.satlantis.io,39.0438,-77.4874
nostr2.girino.org,43.6532,-79.3832
relay.lotek-distro.com,43.6532,-79.3832
nostr-rs-relay.dev.fedibtc.com:443,39.0438,-77.4874
nostrrelay.taylorperron.com,45.5029,-73.5723
nosflare-leefcore.leefcore.workers.dev,43.6532,-79.3832
nostr.spicyz.io,43.6532,-79.3832
x.kojira.io,43.6532,-79.3832
relay.veganostr.com:443,60.1699,24.9384
nostr.alex71btc.com,43.6532,-79.3832
relay.thewired.app,39.0438,-77.4874
relay.getvia.xyz,60.1699,24.9384
relayone.geektank.ai,39.0997,-94.5786
nostr.hoppe-relay.it.com,42.8864,-78.8784
relay2.0xprivacy.online,43.6532,-79.3832
nostr-01.yakihonne.com,1.32123,103.695
offchain.pub,39.1585,-94.5728
nostrcity-club.fly.dev:443,38.7946,-106.535
testr.nymble.world,40.8054,-74.0241
relay.fountain.fm,43.6532,-79.3832
relay.nostr.com,50.1109,8.68213

1 Relay URL Latitude Longitude
inbox.scuba323.com 40.8218 -74.45
relay.pornstr.net 53.3498 -6.26031
relay.btcforplebs.com 43.6532 -79.3832
nostr-01.yakihonne.com 1.32123 103.695
relay.agentry.com 42.8864 -78.8784
offchain.pub:443 39.1585 -94.5728
dev.relay.stream 43.6532 -79.3832
cyberspace.nostr1.com 40.7057 -74.0136
relay.shadowbip.com 54.352 18.6466
nip85-staging.nosfabrica.com 39.0997 -94.5786
portal-relay.pareto.space 53.5488 9.98717
relay.ru.ac.th 13.7607 100.627
nostr-relay.corb.net 39.6478 -104.988
nostrelay.circum.space 52.2245 8.826
relay.satsapp.me 50.4754 12.3683
nostr-01.uid.ovh 50.9871 2.12554
nostr.dzo-hadar.ts.net 35.6764 139.65
vault.iris.to:443 43.6532 -79.3832
relay.nmail.li 50.9871 2.12554
nostr.janx.com 43.6532 -79.3832
relay.satmaxt.xyz:443 43.6532 -79.3832
nos.lol:443 50.4754 12.3683
nostr.mad-social.net 50.4754 12.3683
relay.flashapp.me 43.6548 -79.3885
relay.layer.systems 43.6532 -79.3832
relay.novospes.com 43.6532 -79.3832
relay.satmaxt.xyz 43.6532 -79.3832
relay.getvia.xyz 60.1699 24.9384
vidar.nestoris.online 52.3676 4.90414
nostr.yutakobayashi.com 43.6532 -79.3832
dev-relay.nostreon.com 60.1699 24.9384
nostrcity-club.fly.dev:443 38.7946 -106.535
nostr.oxtr.dev 50.4754 12.3683
relay.lacrypta.ar 43.6532 -79.3832
node.kommonzenze.de 49.4521 11.0767
bitchat.nostr1.com 40.7057 -74.0136
nostr.trepechov.com 49.4543 11.0746
articles.layer3.news:443 37.3387 -121.885
relay.ditto.pub 43.6532 -79.3832
treuzkas.branruz.com 48.8575 2.35138
nostr.plantroon.com:443 50.1013 8.62643
nostr.mas-family.eu 60.3478 15.7505
relay.nostrfy.org 35.6916 139.768
relay.nostrmap.net 60.1699 24.9384
woods-leader-permits-yamaha.trycloudflare.com 43.6532 -79.3832
nostr.middling.mydns.jp 35.8099 140.12
adre.su 59.9311 30.3609
relay.s-w.art 43.6532 -79.3832
nostr.thebiglake.org 32.71 -96.6745
relay.satlantis.io 39.0438 -77.4874
relay.routstr.com 59.4016 17.9455
relay.nostr.space 43.6532 -79.3832
relay.wavlake.com:443 41.2619 -95.8608
relay.islandbitcoin.com 20.3898 78.0903
nostr.tabordalab.com 60.1699 24.9384
nostr-relay.cbrx.io 43.6532 -79.3832
testnet.samt.st 43.6532 -79.3832
temp.iris.to 43.6532 -79.3832
freelay.sovbit.host 60.1699 24.9384
relay.grammstr.com 38.7946 -106.535
relay.nostronchain.com 43.6532 -79.3832
relay.hazeem.xyz 43.6532 -79.3832
relayone.geektank.ai 39.0997 -94.5786
nostr-relay.corb.net:443 39.6478 -104.988
relay.olas.app 60.1699 24.9384
relay.44billion.net 43.6532 -79.3832
relay.mitosystem.net -23.5558 -46.6396
relay.ltgnet.work:8443 51.0511 -114.075
nostr.chaima.info:443 50.1109 8.68213
nostrcity-club.fly.dev 38.7946 -106.535
nostr.sathoarder.com 48.5734 7.75211
relay.fountain.fm:443 43.6532 -79.3832
nostr.txlyre.website 60.1699 24.9384
nostr.planix.org 43.6532 -79.3832
relay.chorus.community 50.1109 8.68213
dev-relay.nostr.space 43.6532 -79.3832
relay.satsmarkt.club 52.3676 4.90414
relay.bullishbounty.com 43.6532 -79.3832
relay.lanacoin-eternity.com 40.8302 -74.1299
social.amanah.eblessing.co 48.1046 11.6002
relay-arg.zombi.cloudrodion.com 1.35208 103.82
relay.primal.net 43.6532 -79.3832
nostr-relay.zimage.com 34.282 -118.439
relay.veganostr.com:443 60.1699 24.9384
relay.hivetalk.org 40.8302 -74.1299
nostr.whitenode45.ddns.net 40.55 -74.4758
nostr-rs-relay-ishosta.phamthanh.me 43.6532 -79.3832
relay.edufeed.org 49.4521 11.0767
nostr.pbfs.io:443 50.4754 12.3683
relay.substation.ninja 43.6532 -79.3832
nostr.liberty.fans 36.8767 -89.5879
nostr-verified.wellorder.net 45.5201 -122.99
relay.hivescope.xyz 40.4332 -3.62431
nostr2.girino.org:443 43.6532 -79.3832
relay.cloistr.xyz 42.337 -77.3178
relay.agora.social 50.7383 15.0648
relayone.soundhsa.com 39.0997 -94.5786
nostr.0x7e.xyz 47.4949 8.71954
nostr-rs-relay.dev.fedibtc.com:443 39.0438 -77.4874
relay-na1.metanomalist.com 43.6532 -79.3832
relay.bitmacro.cloud 43.6532 -79.3832
relay.iyou.me 64.1466 -21.9426
nostr.bitcoiner.social 47.6743 -117.112
relay.wisp.talk:443 49.4543 11.0746
relay.valleirug.nl 52.0274 5.62427
chat-relay.zap-work.com 43.6532 -79.3832
nostr.2b9t.xyz 34.0549 -118.243
groups.beginningend.com 35.2227 -97.4786
relay.edufeed.org:443 49.4521 11.0767
nrs-02.darkcloudarcade.com 39.9526 -75.1652
relay.angor.io 48.1046 11.6002
relay.jmoose.rocks 32.7767 -96.797
nostr.mom 50.4754 12.3683
x.kojira.io 43.6532 -79.3832
cs-relay.nostrdev.com:443 50.4754 12.3683
nostr.computingcache.com 45.5341 -122.956
relay-dev.satlantis.io 39.0438 -77.4874
nostr.jcera.eu 43.6532 -79.3832
relay.aarpia.com 37.3986 -121.964
relay.nostrian-conquest.com 41.223 -111.974
relay.fiatdenier.com 43.7787 -79.5393
relayrs.notoshi.win:443 43.6532 -79.3832
bread.nostrsms.com 40.8218 -74.45
test.relays.nuts.cash 52.3676 4.90414
nostrrelay.taylorperron.com 45.5029 -73.5723
relay.jmoose.rocks:443 32.7767 -96.797
relay.tapestry.ninja 40.8054 -74.0241
nostr.hetuniversum.nl 43.6532 -79.3832
public.crostr.com:443 43.6532 -79.3832
nostr.trevena.info 43.6532 -79.3832
relay.chorus.community:443 50.1109 8.68213
relay.sharegap.net 43.6532 -79.3832
relayone.geektank.ai:443 39.0997 -94.5786
nip85.nosfabrica.com 39.0997 -94.5786
yabu.me 35.6096 139.73
nostr.bitcoiner.social:443 47.6743 -117.112
support.flotilla.social 32.9483 -96.7299
strfry.bonsai.com:443 39.0438 -77.4874
nostr-relayflare.enpitsulin.workers.dev 43.6532 -79.3832
relay.crovaxon.de 53.0777 7.41516
relay.froth.zone 60.1699 24.9384
r.senx.io 50.6924 3.20113
relay2.nostrasia.net 43.6532 -79.3832
relay.decentralia.fr 48.122 11.589
relay.mappingbitcoin.com 43.6532 -79.3832
nosflare-leefcore.leefcore.workers.dev 43.6532 -79.3832
chorus.mikedilger.com:444 -36.8906 174.794
nostr.wild-vibes.ts.net 48.8566 2.35222
spamspamspamspam.rest 43.6532 -79.3832
relay.wisp.talk 49.4543 11.0746
relay.bornheimer.app 50.1109 8.68213
nostr-dev.wellorder.net 45.5201 -122.99
strfry.shock.network:443 39.0438 -77.4874
nostr.mikoshi.de 52.52 13.405
nostr.christiansass.de 50.1109 8.68213
relay.14898.hns.one 42.8864 -78.8784
porchlight.social 43.6532 -79.3832
nostr.twinkle.lol 51.902 7.6657
nostr.pbfs.io 50.4754 12.3683
zencrown.relay2aeon.quest 52.3061 4.6907
21milionidinostr.duckdns.org 45.519 9.08615
test-relay.uid.ovh 48.5734 7.75211
nexus.libernet.app 43.6532 -79.3832
bitcoiner.social 47.6743 -117.112
syb.lol 43.6532 -79.3832
relay.directsponsor.net 42.8864 -78.8784
budabit.nostr1.com 40.7057 -74.0136
relay.budabit.club 49.4543 11.0746
relay.monostr.com 50.4754 12.3683
relay.rebelbase.social 50.965 5.50082
nostr.hifish.org 47.4009 8.57814
nostr.21crypto.ch 47.5356 8.73209
nostr.emanuelemiani.it 45.778 8.79414
relay.kizuna-miki.com 35.694 139.754
relay.binaryrobot.com:443 43.6532 -79.3832
spurline.safebox.dev 43.6532 -79.3832
buzz.cashu.space 43.6532 -79.3832
relay.plebeian.market 50.1109 8.68213
relay.mleku.dev 32.7767 -96.797
nostr.purpura.cloud 43.6532 -79.3832
relay.binaryrobot.com 43.6532 -79.3832
relay.paulstephenborile.com 49.4543 11.0746
relay2.fiatdenier.com 50.1013 8.62643
cs-relay.nostrdev.com 50.4754 12.3683
nostr.davenov.com 50.1109 8.68213
relay.nostrhub.fr 48.1045 11.6004
nostr.spaceshell.xyz 43.6532 -79.3832
relay.gorrdy.cz 50.1109 8.68213
relay.nostrmap.net:443 60.1699 24.9384
relay.getsafebox.app 43.6532 -79.3832
relay.lanavault.space 60.1699 24.9384
relay.veganostr.com 60.1699 24.9384
prl.plus 54.9486 21.7283
relay.nostr.blockhenge.com 39.0438 -77.4874
nostr.quali.chat 60.1699 24.9384
nostr.nodesmap.com 59.3327 18.0656
relay.beginningend.com 35.2227 -97.4786
relay2.0xprivacy.online 43.6532 -79.3832
relay1.privkey.io 25.7617 -80.1918
relay-dev.gulugulu.moe:443 43.6532 -79.3832
relay.lotek-distro.com 43.6532 -79.3832
relay.noeudlibre.fr 50.6924 3.20113
relay.cyberguy.fyi 52.3676 4.90414
nostr-relay.xbytez.io 50.6924 3.20113
relay.kaleidoswap.com 47.3769 8.54169
relay.bnos.space 43.6532 -79.3832
purplerelay.com 43.6532 -79.3832
hasky.chat 40.7862 -74.0743
relay.snort.social 53.3498 -6.26031
nostr.linky.fit 50.1109 8.68213
nostr.plantroon.com 50.1013 8.62643
testr.nymble.world 40.8054 -74.0241
nostr.tac.lol 47.4748 -122.273
cdn.satellite.earth 40.8302 -74.1299
dm-test-strfry-discovery.samt.st 43.6532 -79.3832
wisp.djorivaltech.com 43.6532 -79.3832
nostr.trcjr.xyz 35.3821 -78.5486
offchain.pub 39.1585 -94.5728
nostras.app 38.7946 -106.535
relay.manneken.brussels 49.4543 11.0746
relay.laantungir.net -19.4692 -42.5315
nostr-relay.xbytez.io:443 50.6924 3.20113
relay.gulugulu.moe 43.6532 -79.3832
relayrs.notoshi.win 43.6532 -79.3832
vault.iris.to 43.6532 -79.3832
relay.getsafebox.app:443 43.6532 -79.3832
top.testrelay.top 43.6532 -79.3832
relay.t6x.host 40.8302 -74.1299
relay.vrtmrz.net:443 43.6532 -79.3832
relay.ohstr.com 43.6532 -79.3832
nostr.wecsats.io 43.6532 -79.3832
ynostr.yael.at 60.1699 24.9384
infinity-signal-relay.digitalforlifeagency.workers.dev 43.6532 -79.3832
nostr.islandarea.net:443 35.4669 -97.6473
relay.favillakey.io -27.4705 153.026
nostr.self-determined.de 51.4379 14.2355
nostr-pub.wellorder.net 45.5201 -122.99
relay.trotters.cc 43.6532 -79.3832
nostr-rs-relay.dev.fedibtc.com 39.0438 -77.4874
nostr2.girino.org 43.6532 -79.3832
nostr.rtvslawenia.com 49.4543 11.0746
nostr.stakey.net:443 52.6485 4.73787
relay.artx.market 43.6548 -79.3885
nos.lol 50.4754 12.3683
relay.nearhood.co.uk 51.5134 -0.0890675
nostr.pro.aw 43.6532 -79.3832
relay.piazza.today 48.122 11.589
relay.underorion.se 51.4998 -0.011072
nostr.oxtr.dev:443 50.4754 12.3683
relay.bitos.space 43.6532 -79.3832
nostrelay.circum.space:443 52.2245 8.826
relay.klabo.world 47.674 -122.122
nostr.spicyz.io 43.6532 -79.3832
relay.pyramid.li 47.4093 8.46503
strfry.shock.network 39.0438 -77.4874
relay.lightning.pub:443 39.0438 -77.4874
nostr.snowbla.de 50.4754 12.3683
relay.wavlake.com 41.2619 -95.8608
relay2.orangesync.tech 40.7128 -74.006
relay.mostro.network 40.8302 -74.1299
relay-dev.gulugulu.moe 43.6532 -79.3832
relay.orangesync.tech 40.7128 -74.006
chat-relay.zap-work.com:443 43.6532 -79.3832
relay.wasabiwallet.io 43.6532 -79.3832
relay.kilombino.com 43.6532 -79.3832
bridge.tagomago.me 42.3601 -71.0589
relay.loveisbitcoin.com 43.6532 -79.3832
relay.staging.plebeian.market 51.5072 -0.127586
relay.nostrcade.com 37.79 -122.401
relay.nostr.com 50.1109 8.68213
relay02.lnfi.network 35.6764 139.65
nostr.debate.report 50.1109 8.68213
relay.gulugulu.moe:443 43.6532 -79.3832
n.bogs.ccwu.cc 43.6532 -79.3832
relay.twentyone.ist 43.7696 -79.3309
nostr.mom:443 50.4754 12.3683
relay.czas.plus 50.1109 8.68213
relay.bullishbounty.com:443 43.6532 -79.3832
no.str.cr 8.96171 -83.5246
nostr.88mph.life 52.1941 -2.21905
relay.trotters.cc:443 43.6532 -79.3832
nostr.unkn0wn.world 47.4464 8.21073
relay.dyne.org 49.0291 8.35705
nostr.n7ekb.net 47.4941 -122.294
nostr.sathoarder.com:443 48.5734 7.75211
relay.guggero.org 46.5971 9.59652
relay.bowlafterbowl.com 32.9483 -96.7299
wheat.oslim.dev 43.6532 -79.3832
nostr.myshosholoza.co.za 52.3676 4.90414
nostr.tagomago.me 42.3601 -71.0589
relay.snotr.nl:49999 51.9758 4.31389
relay.lightning.pub 39.0438 -77.4874
0x-nostr-relay.fly.dev 38.7946 -106.535
relay.nostrconnect.com 41.5695 -83.9786
relay-can.zombi.cloudrodion.com 43.6532 -79.3832
relay.nostr.dev.br 48.1046 11.6002
relay.mrmave.work 43.6532 -79.3832
nostr.rtvslawenia.com:443 49.4543 11.0746
nostr.data.haus 50.4754 12.3683
nostr.chaima.info 50.1109 8.68213
nostr.relay.hedwig.sh 60.1699 24.9384
social.hns.one 42.8864 -78.8784
relay.pacsarcade.org 32.7767 -96.797
auth.nostr1.com 40.7057 -74.0136
relayone.soundhsa.com:443 39.0997 -94.5786
nostr.azzamo.net 52.2633 21.0283
nostr.eu.freedomrelay.dev 49.0524 14.4347
basspistol.org 49.0291 8.35696
relay.nostrops.com 32.71 -96.6745
dm-test-nostr-rs-42-disabled.samt.st 43.6532 -79.3832
schnorr.me 43.6532 -79.3832
no.str.cr:443 8.96171 -83.5246
nostr.snowbla.de:443 50.4754 12.3683
relay.openspecs.uid.ovh 50.9871 2.12554
nostr.nestoris.online 55.568 37.5853
relay.plebeian.market:443 50.1109 8.68213
relay.mwaters.net 50.9871 2.12554
relay.conduit.market 38.7946 -106.535
relay.wellorder.net 45.5201 -122.99
relay.nostriot.com 41.5695 -83.9786
nostr.tbxnetworx.de 47.8226 10.0064
relay.zone667.com 60.1699 24.9384
public.crostr.com 43.6532 -79.3832
relay.colombiap2p.com 43.6532 -79.3832
soloco.nl 43.6532 -79.3832
relay.orly.dev 32.7767 -96.797
relay01.lnfi.network 35.6764 139.65
relay-fra.zombi.cloudrodion.com 48.8566 2.35222
nostr.notribe.net 40.8302 -74.1299
relay.shychat.lol 39.8965 -80.1792
relay.cypherguide.org 1.32123 103.695
strfry.bonsai.com 39.0438 -77.4874
relay.decentnewsroom.com 50.4754 12.3683
relay.atsocy.com 43.6532 -79.3832
scores.brainstorm.world 39.0997 -94.5786
relay.nostr.bond 40.1885 29.061
nostr.fullstackcash.net 45.5201 -122.99
nostr.islandarea.net 35.4669 -97.6473
relay.illuminodes.com 43.6532 -79.3832
relay.samt.st 40.8302 -74.1299
relay.pprgb.app 52.52 13.405
relay.jcera.eu 43.6532 -79.3832
relay.nostreon.com 60.1699 24.9384
myvoiceourstory.org 44.8484 -123.234
relay.mypathtofire.de 42.8864 -78.8784
nostr.stakey.net 52.6485 4.73787
schnorr.me:443 43.6532 -79.3832
conduit-congee.fly.dev 38.7946 -106.535
offchain.bostr.online 43.6532 -79.3832
staging.yabu.me 35.6096 139.73
2 nostr.synalysis.com 50.4754 12.3683
3 relay.scuba323.com relay.nostr-check.me 40.8218 43.6532 -74.45 -79.3832
4 nostr.hoppe-relay.it.com nostr.eu.freedomrelay.dev 42.8864 49.0524 -78.8784 14.4347
5 relay.paulstephenborile.com:443 relay.44billion.net 49.4543 43.6532 11.0746 -79.3832
6 memlay.v0l.io nostr.debate.report 53.3498 50.1109 -6.26031 8.68213
7 nostr-02.uid.ovh relayrs.notoshi.win:443 50.9871 43.6532 2.12554 -79.3832
8 articles.layer3.news top.testrelay.top 37.3387 43.6532 -121.885 -79.3832
9 nostr.2b9t.xyz:443 relay.satsmarkt.club 34.0549 52.3676 -118.243 4.90414
10 relay.chatbett.de nostr.plantroon.com:443 40.7128 50.1013 -74.006 8.62643
11 relay.staging.plebeian.market:443 relay.lightning.pub 51.5072 39.0438 -0.127586 -77.4874
12 relay.nostr.black nostr-rs-relay-ishosta.phamthanh.me 43.6532 -79.3832
13 relay.solomonstr.com auth.nostr1.com 43.6532 40.7057 -79.3832 -74.0136
14 relay.nostr.net offchain.pub:443 43.6532 39.1585 -79.3832 -94.5728
dm-test-strfry-generic.samt.st 43.6532 -79.3832
nostr.girino.org 43.6532 -79.3832
bcast.girino.org 43.6532 -79.3832
n.kojira.io 43.6532 -79.3832
relay.sigit.io 50.4754 12.3683
bruh.samt.st 43.6532 -79.3832
relay.i9.eti.br 43.6532 -79.3832
relay.degmods.com 50.4754 12.3683
pymd.kamp.site 38.9072 -77.0369
relay.internationalright-wing.org -22.4692 -48.9875
relay.earthly.city 34.1749 -118.54
relay-us.zombi.cloudrodion.com 40.7862 -74.0743
relay.yoinekodo.jp 43.6532 -79.3832
cache.trustr.ing 43.6548 -79.3885
relay2.angor.io 48.1046 11.6002
relay.lanacoin-eternity.com:443 40.8302 -74.1299
relay.nostar.org 43.6532 -79.3832
nostr-kyomu-haskell.onrender.com 37.7775 -122.397
relay.nostx.io 43.6532 -79.3832
relay.sincensura.org 43.6532 -79.3832
relay.qstr.app 50.1109 8.68213
relay2.veganostr.com 60.1699 24.9384
denimroad.feeds.relay.tools 38.6327 -90.1961
15 relay.fundstr.me 42.3601 -71.0589
16 openrelay.ziomc.com relay.pacsarcade.org 41.8967 32.7767 12.4822 -96.797
17 nostrride.io relay.tapestry.ninja 37.3986 40.8054 -121.964 -74.0241
18 relay.vrtmrz.net nostr.mom:443 43.6532 50.4754 -79.3832 12.3683
19 relay-testnet.k8s.layer3.news strfry.bonsai.com 37.3387 39.0438 -121.885 -77.4874
20 relay.keykeeper.world relay.zone667.com 40.7824 60.1699 -74.0711 24.9384
21 relay.minibolt.info relay.directsponsor.net 43.6532 42.8864 -79.3832 -78.8784
22 relay.veganostr.com 60.1699 24.9384
23 relay.nostrmap.net:443 60.1699 24.9384
24 relay.nostrconnect.com 41.5695 -83.9786
25 nostr.hifish.org 47.4009 8.57814
26 nostr.dzo-hadar.ts.net 35.6764 139.65
27 relay.openspecs.uid.ovh 50.9871 2.12554
28 no.str.cr 8.96171 -83.5246
29 nostr.mikoshi.de 51.2829 6.7827
30 nostr.chaima.info 50.1109 8.68213
31 relay.wavlake.com:443 41.2619 -95.8608
32 nostr-relay.xbytez.io 50.6924 3.20113
33 relay5.bitransfer.org 43.6532 -79.3832
34 relay.degmods.com 50.4754 12.3683
35 nostr-relay.xbytez.io:443 50.6924 3.20113
36 relay.lanacoin-eternity.com 40.8302 -74.1299
37 relay.illuminodes.com 43.6532 -79.3832
38 nostr.overmind.lol 43.6532 -79.3832
39 nostr.hetuniversum.nl 43.6532 -79.3832
40 relay.samt.st 40.8302 -74.1299
41 nos.lol 50.4754 12.3683
42 relay.valleirug.nl 52.0274 5.62427
43 relay.nostar.org 43.6532 -79.3832
44 wheat.oslim.dev 43.6532 -79.3832
45 nostr.thebiglake.org 32.71 -96.6745
46 relay.budabit.club 49.4543 11.0746
47 cdn.satellite.earth 40.8302 -74.1299
48 n.kojira.io 43.6532 -79.3832
49 dm-test-strfry-discovery.samt.st 43.6532 -79.3832
50 relay.nostrfy.org 35.6916 139.768
51 nostr.janx.com 43.6532 -79.3832
52 nostr.mad-social.net 50.4754 12.3683
53 nostr.girino.org 43.6532 -79.3832
54 relay.jmoose.rocks 32.7767 -96.797
55 nostr.2b9t.xyz:443 34.0549 -118.243
56 chat-relay.zap-work.com 43.6532 -79.3832
57 relayrs.notoshi.win 43.6532 -79.3832
58 relay1.privkey.io 25.7617 -80.1918
59 nostr.emanuelemiani.it 45.778 8.79414
60 relay.snort.social 53.3498 -6.26031
61 relay.ohstr.com 43.6532 -79.3832
62 relay.lightning.pub:443 39.0438 -77.4874
63 nostr.islandarea.net:443 35.4669 -97.6473
64 nostr.tagomago.me 42.3601 -71.0589
65 fanfares.nostr1.com:443 40.7057 -74.0136
66 nostr.snowbla.de:443 50.4754 12.3683
67 vault.iris.to:443 43.6532 -79.3832
68 test.relays.nuts.cash 52.3676 4.90414
69 relay-us.zombi.cloudrodion.com 40.7862 -74.0743
70 relay.nostrmap.net 60.1699 24.9384
71 relay.nostrcade.com 37.79 -122.401
72 relay.crovaxon.de 53.0777 7.41516
73 relay.flashapp.me 43.6548 -79.3885
74 nostr.oxtr.dev:443 50.4754 12.3683
75 relay.decentnewsroom.com 50.4754 12.3683
76 relay.liberbitworld.org 43.6532 -79.3832
77 relayone.soundhsa.com:443 39.0997 -94.5786
78 bucket.coracle.social 37.7775 -122.397
79 nrl.ceskar.xyz relay.lanavault.space 50.5145 60.1699 16.0119 24.9384
80 public.crostr.com 43.6532 -79.3832
81 relay.thehakon.com 43.6532 -79.3832
82 relay.wisp.talk 49.4543 11.0746
83 nostr.trevena.info 43.6532 -79.3832
84 relay.qstr.app 50.1109 8.68213
85 relay.chorus.community:443 50.1109 8.68213
86 relay.plebeian.market:443 50.1109 8.68213
87 relay.nostrops.com 32.71 -96.6745
88 nostr.chaima.info:443 50.1109 8.68213
89 relay.nostr.dev.br 48.1046 11.6002
90 relay.camelus.app 45.5201 -122.99
91 nostr-pub.wellorder.net 45.5201 -122.99
92 cyberspace.nostr1.com 40.7057 -74.0136
93 relay2.angor.io 48.1046 11.6002
94 relay.edufeed.org:443 49.4521 11.0767
95 relay.bnos.space 43.6532 -79.3832
96 groups.beginningend.com 35.2227 -97.4786
97 relay.gulugulu.moe:443 43.6532 -79.3832
98 relayone.soundhsa.com 39.0997 -94.5786
99 relay.noeudlibre.fr 50.6924 3.20113
100 nostr.christiansass.de 50.1109 8.68213
101 chat-relay.zap-work.com:443 43.6532 -79.3832
102 conduit-congee.fly.dev 38.7946 -106.535
103 relay.satmaxt.xyz:443 43.6532 -79.3832
104 relay.rebelbase.social 50.965 5.50082
105 nostr.relay.hedwig.sh 60.1699 24.9384
106 dev.relay.stream 43.6532 -79.3832
107 relay.edufeed.org 49.4521 11.0767
108 relay.s-w.art 43.6532 -79.3832
109 nostr.wecsats.io 43.6532 -79.3832
110 relay.nostreon.com 60.1699 24.9384
111 relay.agentry.com 42.8864 -78.8784
112 denimroad.feeds.relay.tools 38.6327 -90.1961
113 relay.artx.market 43.6548 -79.3885
114 nostr.unkn0wn.world 47.4464 8.21073
115 relay.mappingbitcoin.com 43.6532 -79.3832
116 relay-na1.metanomalist.com 43.6532 -79.3832
117 nostr.2b9t.xyz 34.0549 -118.243
118 relay.artio.inf.unibe.ch 46.9501 7.43678
119 relay.mostro.network 40.8302 -74.1299
120 relay.nmail.li 50.9871 2.12554
121 bruh.samt.st 43.6532 -79.3832
122 relay.conduit.market 38.7946 -106.535
123 relay.mwaters.net 50.9871 2.12554
124 nostr.twinkle.lol 51.902 7.6657
125 relay.selfrule.co.za 43.6532 -79.3832
126 relay.btcforplebs.com 43.6532 -79.3832
127 relay.plebeian.market 50.1109 8.68213
128 nostr.n7ekb.net 47.4941 -122.294
129 nostr.easycryptosend.it 43.6532 -79.3832
130 relay.primal.net 43.6532 -79.3832
131 dev-relay.nostreon.com 60.1699 24.9384
132 nostr-kyomu-haskell.onrender.com 37.7775 -122.397
133 relay.kilombino.com 43.6532 -79.3832
134 nostr.bitcoiner.social:443 47.6743 -117.112
135 relay.underorion.se 51.4998 -0.011072
136 relay.paulstephenborile.com:443 49.4543 11.0746
137 relay.nostr.net 43.6532 -79.3832
138 syb.lol 43.6532 -79.3832
139 articles.layer3.news 37.3387 -121.885
140 ec1.f7z.io 60.1699 24.9384
141 relay.manneken.brussels 49.4543 11.0746
142 nostr.tac.lol 47.4748 -122.273
143 relay.agora.social 50.7383 15.0648
144 nostr.oxtr.dev 50.4754 12.3683
145 relay.hivetalk.org 40.8302 -74.1299
146 income-strongly-active-systematic.trycloudflare.com 43.6532 -79.3832
147 nostr.jcera.eu 43.6532 -79.3832
148 relay-dev.satlantis.io 39.0438 -77.4874
149 nostr.sovereignservices.xyz 43.6532 -79.3832
150 relay.lacrypta.ar 43.6532 -79.3832
151 nostr.data.haus 50.4754 12.3683
152 relay.monostr.com 50.4754 12.3683
153 relay.nostrhub.fr 48.1045 11.6004
154 relay.loveisbitcoin.com 43.6532 -79.3832
155 portal-relay.pareto.space 53.5488 9.98717
156 relay.minibolt.info 43.6532 -79.3832
157 relay.czas.plus 50.1109 8.68213
158 relay2.veganostr.com 60.1699 24.9384
159 relay.gorrdy.cz 50.1109 8.68213
160 relay.paulstephenborile.com 49.4543 11.0746
161 temp.iris.to 43.6532 -79.3832
162 relay.formstr.app 43.6532 -79.3832
163 talon.quest 43.6532 -79.3832
164 relay.ltgnet.work:8443 51.0511 -114.075
165 relay.guggero.org 46.5971 9.59652
166 relay.nostr.bond 40.1885 29.061
167 bitchat.nostr1.com 40.7057 -74.0136
168 prl.plus 54.9486 21.7283
169 relay.sharegap.net 43.6532 -79.3832
170 nostr.21crypto.ch 47.5356 8.73209
171 relay.jcera.eu 43.6532 -79.3832
172 r.senx.io 50.6924 3.20113
173 porchlight.social 43.6532 -79.3832
174 bread.nostrsms.com 40.8218 -74.45
175 relay.nostrian-conquest.com 41.223 -111.974
176 relay.getsafebox.app:443 43.6532 -79.3832
177 nostr.quali.chat 60.1699 24.9384
178 relay.laantungir.net -19.4692 -42.5315
179 relay.hivescope.xyz 40.4332 -3.62431
180 nostr.fullstackcash.net 45.5201 -122.99
181 nip85-staging.nosfabrica.com 39.0997 -94.5786
182 relay-fra.zombi.cloudrodion.com 48.8566 2.35222
183 relay.nostr.black 43.6532 -79.3832
184 nostr.trepechov.com 49.4543 11.0746
185 relay.binaryrobot.com:443 43.6532 -79.3832
186 basspistol.org 49.0291 8.35696
187 free.relayted.de 50.1109 8.68213
188 relay.beginningend.com 35.2227 -97.4786
189 nostrelay.circum.space:443 52.2245 8.826
190 strfry.bonsai.com:443 39.0438 -77.4874
191 relay.wasabiwallet.io 43.6532 -79.3832
192 relay.scuba323.com 40.8218 -74.45
193 vidar.nestoris.online 52.3676 4.90414
194 nostr.pbfs.io:443 50.4754 12.3683
195 relay.pyramid.li 47.4093 8.46503
196 relay.olas.app 60.1699 24.9384
197 relay.internationalright-wing.org -22.4692 -48.9875
198 nostr-relay.corb.net 39.6478 -104.988
199 relay.plebdevs.com 40.7128 -74.006
200 relay.ditto.pub 43.6532 -79.3832
201 relay.i9.eti.br 43.6532 -79.3832
202 nostr.pro.aw 43.6532 -79.3832
203 relay.hazeem.xyz 43.6532 -79.3832
204 zencrown.relay2aeon.quest 52.3061 4.6907
205 relay.npubhaus.com 43.6532 -79.3832
206 relay.nostriot.com 41.5695 -83.9786
207 cs-relay.nostrdev.com 50.4754 12.3683
208 relay.substation.ninja 43.6532 -79.3832
209 relay1.nostrchat.io 60.1699 24.9384
210 nostr-relay.cbrx.io 43.6532 -79.3832
211 nostr.stakey.net:443 52.6485 4.73787
212 relayone.geektank.ai:443 39.0997 -94.5786
213 adre.su 59.9311 30.3609
214 nostr.islandarea.net 35.4669 -97.6473
215 relay.cypherguide.org 1.32123 103.695
216 fanfares.nostr1.com 40.7057 -74.0136
217 nostrride.io 43.6532 -79.3832
218 relay.littlebitstudios.com 34.8688 -86.5502
219 relay.snotr.nl:49999 51.9758 4.31389
220 relay02.lnfi.network 35.6764 139.65
221 nostr.rtvslawenia.com 49.4543 11.0746
222 relay.staging.plebeian.market:443 51.5072 -0.127586
223 relay.earthly.city 34.1749 -118.54
224 relay.binaryrobot.com 43.6532 -79.3832
225 relay.artiostr.ch 43.6532 -79.3832
226 nostr.nestoris.online 55.568 37.5853
227 nostr.liberty.fans 36.8767 -89.5879
228 n.bogs.ccwu.cc 43.6532 -79.3832
229 relay.t6x.host 40.8302 -74.1299
230 relay.nostrverse.net 43.6532 -79.3832
231 relay.bullishbounty.com:443 43.6532 -79.3832
232 nostr.mom 50.4754 12.3683
233 scores.brainstorm.world 39.0997 -94.5786
234 nostr.tabordalab.com 60.1699 24.9384
235 relay.favillakey.io -27.4705 153.026
236 relay.nostr.blockhenge.com 39.0438 -77.4874
237 relay.bullishbounty.com 43.6532 -79.3832
238 vm-1734.lnvps.cloud 53.3498 -6.26031
239 nostr.bitcoiner.social 47.6743 -117.112
240 budabit.nostr1.com 40.7057 -74.0136
241 soloco.nl 43.6532 -79.3832
242 relay.iyou.me 64.1466 -21.9426
243 dm-test-nostr-rs-42-disabled.samt.st 43.6532 -79.3832
244 relay.cloistr.xyz 42.337 -77.3178
245 relay.nearhood.co.uk 51.5134 -0.0890675
246 offchain.bostr.online 43.6532 -79.3832
247 relay.keykeeper.world 40.7824 -74.0711
248 nostras.app 38.7946 -106.535
249 nostr.wild-vibes.ts.net 48.8566 2.35222
250 nostr-relayflare.enpitsulin.workers.dev 43.6532 -79.3832
251 relay.lanacoin-eternity.com:443 40.8302 -74.1299
252 greenart7c3.feeds.relay.tools 38.6327 -90.1961
253 relay.mitosystem.net -23.5558 -46.6396
254 nostr.snowbla.de 50.4754 12.3683
255 cs-relay.nostrdev.com:443 50.4754 12.3683
256 nostrelay.circum.space 52.2245 8.826
257 0x-nostr-relay.fly.dev 38.7946 -106.535
258 nip85.nosfabrica.com 39.0997 -94.5786
259 chorus.mikedilger.com:444 -36.8906 174.794
260 nostr.rtvslawenia.com:443 49.4543 11.0746
261 yabu.me 35.6096 139.73
262 nostr.planix.org 43.6532 -79.3832
263 relay2.orangesync.tech 40.7128 -74.006
264 relay.dyne.org 49.0291 8.35705
265 relay.aarpia.com 37.3986 -121.964
266 relay.fiatdenier.com 43.7787 -79.5393
267 relay.wisp.talk:443 49.4543 11.0746
268 nostr.azzamo.net 52.2633 21.0283
269 relay.satmaxt.xyz 43.6532 -79.3832
270 relay.jmoose.rocks:443 32.7767 -96.797
271 nostr-01.uid.ovh 50.9871 2.12554
272 nostr-relay.corb.net:443 39.6478 -104.988
273 buzz.cashu.space 43.6532 -79.3832
274 dev-relay.nostr.space 43.6532 -79.3832
275 relay.cyberguy.fyi 52.3676 4.90414
276 freelay.sovbit.host 60.1699 24.9384
277 hasky.chat 40.7862 -74.0743
278 treuzkas.branruz.com 48.8575 2.35138
279 nostr-verified.wellorder.net 45.5201 -122.99
280 nexus.libernet.app 43.6532 -79.3832
281 relay.gulugulu.moe 43.6532 -79.3832
282 bitcoiner.social 47.6743 -117.112
283 nostr.stakey.net 52.6485 4.73787
284 schnorr.me 43.6532 -79.3832
285 nos.xmark.cc 50.6916 3.20151
286 vault.iris.to 43.6532 -79.3832
287 nostr.nodesmap.com 59.3327 18.0656
288 nostr.88mph.life 52.1941 -2.21905
289 nostr.yutakobayashi.com 43.6532 -79.3832
290 relay.layer.systems 43.6532 -79.3832
291 relay-can.zombi.cloudrodion.com 43.6532 -79.3832
292 bcast.girino.org 43.6532 -79.3832
293 relay.chorus.community 50.1109 8.68213
294 strfry.shock.network 39.0438 -77.4874
295 ynostr.yael.at 60.1699 24.9384
296 relay.staging.plebeian.market 51.5072 -0.127586
297 cache.trustr.ing 43.6548 -79.3885
298 nos.lol:443 50.4754 12.3683
299 relay.shadowbip.com 54.352 18.6466
300 nostr.computingcache.com 45.5341 -122.956
301 memlay.v0l.io 53.3498 -6.26031
302 relay.colombiap2p.com 43.6532 -79.3832
303 dm-test-strfry-generic.samt.st 43.6532 -79.3832
304 borgar.lol 52.3676 4.90414
305 relay.wellorder.net 45.5201 -122.99
306 node.kommonzenze.de 49.4521 11.0767
307 relay.ru.ac.th 13.7607 100.627
308 nostr.davenov.com 50.1109 8.68213
309 nostr.linky.fit 50.1109 8.68213
310 nostr-relay.zimage.com 34.282 -118.439
311 relay.nostx.io 43.6532 -79.3832
312 relay.dwadziesciajeden.pl 52.2297 21.0122
313 relay.pornstr.net 53.3498 -6.26031
314 nostr-rs-relay.dev.fedibtc.com 39.0438 -77.4874
315 schnorr.me:443 43.6532 -79.3832
316 nostr.plantroon.com 50.1013 8.62643
317 relay.routstr.com 59.4016 17.9455
318 relay.shychat.lol 39.8965 -80.1792
319 nr.yay.so 46.2126 6.1154
320 pymd.kamp.site 38.9072 -77.0369
321 relay.decentralia.fr 48.122 11.589
322 relay.klabo.world 47.674 -122.122
323 spurline.safebox.dev 43.6532 -79.3832
324 bridge.tagomago.me 42.3601 -71.0589
325 inbox.scuba323.com 40.8218 -74.45
326 no.str.cr:443 8.96171 -83.5246
327 nostr2.girino.org:443 43.6532 -79.3832
328 nostr-dev.wellorder.net 45.5201 -122.99
329 relay.mypathtofire.de 42.8864 -78.8784
330 nostr-02.uid.ovh 50.9871 2.12554
331 support.flotilla.social 32.9483 -96.7299
332 relay.mccormick.cx 52.3563 4.95714
333 relay.getsafebox.app 43.6532 -79.3832
334 relay.bitos.space 43.6532 -79.3832
335 nostrcity-club.fly.dev 38.7946 -106.535
336 spamspamspamspam.rest 43.6532 -79.3832
337 relay.solomonstr.com 43.6532 -79.3832
338 relay.angor.io 48.1046 11.6002
339 relay.islandbitcoin.com 20.3898 78.0903
340 relay.bornheimer.app 50.1109 8.68213
341 relay.atsocy.com 43.6532 -79.3832
342 relay.wavlake.com 41.2619 -95.8608
343 relay.nostrcheck.me 43.6532 -79.3832
344 relay.bowlafterbowl.com 32.9483 -96.7299
345 relay.novospes.com 43.6532 -79.3832
346 nostr.spaceshell.xyz 43.6532 -79.3832
347 purplerelay.com 43.6532 -79.3832
348 relay.satsapp.me 50.4754 12.3683
349 nostr.pbfs.io 50.4754 12.3683
350 relay2.nostrasia.net 43.6532 -79.3832
351 relay.piazza.today 48.122 11.589
352 relay.nostr.space 43.6532 -79.3832
353 relay.duck1123.com 43.6532 -79.3832
354 relay.kizuna-miki.com 35.694 139.754
355 relay.dreamith.to 43.6532 -79.3832
356 relay.chatbett.de 40.7128 -74.006
357 public.crostr.com:443 43.6532 -79.3832
358 relay.satlantis.io 39.0438 -77.4874
359 nostr2.girino.org 43.6532 -79.3832
360 relay.lotek-distro.com 43.6532 -79.3832
361 nostr-rs-relay.dev.fedibtc.com:443 39.0438 -77.4874
362 nostrrelay.taylorperron.com 45.5029 -73.5723
363 nosflare-leefcore.leefcore.workers.dev 43.6532 -79.3832
364 nostr.spicyz.io 43.6532 -79.3832
365 x.kojira.io 43.6532 -79.3832
366 relay.veganostr.com:443 60.1699 24.9384
367 nostr.alex71btc.com 43.6532 -79.3832
368 relay.thewired.app 39.0438 -77.4874
369 relay.getvia.xyz 60.1699 24.9384
370 relayone.geektank.ai 39.0997 -94.5786
371 nostr.hoppe-relay.it.com 42.8864 -78.8784
372 relay2.0xprivacy.online 43.6532 -79.3832
373 nostr-01.yakihonne.com 1.32123 103.695
374 offchain.pub 39.1585 -94.5728
375 nostrcity-club.fly.dev:443 38.7946 -106.535
376 testr.nymble.world 40.8054 -74.0241
377 relay.fountain.fm 43.6532 -79.3832
378 relay.nostr.com 50.1109 8.68213

View File

@ -45,6 +45,9 @@ class BitchatApplication : Application() {
com.bitchat.android.nostr.NostrIdentityBridge.getCurrentNostrIdentity(this)
} catch (_: Exception) { }
// Restore the spam filter toggle before transports start admitting public messages
com.bitchat.android.spam.SpamFilterPreferences.init(this)
// Initialize theme preference
ThemePreferenceManager.init(this)

View File

@ -34,7 +34,8 @@ fun BitchatSheetTopBar(
val dismiss = LocalSheetDismiss.current
CloseButton(
onClick = { dismiss?.invoke() ?: onClose() },
modifier = Modifier.padding(horizontal = 16.dp)
// 2.dp from any preceding action, matching the conversation header's action spacing.
modifier = Modifier.padding(start = 2.dp, end = 16.dp)
)
},
colors = TopAppBarDefaults.topAppBarColors(
@ -64,7 +65,8 @@ fun BitchatSheetCenterTopBar(
val dismiss = LocalSheetDismiss.current
CloseButton(
onClick = { dismiss?.invoke() ?: onClose() },
modifier = Modifier.padding(horizontal = 16.dp)
// 2.dp from any preceding action, matching the conversation header's action spacing.
modifier = Modifier.padding(start = 2.dp, end = 16.dp)
)
},
colors = TopAppBarDefaults.topAppBarColors(

View File

@ -97,8 +97,10 @@ class SecurityManager(private val encryptionService: EncryptionService, private
// Record only authenticated packets. Recording an attacker-controlled
// invalid packet first would let it poison duplicate detection for a
// later legitimate packet with the same timestamp and payload.
processedMessages.add(messageID)
messageTimestamps[messageID] = currentTime
synchronized(messageTimestamps) {
processedMessages.add(messageID)
messageTimestamps[messageID] = currentTime
}
return true
}
@ -136,8 +138,10 @@ class SecurityManager(private val encryptionService: EncryptionService, private
// flow and reports whether this exact frame completed authentication. Never infer that
// from ambient session state: a rejected replacement may leave the old session active.
val result = encryptionService.processHandshakeMessageWithResult(packet.payload, peerID)
processedKeyExchanges.add(exchangeKey)
keyExchangeTimestamps[exchangeKey] = System.currentTimeMillis()
synchronized(keyExchangeTimestamps) {
processedKeyExchanges.add(exchangeKey)
keyExchangeTimestamps[exchangeKey] = System.currentTimeMillis()
}
if (result.response != null) {
// Send handshake response through delegate
@ -389,42 +393,45 @@ class SecurityManager(private val encryptionService: EncryptionService, private
internal fun cleanupOldData(nowMs: Long = System.currentTimeMillis()) {
val cutoffTime = nowMs - MESSAGE_TIMEOUT
// Clean up old message timestamps and corresponding processed messages
val messagesToRemove = messageTimestamps.entries.filter { (_, timestamp) ->
timestamp < cutoffTime
}.map { it.key }
messagesToRemove.forEach { messageId ->
messageTimestamps.remove(messageId)
processedMessages.remove(messageId)
// Synchronized collection wrappers protect individual operations, not iteration.
// Keep each timestamp/set pair locked through expiry and capacity eviction so
// cleanup cannot remove a freshly recorded entry or traverse a mutating set.
synchronized(messageTimestamps) {
synchronized(processedMessages) {
val messagesToRemove = messageTimestamps.entries.filter { (_, timestamp) ->
timestamp < cutoffTime
}.map { it.key }
messagesToRemove.forEach { messageId ->
messageTimestamps.remove(messageId)
processedMessages.remove(messageId)
}
if (processedMessages.size > MAX_PROCESSED_MESSAGES) {
val excess = processedMessages.size - MAX_PROCESSED_MESSAGES
val toRemove = processedMessages.take(excess)
processedMessages.removeAll(toRemove.toSet())
removeFromMessageTimestamps(toRemove)
}
}
}
// Limit the size of processed messages set
if (processedMessages.size > MAX_PROCESSED_MESSAGES) {
val excess = processedMessages.size - MAX_PROCESSED_MESSAGES
val toRemove = processedMessages.take(excess)
processedMessages.removeAll(toRemove.toSet())
removeFromMessageTimestamps(toRemove)
}
// Expire handshake dedup entries by time so a delayed same-ephemeral delivery
// (e.g. a re-handshake retry after a failed attempt) is not blocked forever.
val keyExchangeCutoff = nowMs - KEY_EXCHANGE_DEDUP_TIMEOUT
val keyExchangesToRemove = keyExchangeTimestamps.entries.filter { (_, timestamp) ->
timestamp < keyExchangeCutoff
}.map { it.key }
keyExchangesToRemove.forEach { exchangeKey ->
keyExchangeTimestamps.remove(exchangeKey)
processedKeyExchanges.remove(exchangeKey)
}
// Limit the size of processed key exchanges set
if (processedKeyExchanges.size > MAX_PROCESSED_KEY_EXCHANGES) {
val excess = processedKeyExchanges.size - MAX_PROCESSED_KEY_EXCHANGES
val toRemove = processedKeyExchanges.take(excess)
processedKeyExchanges.removeAll(toRemove.toSet())
toRemove.forEach { keyExchangeTimestamps.remove(it) }
synchronized(keyExchangeTimestamps) {
synchronized(processedKeyExchanges) {
// Expire delayed handshake retries as well as bounding the cache size.
val keyExchangeCutoff = nowMs - KEY_EXCHANGE_DEDUP_TIMEOUT
val keyExchangesToRemove = keyExchangeTimestamps.entries.filter { (_, timestamp) ->
timestamp < keyExchangeCutoff
}.map { it.key }
keyExchangesToRemove.forEach { exchangeKey ->
keyExchangeTimestamps.remove(exchangeKey)
processedKeyExchanges.remove(exchangeKey)
}
if (processedKeyExchanges.size > MAX_PROCESSED_KEY_EXCHANGES) {
val excess = processedKeyExchanges.size - MAX_PROCESSED_KEY_EXCHANGES
val toRemove = processedKeyExchanges.take(excess)
processedKeyExchanges.removeAll(toRemove.toSet())
toRemove.forEach { keyExchangeTimestamps.remove(it) }
}
}
}
}
@ -441,10 +448,14 @@ class SecurityManager(private val encryptionService: EncryptionService, private
* Clear all security data
*/
fun clearAllData() {
processedMessages.clear()
processedKeyExchanges.clear()
messageTimestamps.clear()
keyExchangeTimestamps.clear()
synchronized(messageTimestamps) {
processedMessages.clear()
messageTimestamps.clear()
}
synchronized(keyExchangeTimestamps) {
processedKeyExchanges.clear()
keyExchangeTimestamps.clear()
}
}
/**

View File

@ -3,6 +3,7 @@ package com.bitchat.android.nostr
import android.app.Application
import android.util.Log
import com.bitchat.android.model.BitchatMessage
import com.bitchat.android.spam.SpamFilter
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -14,7 +15,7 @@ import java.util.Date
* GeohashMessageHandler
* - Processes kind=20000 Nostr events for geohash channels
* - Updates repository for participants + nicknames
* - Emits messages to MessageManager
* - Hides spam via [SpamFilter], then emits messages to MessageManager
*/
class GeohashMessageHandler(
private val application: Application,
@ -22,7 +23,9 @@ class GeohashMessageHandler(
private val scope: CoroutineScope,
private val dataManager: com.bitchat.android.ui.DataManager,
private val addChannelMessage: (String, BitchatMessage) -> Unit,
private val signatureVerificationDispatcher: CoroutineDispatcher = Dispatchers.Default
private val signatureVerificationDispatcher: CoroutineDispatcher = Dispatchers.Default,
// The foreground handler runs on the main thread; the spam checks are CPU work.
private val spamFilterDispatcher: CoroutineDispatcher = signatureVerificationDispatcher
) {
companion object { private const val TAG = "GeohashMessageHandler" }
@ -111,6 +114,13 @@ class GeohashMessageHandler(
if (hasNonce) NostrProofOfWork.calculateDifficulty(event.id).takeIf { it > 0 } else null
} catch (_: Exception) { null }
)
val hide = withContext(spamFilterDispatcher) {
SpamFilter.shared.shouldHide(msg, SpamFilter.nostrSenderKey(pubkey))
}
if (hide) {
Log.d(TAG, "Hiding geohash spam ${event.id.take(8)}...")
return@launch
}
addChannelMessage("geo:$subscribedGeohash", msg)
} catch (e: Exception) {
Log.e(TAG, "onEvent error: ${e.message}")

View File

@ -424,7 +424,7 @@ class NostrRelayManager private constructor() {
liveLocationConnectionJobs.forEach(Job::cancel)
liveLocationConnectionJobs.clear()
val sockets = connections.values.toList()
val sockets = buildList { connections.values.forEach { add(it) } }
connections.clear()
sockets.forEach { webSocket ->
webSocket.close(1000, "Manual disconnect")
@ -514,7 +514,10 @@ class NostrRelayManager private constructor() {
scope.launch {
if (!isNetworkActionAllowed(subscriptionInfo.liveLocationToken)) return@launch
val targetRelays = subscriptionInfo.targetRelayUrls?.toList() ?: connections.keys.toList()
// A concurrent view can shrink between Collection.size and iterator.next.
// Build by iteration instead of Kotlin's size-based toList fast paths.
val targetRelays = subscriptionInfo.targetRelayUrls?.toList()
?: buildList { connections.keys.forEach { add(it) } }
targetRelays.forEach { relayUrl ->
val webSocket = connections[relayUrl]

View File

@ -1,6 +1,7 @@
package com.bitchat.android.services
import com.bitchat.android.model.BitchatMessage
import com.bitchat.android.spam.SpamFilter
import kotlinx.coroutines.runBlocking
/**
@ -8,7 +9,8 @@ import kotlinx.coroutines.runBlocking
*
* Private-message admission is authoritative: a duplicate or a message rejected while panic mode
* is wiping state must not continue to UI delegates, unread tracking, haptics, or notifications.
* Public and channel messages retain their existing best-effort behavior if state reflection fails.
* Public and channel messages retain their existing best-effort behavior if state reflection fails,
* and are refused (no timeline row, no UI callback) when the spam filter hides them.
*/
internal object IncomingMessageAdmission {
fun admitToAppState(message: BitchatMessage): Boolean = try {
@ -24,6 +26,8 @@ internal object IncomingMessageAdmission {
}
}
isHiddenAsSpam(message) -> false
message.channel != null -> {
AppStateStore.addChannelMessage(message.channel, message)
true
@ -39,4 +43,9 @@ internal object IncomingMessageAdmission {
// bypass private-message admission when persistence or canonicalization fails.
!message.isPrivate
}
private fun isHiddenAsSpam(message: BitchatMessage): Boolean {
if (SpamFilter.isLocalNotice(message)) return false
return SpamFilter.shared.shouldHide(message, SpamFilter.meshSenderKey(message))
}
}

View File

@ -0,0 +1,14 @@
package com.bitchat.android.spam
/**
* Counts links the way the chat linkifies them: scheme URLs, `www.` hosts and bare domains
* such as `example.com/path`. Pure Kotlin so it also runs on the watch and in JVM tests.
*/
internal object LinkCounter {
private val LINK = Regex(
"""(?i)(?<![\w@.\-])(?:https?://[^\s<>]+|www\.[^\s<>]+|[a-z0-9\-]+(?:\.[a-z0-9\-]+)*\.[a-z]{2,}(?:/[^\s<>]*)?)(?![\w@])"""
)
fun count(text: String): Int = LINK.findAll(text).count()
}

View File

@ -0,0 +1,187 @@
package com.bitchat.android.spam
import java.text.Normalizer
/**
* Character autocorrelation measures for repeated-content spam.
*
* Text is compared as Unicode code points after [normalize], so case, spacing, look-alike
* letters, accents and invisible characters don't hide a repeat.
*/
internal object RepetitionDetector {
/**
* Folds a message to what a reader sees, for comparison only (never for display): NFKD
* turns fullwidth and math-styled letters into plain ones and splits off accents, then
* combining marks and invisible characters are dropped, whitespace runs become one space
* and the result is lowercased and capped to [maxLength] code points.
*/
fun normalize(text: String, maxLength: Int): IntArray {
// Disguises at most a few code units per visible character; bound the work up front.
val head = if (text.length > maxLength * 4) text.substring(0, maxLength * 4) else text
val decomposed = Normalizer.normalize(head, Normalizer.Form.NFKD)
val out = IntArray(minOf(maxLength, decomposed.length))
var size = 0
var pendingSpace = false
var offset = 0
while (offset < decomposed.length && size < out.size) {
val point = decomposed.codePointAt(offset)
offset += Character.charCount(point)
when {
isMark(point) || isInvisible(point) -> continue
isWhitespace(point) -> pendingSpace = size > 0
else -> {
if (pendingSpace && size < out.size - 1) out[size++] = ' '.code
pendingSpace = false
out[size++] = Character.toLowerCase(point)
}
}
}
return out.copyOf(size)
}
private fun isWhitespace(point: Int): Boolean =
Character.isWhitespace(point) || Character.isSpaceChar(point)
/** Zero-width and direction controls, soft hyphens and other non-printing characters. */
private fun isInvisible(point: Int): Boolean =
Character.getType(point) == Character.FORMAT.toInt() ||
(Character.isISOControl(point) && !isWhitespace(point))
private fun isMark(point: Int): Boolean = when (Character.getType(point)) {
Character.NON_SPACING_MARK.toInt(),
Character.COMBINING_SPACING_MARK.toInt(),
Character.ENCLOSING_MARK.toInt() -> true
else -> false
}
/**
* Highest autocorrelation of [text] with itself over shifts 1..n/2: the fraction of
* overlapping positions where `text[i] == text[i + lag]`. A pattern repeated back to back
* ("aaaa", "buy now buy now") scores 1.0; ordinary prose stays well below 0.3.
*/
fun selfSimilarity(text: IntArray): Double {
val n = text.size
var best = 0.0
for (lag in 1..n / 2) {
val overlap = n - lag
var matches = 0
for (i in 0 until overlap) if (text[i] == text[i + lag]) matches++
val score = matches.toDouble() / overlap
if (score > best) best = score
}
return best
}
/** One diagonal of the cross-correlation: `b[j]` lined up under `a[j + shift]`. */
data class Peak(val shift: Int, val matches: Int)
/** How two texts line up: the peaks used and the share of the longer text they cover. */
data class Alignment(val peaks: List<Peak>, val matches: Int, val score: Double)
fun crossSimilarity(a: IntArray, b: IntArray, maxPeaks: Int = 3, minRun: Int = 3): Double =
align(a, b, maxPeaks, minRun).score
/**
* Cross-correlation of two texts that tolerates small edits.
*
* The first peak is the classic correlation maximum, the shift where the most characters
* match. An inserted or deleted character moves the rest of the text onto a neighboring
* shift, so up to [maxPeaks] - 1 further peaks add matches from other shifts. Those count
* only runs of at least [minRun] consecutive characters, so coincidental single letters
* don't add up, and no character of either text is used twice. The score is the matched
* characters over the longer length: 1.0 for identical texts, and a short text never
* matches a long one.
*/
fun align(a: IntArray, b: IntArray, maxPeaks: Int = 3, minRun: Int = 3): Alignment {
if (a.isEmpty() || b.isEmpty()) return Alignment(emptyList(), 0, 0.0)
val usedA = BooleanArray(a.size)
val usedB = BooleanArray(b.size)
val peaks = ArrayList<Peak>()
var total = 0
while (peaks.size < maxPeaks) {
val run = if (peaks.isEmpty()) 1 else minRun
var bestGain = 0
var bestShift = 0
for (shift in -(b.size - 1) until a.size) {
val from = maxOf(0, -shift)
val to = minOf(b.size, a.size - shift)
if (to - from <= bestGain) continue
val gain = runMatches(a, b, shift, from, to, usedA, usedB, run, mark = false)
if (gain > bestGain) {
bestGain = gain
bestShift = shift
}
}
if (bestGain < run) break
val from = maxOf(0, -bestShift)
val to = minOf(b.size, a.size - bestShift)
runMatches(a, b, bestShift, from, to, usedA, usedB, run, mark = true)
peaks += Peak(bestShift, bestGain)
total += bestGain
}
return Alignment(peaks, total, total.toDouble() / maxOf(a.size, b.size))
}
/** Unused matches at [shift] that sit in runs of at least [minRun]; [mark] claims them. */
private fun runMatches(
a: IntArray,
b: IntArray,
shift: Int,
from: Int,
to: Int,
usedA: BooleanArray,
usedB: BooleanArray,
minRun: Int,
mark: Boolean
): Int {
var gain = 0
var runStart = -1
for (j in from..to) {
val hit = j < to && !usedB[j] && !usedA[j + shift] && a[j + shift] == b[j]
if (hit) {
if (runStart < 0) runStart = j
} else if (runStart >= 0) {
val length = j - runStart
if (length >= minRun) {
gain += length
if (mark) for (k in runStart until j) {
usedB[k] = true
usedA[k + shift] = true
}
}
runStart = -1
}
}
return gain
}
/** Distinct character trigrams as sorted hashes, for a cheap pre-check before [align]. */
fun trigrams(text: IntArray): IntArray {
if (text.size < 3) return IntArray(0)
return IntArray(text.size - 2) { i -> (text[i] * 31 + text[i + 1]) * 31 + text[i + 2] }
.distinct()
.sorted()
.toIntArray()
}
/**
* Shared trigrams over the larger trigram set. Near-copies share most of theirs, unrelated
* texts few, so pairs below a low bar can skip the quadratic [align]. Texts too short to
* have trigrams always pass.
*/
fun trigramOverlap(a: IntArray, b: IntArray): Double {
if (a.isEmpty() || b.isEmpty()) return 1.0
var i = 0
var j = 0
var shared = 0
while (i < a.size && j < b.size) {
when {
a[i] == b[j] -> { shared++; i++; j++ }
a[i] < b[j] -> i++
else -> j++
}
}
return shared.toDouble() / maxOf(a.size, b.size)
}
}

View File

@ -0,0 +1,246 @@
package com.bitchat.android.spam
import com.bitchat.android.model.BitchatMessage
import com.bitchat.android.model.BitchatMessageType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlin.math.abs
enum class SpamVerdict {
ALLOWED,
MESSAGE_RATE,
CHARACTER_RATE,
LINK_RATE,
REPEATED_MESSAGE,
REPEATED_ACROSS_USERS,
REPEATED_CHARACTERS;
val isSpam: Boolean get() = this != ALLOWED
}
/**
* Decides whether an incoming public message should be hidden.
*
* Each sender gets three token buckets (messages, characters, links; see [SpamFilterConfig])
* plus three repetition checks: a message that repeats itself internally, a near-copy of the
* sender's own recent history, and a near-copy of what *other* senders posted recently. The last
* one catches spam spread over many throwaway identities, which per-sender limits cannot see.
* Hidden messages never spend tokens, but they do enter the repetition history so a spammer
* cannot cycle the same text.
*
* Verdicts are remembered by message ID, so the same message arriving over BLE and Wi-Fi Aware,
* or through both geohash handlers, is judged once and counted once.
*/
class SpamFilter(
config: SpamFilterConfig = SpamFilterConfig(),
private val clock: () -> Long = System::currentTimeMillis
) {
@Volatile
var enabled: Boolean = true
/** Changing the limits forgets every sender, so new budgets start full. */
@Volatile
var config: SpamFilterConfig = config
set(value) {
synchronized(this) {
field = value
senders.clear()
verdicts.clear()
recentTexts.clear()
}
}
/** Debug: let spam through and record its verdict in [flagged] so the chat can mark it. */
@Volatile
var debugBypass: Boolean = false
private val _flagged = MutableStateFlow<Map<String, SpamVerdict>>(emptyMap())
/** Verdicts of spam shown because of [debugBypass], by message ID. */
val flagged: StateFlow<Map<String, SpamVerdict>> = _flagged.asStateFlow()
/** A message prepared for comparison: its leading characters and their trigrams. */
private class Fingerprint(val timeMs: Long, val text: IntArray, val trigrams: IntArray)
private class SharedEntry(val senderKey: String, val fingerprint: Fingerprint)
private inner class SenderState {
val rate = TokenBucketSchedule(
listOf(config.messageBucket, config.characterBucket, config.linkBucket)
)
val history = ArrayDeque<Fingerprint>()
}
private val senders = object : LinkedHashMap<String, SenderState>(64, 0.75f, true) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, SenderState>) =
size > config.maxTrackedSenders
}
/** Recent texts from every sender, for the cross-user repeat check. */
private val recentTexts = ArrayDeque<SharedEntry>()
private val verdicts = object : LinkedHashMap<String, SpamVerdict>(256, 0.75f, false) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, SpamVerdict>) =
size > config.maxRememberedVerdicts
}
/**
* Returns true when [message] must be kept out of the chat. With [debugBypass] on, spam is
* let through and recorded in [flagged] instead.
*/
fun shouldHide(message: BitchatMessage, senderKey: String): Boolean {
val verdict = check(message, senderKey)
if (!verdict.isSpam) return false
if (!debugBypass) return true
_flagged.update { current ->
if (current[message.id] == verdict) current
else (current + (message.id to verdict)).entries
.toList()
.takeLast(MAX_FLAGGED)
.associate { it.key to it.value }
}
return false
}
/** Checks a received message; [senderKey] must identify the author stably within a transport. */
fun check(message: BitchatMessage, senderKey: String): SpamVerdict = check(
senderKey = senderKey,
messageId = message.id,
content = message.content,
timestampMs = message.timestamp.time,
isText = message.type == BitchatMessageType.Message
)
fun check(
senderKey: String,
messageId: String,
content: String,
timestampMs: Long,
isText: Boolean = true
): SpamVerdict {
if (!enabled) return SpamVerdict.ALLOWED
synchronized(this) {
verdicts[messageId]?.let { return it }
val now = clock()
val oldestTrusted = now - config.maxBackdateMs
val time = timestampMs.coerceIn(oldestTrusted, now)
val state = senders.getOrPut(senderKey) { SenderState() }
state.rate.prune(oldestTrusted)
val repetition = config.repetition
val text = if (isText) RepetitionDetector.normalize(content, repetition.maxAnalyzedLength) else null
val fingerprint = text?.let { fingerprint(it, time) }
val verdict = when {
text != null && hasRepeatedCharacters(text) -> SpamVerdict.REPEATED_CHARACTERS
text != null && repeatsHistory(state, text.size, fingerprint!!) -> SpamVerdict.REPEATED_MESSAGE
text != null && repeatsOtherSenders(senderKey, text.size, fingerprint!!) ->
SpamVerdict.REPEATED_ACROSS_USERS
else -> admitRate(state, content, time, isText)
}
if (text != null && fingerprint != null) {
state.history.addLast(fingerprint)
while (state.history.size > repetition.historySize) state.history.removeFirst()
if (text.size >= repetition.crossUserMinLength) {
recentTexts.addLast(SharedEntry(senderKey, fingerprint))
while (recentTexts.size > repetition.crossUserHistorySize) recentTexts.removeFirst()
}
}
verdicts[messageId] = verdict
return verdict
}
}
/** Forgets all senders, e.g. when the filter is toggled or the app is wiped. */
fun reset() {
synchronized(this) {
senders.clear()
verdicts.clear()
recentTexts.clear()
}
_flagged.value = emptyMap()
}
private fun hasRepeatedCharacters(text: IntArray): Boolean {
val repetition = config.repetition
return text.size >= repetition.minRepeatedCharactersLength &&
RepetitionDetector.selfSimilarity(text) >= repetition.repeatedCharactersThreshold
}
private fun fingerprint(text: IntArray, timeMs: Long): Fingerprint {
val max = config.repetition.maxComparedLength
val compared = if (text.size > max) text.copyOf(max) else text
return Fingerprint(timeMs, compared, RepetitionDetector.trigrams(compared))
}
private fun repeatsHistory(state: SenderState, length: Int, message: Fingerprint): Boolean {
val repetition = config.repetition
if (length < repetition.minRepeatedMessageLength) return false
val copies = state.history.count { isNearCopy(it, message) }
return copies > repetition.allowedRepeats
}
private fun repeatsOtherSenders(senderKey: String, length: Int, message: Fingerprint): Boolean {
val repetition = config.repetition
if (length < repetition.crossUserMinLength) return false
var copies = 0
for (entry in recentTexts) {
if (entry.senderKey == senderKey || !isNearCopy(entry.fingerprint, message)) continue
if (++copies > repetition.crossUserAllowedRepeats) return true
}
return false
}
private fun isNearCopy(earlier: Fingerprint, message: Fingerprint): Boolean {
val repetition = config.repetition
return abs(earlier.timeMs - message.timeMs) <= repetition.historyWindowMs &&
lengthsCanMatch(earlier.text.size, message.text.size) &&
RepetitionDetector.trigramOverlap(earlier.trigrams, message.trigrams) >= repetition.minSharedTrigrams &&
RepetitionDetector.crossSimilarity(
earlier.text,
message.text,
repetition.maxAlignmentPeaks,
repetition.minPeakRun
) >= repetition.repeatedMessageSimilarity
}
// Cross-similarity is bounded by shorter/longer, so skip pairs that can never reach it.
private fun lengthsCanMatch(a: Int, b: Int): Boolean =
minOf(a, b).toDouble() / maxOf(a, b) >= config.repetition.repeatedMessageSimilarity
private fun admitRate(state: SenderState, content: String, timeMs: Long, isText: Boolean): SpamVerdict {
val characters = if (isText) content.codePointCount(0, content.length).toDouble() else 0.0
val links = if (isText) LinkCounter.count(content).toDouble() else 0.0
return when (state.rate.tryAdmit(timeMs, doubleArrayOf(1.0, characters, links))) {
-1 -> SpamVerdict.ALLOWED
0 -> SpamVerdict.MESSAGE_RATE
1 -> SpamVerdict.CHARACTER_RATE
else -> SpamVerdict.LINK_RATE
}
}
companion object {
private const val MAX_FLAGGED = 500
/** Process-wide filter shared by every transport and geohash handler. */
val shared = SpamFilter()
/**
* True for notices this device generated itself, such as "X favorited you". Every message
* read off the wire carries the sender's peer ID; only local notices have none. The
* nickname is no signal, since any peer can call itself "system".
*/
fun isLocalNotice(message: BitchatMessage): Boolean =
message.senderPeerID == null && message.sender == "system"
/** Key for public mesh and mesh channel messages. */
fun meshSenderKey(message: BitchatMessage): String =
"mesh:" + (message.senderPeerID ?: message.sender)
/** Key for geohash messages, by the sender's per-geohash Nostr pubkey. */
fun nostrSenderKey(pubkeyHex: String): String = "nostr:" + pubkeyHex.lowercase()
}
}

View File

@ -0,0 +1,74 @@
package com.bitchat.android.spam
/**
* Tunables for [SpamFilter]. These are code-level knobs only; the user can just turn the
* filter on or off.
*/
data class SpamFilterConfig(
/** Messages per sender: a burst of 6, then one more every 10 seconds. */
val messageBucket: TokenBucketConfig = TokenBucketConfig(
capacity = 6.0,
refillPerSecond = 1.0 / 10.0
),
/** Characters per sender: 1200 per minute, refilled continuously (20 per second). */
val characterBucket: TokenBucketConfig = TokenBucketConfig(
capacity = 1200.0,
refillPerSecond = 1200.0 / 60.0
),
/** Links per sender: 2 per minute, refilled continuously (one every 30 seconds). */
val linkBucket: TokenBucketConfig = TokenBucketConfig(
capacity = 2.0,
refillPerSecond = 2.0 / 60.0
),
val repetition: RepetitionConfig = RepetitionConfig(),
/**
* How far in the past a message timestamp is trusted. Relay backfill (geohash subscribes to
* the last hour) and mesh sync deliver history in bursts, so buckets run on message time;
* older or future timestamps are clamped into [now - maxBackdateMs, now].
*/
val maxBackdateMs: Long = 60 * 60 * 1000L,
/** Least recently active senders beyond this are forgotten. */
val maxTrackedSenders: Int = 1_000,
/** Verdicts remembered by message ID so multi-transport duplicates are judged once. */
val maxRememberedVerdicts: Int = 4_000
)
data class TokenBucketConfig(
val capacity: Double,
val refillPerSecond: Double
)
data class RepetitionConfig(
/** Messages shorter than this (in characters) are never checked for repeated characters. */
val minRepeatedCharactersLength: Int = 32,
/** Hide when some shift of the message matches itself at this fraction of positions. */
val repeatedCharactersThreshold: Double = 0.85,
/** Messages shorter than this are never compared with the sender's history. */
val minRepeatedMessageLength: Int = 10,
/** Two messages count as the same when their best alignment matches this fraction. */
val repeatedMessageSimilarity: Double = 0.8,
/** How many near-copies of one text a sender may post within [historyWindowMs]. */
val allowedRepeats: Int = 1,
/**
* How many near-copies of a text *other* senders may already have posted within
* [historyWindowMs] before it is hidden. Catches spam spread over many identities.
*/
val crossUserAllowedRepeats: Int = 2,
/** Messages shorter than this are never compared across senders; short replies coincide. */
val crossUserMinLength: Int = 16,
/** Recent messages from all senders kept for the cross-user comparison. */
val crossUserHistorySize: Int = 200,
/** Recent messages per sender kept for comparison. */
val historySize: Int = 10,
val historyWindowMs: Long = 10 * 60 * 1000L,
/** Longer messages are analyzed on their first N characters only, bounding CPU per message. */
val maxAnalyzedLength: Int = 1_000,
/** Messages are compared with others on their first N characters only. */
val maxComparedLength: Int = 280,
/** Correlation peaks combined per comparison; extra peaks absorb inserted or deleted text. */
val maxAlignmentPeaks: Int = 3,
/** Extra peaks only count runs of at least this many matching characters. */
val minPeakRun: Int = 3,
/** Pairs sharing fewer trigrams than this fraction skip the full comparison. */
val minSharedTrigrams: Double = 0.25
)

View File

@ -0,0 +1,115 @@
package com.bitchat.android.spam
import android.content.Context
import android.content.SharedPreferences
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
* Persists the user's on/off choice for [SpamFilter.shared] (on by default), plus the debug
* settings: tuned limits and the bypass that marks spam instead of hiding it.
*/
object SpamFilterPreferences {
private const val PREFS_NAME = "spam_filter_preferences"
private const val KEY_ENABLED = "spam_filter_enabled"
private const val KEY_DEBUG_BYPASS = "spam_filter_debug_bypass"
private const val KEY_MESSAGE_BURST = "spam_filter_message_burst"
private const val KEY_SECONDS_PER_MESSAGE = "spam_filter_seconds_per_message"
private const val KEY_CHARACTERS_PER_MINUTE = "spam_filter_characters_per_minute"
private const val KEY_LINKS_PER_MINUTE = "spam_filter_links_per_minute"
private const val KEY_ALLOWED_REPEATS = "spam_filter_allowed_repeats"
private const val KEY_CROSS_USER_ALLOWED_REPEATS = "spam_filter_cross_user_allowed_repeats"
private const val KEY_CROSS_USER_MIN_LENGTH = "spam_filter_cross_user_min_length"
private const val KEY_REPEAT_SIMILARITY = "spam_filter_repeat_similarity"
private const val KEY_REPEAT_WINDOW_MINUTES = "spam_filter_repeat_window_minutes"
private const val KEY_SELF_MATCH_THRESHOLD = "spam_filter_self_match_threshold"
private const val KEY_SELF_MATCH_MIN_LENGTH = "spam_filter_self_match_min_length"
private const val DEFAULT_ENABLED = true
private val _enabled = MutableStateFlow(DEFAULT_ENABLED)
val enabled: StateFlow<Boolean> = _enabled.asStateFlow()
private val _debugBypass = MutableStateFlow(false)
val debugBypass: StateFlow<Boolean> = _debugBypass.asStateFlow()
private val _tuning = MutableStateFlow(SpamTuning.DEFAULT)
val tuning: StateFlow<SpamTuning> = _tuning.asStateFlow()
@Volatile
private var sharedPrefs: SharedPreferences? = null
fun init(context: Context) {
if (sharedPrefs != null) return
val prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
sharedPrefs = prefs
applyEnabled(prefs.getBoolean(KEY_ENABLED, DEFAULT_ENABLED))
applyDebugBypass(prefs.getBoolean(KEY_DEBUG_BYPASS, false))
applyTuning(readTuning(prefs))
}
fun setEnabled(enabled: Boolean) {
if (_enabled.value == enabled) return
sharedPrefs?.edit()?.putBoolean(KEY_ENABLED, enabled)?.apply()
applyEnabled(enabled)
// Start from a clean slate so budgets don't carry over from before the toggle.
SpamFilter.shared.reset()
}
fun setDebugBypass(enabled: Boolean) {
sharedPrefs?.edit()?.putBoolean(KEY_DEBUG_BYPASS, enabled)?.apply()
applyDebugBypass(enabled)
}
fun setTuning(tuning: SpamTuning) {
if (_tuning.value == tuning) return
sharedPrefs?.edit()?.apply {
putInt(KEY_MESSAGE_BURST, tuning.messageBurst)
putInt(KEY_SECONDS_PER_MESSAGE, tuning.secondsPerMessage)
putInt(KEY_CHARACTERS_PER_MINUTE, tuning.charactersPerMinute)
putInt(KEY_LINKS_PER_MINUTE, tuning.linksPerMinute)
putInt(KEY_ALLOWED_REPEATS, tuning.allowedRepeats)
putInt(KEY_CROSS_USER_ALLOWED_REPEATS, tuning.crossUserAllowedRepeats)
putInt(KEY_CROSS_USER_MIN_LENGTH, tuning.crossUserMinLength)
putInt(KEY_REPEAT_SIMILARITY, tuning.repeatSimilarityPercent)
putInt(KEY_REPEAT_WINDOW_MINUTES, tuning.repeatWindowMinutes)
putInt(KEY_SELF_MATCH_THRESHOLD, tuning.selfMatchPercent)
putInt(KEY_SELF_MATCH_MIN_LENGTH, tuning.selfMatchMinLength)
apply()
}
applyTuning(tuning)
}
private fun readTuning(prefs: SharedPreferences): SpamTuning {
val d = SpamTuning.DEFAULT
return SpamTuning(
messageBurst = prefs.getInt(KEY_MESSAGE_BURST, d.messageBurst),
secondsPerMessage = prefs.getInt(KEY_SECONDS_PER_MESSAGE, d.secondsPerMessage),
charactersPerMinute = prefs.getInt(KEY_CHARACTERS_PER_MINUTE, d.charactersPerMinute),
linksPerMinute = prefs.getInt(KEY_LINKS_PER_MINUTE, d.linksPerMinute),
allowedRepeats = prefs.getInt(KEY_ALLOWED_REPEATS, d.allowedRepeats),
crossUserAllowedRepeats = prefs.getInt(KEY_CROSS_USER_ALLOWED_REPEATS, d.crossUserAllowedRepeats),
crossUserMinLength = prefs.getInt(KEY_CROSS_USER_MIN_LENGTH, d.crossUserMinLength),
repeatSimilarityPercent = prefs.getInt(KEY_REPEAT_SIMILARITY, d.repeatSimilarityPercent),
repeatWindowMinutes = prefs.getInt(KEY_REPEAT_WINDOW_MINUTES, d.repeatWindowMinutes),
selfMatchPercent = prefs.getInt(KEY_SELF_MATCH_THRESHOLD, d.selfMatchPercent),
selfMatchMinLength = prefs.getInt(KEY_SELF_MATCH_MIN_LENGTH, d.selfMatchMinLength)
)
}
private fun applyEnabled(enabled: Boolean) {
_enabled.value = enabled
SpamFilter.shared.enabled = enabled
}
private fun applyDebugBypass(enabled: Boolean) {
_debugBypass.value = enabled
SpamFilter.shared.debugBypass = enabled
}
private fun applyTuning(tuning: SpamTuning) {
_tuning.value = tuning
SpamFilter.shared.config = tuning.toConfig()
}
}

View File

@ -0,0 +1,64 @@
package com.bitchat.android.spam
/**
* The [SpamFilterConfig] knobs exposed in debug settings, in whole units that suit sliders.
* [DEFAULT] matches the code defaults in [SpamFilterConfig].
*/
data class SpamTuning(
val messageBurst: Int,
val secondsPerMessage: Int,
val charactersPerMinute: Int,
val linksPerMinute: Int,
val allowedRepeats: Int,
val crossUserAllowedRepeats: Int,
val crossUserMinLength: Int,
val repeatSimilarityPercent: Int,
val repeatWindowMinutes: Int,
val selfMatchPercent: Int,
val selfMatchMinLength: Int
) {
fun toConfig(): SpamFilterConfig {
val base = SpamFilterConfig()
return base.copy(
messageBucket = TokenBucketConfig(
capacity = messageBurst.toDouble(),
refillPerSecond = 1.0 / secondsPerMessage.coerceAtLeast(1)
),
characterBucket = TokenBucketConfig(
capacity = charactersPerMinute.toDouble(),
refillPerSecond = charactersPerMinute / 60.0
),
linkBucket = TokenBucketConfig(
capacity = linksPerMinute.toDouble(),
refillPerSecond = linksPerMinute / 60.0
),
repetition = base.repetition.copy(
allowedRepeats = allowedRepeats,
crossUserAllowedRepeats = crossUserAllowedRepeats,
crossUserMinLength = crossUserMinLength,
repeatedMessageSimilarity = repeatSimilarityPercent / 100.0,
historyWindowMs = repeatWindowMinutes * 60_000L,
repeatedCharactersThreshold = selfMatchPercent / 100.0,
minRepeatedCharactersLength = selfMatchMinLength
)
)
}
companion object {
val DEFAULT: SpamTuning = SpamFilterConfig().let { config ->
SpamTuning(
messageBurst = config.messageBucket.capacity.toInt(),
secondsPerMessage = Math.round(1.0 / config.messageBucket.refillPerSecond).toInt(),
charactersPerMinute = config.characterBucket.capacity.toInt(),
linksPerMinute = config.linkBucket.capacity.toInt(),
allowedRepeats = config.repetition.allowedRepeats,
crossUserAllowedRepeats = config.repetition.crossUserAllowedRepeats,
crossUserMinLength = config.repetition.crossUserMinLength,
repeatSimilarityPercent = Math.round(config.repetition.repeatedMessageSimilarity * 100).toInt(),
repeatWindowMinutes = (config.repetition.historyWindowMs / 60_000L).toInt(),
selfMatchPercent = Math.round(config.repetition.repeatedCharactersThreshold * 100).toInt(),
selfMatchMinLength = config.repetition.minRepeatedCharactersLength
)
}
}
}

View File

@ -0,0 +1,77 @@
package com.bitchat.android.spam
/**
* A set of token buckets for one sender, evaluated on message time instead of arrival order.
*
* Relay backfill and mesh sync hand over a sender's recent history in one burst, often newest
* first, so a plain arrival-time bucket would hide an ordinary conversation. Instead each
* admitted message stays on a timeline, and a candidate is admitted only if replaying every
* bucket over the timeline with the candidate inserted never runs dry. For live, in-order
* traffic this behaves exactly like a classic token bucket. Rejected messages cost nothing.
*
* Entries older than the backdate window are folded into a checkpoint, and nothing can be
* inserted before the checkpoint, so the replay stays short and exact.
*/
internal class TokenBucketSchedule(private val buckets: List<TokenBucketConfig>) {
private class Entry(val timeMs: Long, val costs: DoubleArray)
private val entries = ArrayList<Entry>()
private var checkpointMs: Long? = null
private val checkpointTokens = DoubleArray(buckets.size) { buckets[it].capacity }
/**
* Admits and records the message when every bucket can pay [costs] at [timeMs].
* Returns -1 on success, otherwise the index of the first bucket that ran dry.
*/
fun tryAdmit(timeMs: Long, costs: DoubleArray): Int {
require(costs.size == buckets.size)
val time = checkpointMs?.let { maxOf(it, timeMs) } ?: timeMs
var insertAt = entries.size
while (insertAt > 0 && entries[insertAt - 1].timeMs > time) insertAt--
val candidate = Entry(time, costs)
val tokens = checkpointTokens.copyOf()
var lastMs = checkpointMs
for (i in 0..entries.size) {
val entry = when {
i < insertAt -> entries[i]
i == insertAt -> candidate
else -> entries[i - 1]
}
refill(tokens, lastMs, entry.timeMs)
lastMs = entry.timeMs
for (b in buckets.indices) {
tokens[b] -= entry.costs[b]
if (tokens[b] < -EPSILON) return b
}
}
entries.add(insertAt, candidate)
return -1
}
/** Folds entries before [cutoffMs] into the checkpoint. */
fun prune(cutoffMs: Long) {
var folded = 0
while (folded < entries.size && entries[folded].timeMs < cutoffMs) {
val entry = entries[folded]
refill(checkpointTokens, checkpointMs, entry.timeMs)
for (b in buckets.indices) checkpointTokens[b] -= entry.costs[b]
checkpointMs = entry.timeMs
folded++
}
if (folded > 0) entries.subList(0, folded).clear()
}
private fun refill(tokens: DoubleArray, fromMs: Long?, toMs: Long) {
if (fromMs == null || toMs <= fromMs) return
val seconds = (toMs - fromMs) / 1000.0
for (b in buckets.indices) {
tokens[b] = minOf(buckets[b].capacity, tokens[b] + seconds * buckets[b].refillPerSecond)
}
}
private companion object {
const val EPSILON = 1e-9
}
}

View File

@ -15,6 +15,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bluetooth
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.FilterAlt
import androidx.compose.material.icons.filled.Speed
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -77,6 +78,7 @@ import com.bitchat.android.net.TorMode
import com.bitchat.android.net.TorPreferenceManager
import com.bitchat.android.nostr.NostrProofOfWork
import com.bitchat.android.nostr.PoWPreferenceManager
import com.bitchat.android.spam.SpamFilterPreferences
import com.bitchat.android.ui.theme.BitchatMotion
import com.bitchat.android.ui.theme.LocalBitchatPalette
import com.bitchat.android.util.ShareableApkVariant
@ -508,6 +510,7 @@ fun AboutSheet(
LaunchedEffect(Unit) { PoWPreferenceManager.init(context) }
val powEnabled by PoWPreferenceManager.powEnabled.collectAsState()
val powDifficulty by PoWPreferenceManager.powDifficulty.collectAsState()
val spamFilterEnabled by SpamFilterPreferences.enabled.collectAsState()
var backgroundEnabled by remember { mutableStateOf(com.bitchat.android.service.MeshServicePreferences.isBackgroundEnabled(true)) }
var liveVoiceEnabled by remember {
mutableStateOf(com.bitchat.android.features.voice.LiveVoicePreferences.isEnabled(context))
@ -582,6 +585,21 @@ fun AboutSheet(
color = colorScheme.outlineVariant
)
// Spam Filter Toggle
SettingsToggleRow(
icon = Icons.Filled.FilterAlt,
title = stringResource(R.string.about_spam_filter_title),
subtitle = stringResource(R.string.about_spam_filter_desc),
checked = spamFilterEnabled,
onCheckedChange = { SpamFilterPreferences.setEnabled(it) }
)
HorizontalDivider(
modifier = Modifier.padding(start = 54.dp),
thickness = 1.dp,
color = colorScheme.outlineVariant
)
// Tor Toggle
SettingsToggleRow(
icon = Icons.Filled.Security,

View File

@ -930,10 +930,8 @@ private fun ChatDialogs(
isPresented = showMeshPeerListSheet,
viewModel = viewModel,
onDismiss = onMeshPeerListDismiss,
onShowVerification = {
onMeshPeerListDismiss()
viewModel.showVerificationSheet(fromSidebar = true)
}
// Stacks on top of the network sheet, so dismissing it lands back here.
onShowVerification = viewModel::showVerificationSheet
)
}

View File

@ -1300,21 +1300,12 @@ class ChatViewModel(
notificationManager.clearMeshMentionNotifications()
}
private var reopenSidebarAfterVerification = false
fun showVerificationSheet(fromSidebar: Boolean = false) {
if (fromSidebar) {
reopenSidebarAfterVerification = true
}
fun showVerificationSheet() {
state.setShowVerificationSheet(true)
}
fun hideVerificationSheet() {
state.setShowVerificationSheet(false)
if (reopenSidebarAfterVerification) {
reopenSidebarAfterVerification = false
state.setShowMeshPeerList(true)
}
}
fun showSecurityVerificationSheet() {
@ -1490,6 +1481,9 @@ class ChatViewModel(
} catch (_: Exception) {
wipeComplete = false
}
// The spam filter keeps recent message text and sender keys for its repeat checks
com.bitchat.android.spam.SpamFilter.shared.reset()
// Clear all cryptographic data
if (!clearAllCryptographicData()) wipeComplete = false

View File

@ -464,8 +464,8 @@ fun MeshPeerListSheet(
Icon(
imageVector = Icons.Outlined.QrCode,
contentDescription = stringResource(R.string.verify_title),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(22.dp)
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(HeaderIconSize)
)
}
}

View File

@ -87,6 +87,8 @@ import com.bitchat.android.mesh.MeshService
import com.bitchat.android.model.BitchatMessage
import com.bitchat.android.model.BitchatMessageType
import com.bitchat.android.model.DeliveryStatus
import com.bitchat.android.spam.SpamFilter
import com.bitchat.android.spam.SpamVerdict
import com.bitchat.android.ui.media.FileMessageItem
import com.bitchat.android.ui.theme.BASE_FONT_SIZE
import com.bitchat.android.ui.theme.BitchatMotion
@ -253,6 +255,8 @@ fun MessagesList(
// Collected once here so individual rows never subscribe to the preference flow; a mode
// switch simply recomposes the list against the new layout.
val bubbles by ChatUiModeManager.modeFlow.collectAsState()
// Spam let through by the debug bypass; empty unless that debug setting is on.
val spamFlags by SpamFilter.shared.flagged.collectAsState()
// A fresh scroll position per conversation. Sharing one state meant a switch inherited the
// previous channel's offset and then had to correct itself, which is what the jump was.
@ -268,8 +272,11 @@ fun MessagesList(
var hasScrolledToInitialPosition by remember(conversationKey) { mutableStateOf(false) }
var followIncomingMessages by remember(conversationKey) { mutableStateOf(true) }
// Smart scroll: auto-scroll to bottom for initial load, then follow unless user scrolls away
LaunchedEffect(messages.size) {
// Smart scroll: auto-scroll to bottom for initial load, then follow unless user scrolls away.
//
// Keyed on the newest message as well as the count: a timeline at its retention cap evicts its
// oldest message for every arrival, so its size stops changing while messages keep coming in.
LaunchedEffect(conversationKey, messages.size, messages.lastOrNull()?.id) {
if (messages.isNotEmpty()) {
val isFirstLoad = !hasScrolledToInitialPosition
if (isFirstLoad || followIncomingMessages) {
@ -384,6 +391,7 @@ fun MessagesList(
mentionPeerIdentities = resolvedMentionPeerIdentities,
showSender = !isGrouped,
bubbles = bubbles.isBubbles,
spamVerdict = spamFlags[message.id],
topSpacing = MessageGrouping.topSpacingFor(
isGrouped = isGrouped,
isFirstInList = originalIndex == 0
@ -418,6 +426,8 @@ fun MessageItem(
mentionPeerIdentities: Map<String, PeerIdentity> = emptyMap(),
showSender: Boolean = true,
bubbles: Boolean = false,
/** Set when the spam filter's debug bypass let this message through; tints the row red. */
spamVerdict: SpamVerdict? = null,
topSpacing: Dp = 0.dp,
onNicknameClick: ((String) -> Unit)? = null,
onMessageLongPress: ((BitchatMessage) -> Unit)? = null,
@ -427,11 +437,19 @@ fun MessageItem(
) {
val colorScheme = MaterialTheme.colorScheme
val timeFormatter = remember { SimpleDateFormat(CHAT_TIMESTAMP_PATTERN, Locale.getDefault()) }
val spamModifier = if (spamVerdict != null) {
Modifier
.background(colorScheme.error.copy(alpha = 0.18f), RoundedCornerShape(8.dp))
.padding(horizontal = 6.dp, vertical = 4.dp)
} else {
Modifier
}
Column(
modifier = modifier
.fillMaxWidth()
.padding(top = topSpacing),
.padding(top = topSpacing)
.then(spamModifier),
verticalArrangement = Arrangement.spacedBy(0.dp)
) {
Box(modifier = Modifier.fillMaxWidth()) {
@ -497,9 +515,29 @@ fun MessageItem(
}
// Link previews removed; links are now highlighted inline and clickable within the message text
if (spamVerdict != null) {
Text(
text = "spam filter would hide this: ${spamVerdict.debugLabel()}",
fontFamily = BitchatFontFamily,
fontSize = 11.sp,
color = colorScheme.error,
modifier = Modifier.padding(top = 2.dp)
)
}
}
}
private fun SpamVerdict.debugLabel(): String = when (this) {
SpamVerdict.ALLOWED -> "allowed"
SpamVerdict.MESSAGE_RATE -> "too many messages"
SpamVerdict.CHARACTER_RATE -> "too many characters"
SpamVerdict.LINK_RATE -> "too many links"
SpamVerdict.REPEATED_MESSAGE -> "repeated message"
SpamVerdict.REPEATED_ACROSS_USERS -> "same text from other users"
SpamVerdict.REPEATED_CHARACTERS -> "repeated characters"
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun MessageTextWithClickableNicknames(

View File

@ -512,7 +512,7 @@ private fun bitmapFromMatrix(matrix: BitMatrix): Bitmap {
return bitmap
}
private class QRCodeAnalyzer(
internal class QRCodeAnalyzer(
private val onCode: (String) -> Unit
) : ImageAnalysis.Analyzer {
private val scanner = BarcodeScanning.getClient(

View File

@ -355,6 +355,9 @@ fun DebugSettingsSheet(
item {
DistributionInfoSection(distributionInfo)
}
item {
SpamFilterDebugSection()
}
// Verbose logging toggle
item {
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {

View File

@ -0,0 +1,139 @@
package com.bitchat.android.ui.debug
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FilterAlt
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bitchat.android.spam.SpamFilter
import com.bitchat.android.spam.SpamFilterPreferences
import com.bitchat.android.spam.SpamTuning
import com.bitchat.android.ui.theme.BitchatFontFamily
import kotlin.math.roundToInt
/** Debug controls for the spam filter: live limit tuning and the mark-instead-of-hide bypass. */
@Composable
fun SpamFilterDebugSection() {
val colorScheme = MaterialTheme.colorScheme
val filterEnabled by SpamFilterPreferences.enabled.collectAsState()
val bypass by SpamFilterPreferences.debugBypass.collectAsState()
val tuning by SpamFilterPreferences.tuning.collectAsState()
val flagged by SpamFilter.shared.flagged.collectAsState()
val update: (SpamTuning) -> Unit = SpamFilterPreferences::setTuning
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(Icons.Filled.FilterAlt, contentDescription = null, tint = colorScheme.error)
Text("Spam filter", fontFamily = BitchatFontFamily, fontSize = 14.sp, fontWeight = FontWeight.Medium)
Spacer(Modifier.weight(1f))
Switch(checked = filterEnabled, onCheckedChange = SpamFilterPreferences::setEnabled)
}
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Column(Modifier.weight(1f)) {
Text("Show spam in red instead of hiding it", fontFamily = BitchatFontFamily, fontSize = 12.sp)
Text(
"Bypass for debugging. ${flagged.size} marked since the last reset or limit change.",
fontFamily = BitchatFontFamily,
fontSize = 11.sp,
color = colorScheme.onSurface.copy(alpha = 0.7f)
)
}
Switch(checked = bypass, onCheckedChange = SpamFilterPreferences::setDebugBypass, enabled = filterEnabled)
}
Text(
"Limits per sender. Changing one starts every sender with a full budget.",
fontFamily = BitchatFontFamily,
fontSize = 11.sp,
color = colorScheme.onSurface.copy(alpha = 0.7f)
)
TuningSlider("Message burst", "${tuning.messageBurst} messages", tuning.messageBurst, 1..30) {
update(tuning.copy(messageBurst = it))
}
TuningSlider("Message refill", "1 every ${tuning.secondsPerMessage} s", tuning.secondsPerMessage, 1..60) {
update(tuning.copy(secondsPerMessage = it))
}
TuningSlider("Characters", "${tuning.charactersPerMinute} per minute", tuning.charactersPerMinute, 100..3000, step = 50) {
update(tuning.copy(charactersPerMinute = it))
}
TuningSlider("Links", "${tuning.linksPerMinute} per minute", tuning.linksPerMinute, 0..10) {
update(tuning.copy(linksPerMinute = it))
}
TuningSlider("Repeats allowed", "${tuning.allowedRepeats} near-copies", tuning.allowedRepeats, 0..10) {
update(tuning.copy(allowedRepeats = it))
}
TuningSlider("Copies from other users allowed", "${tuning.crossUserAllowedRepeats} near-copies", tuning.crossUserAllowedRepeats, 0..10) {
update(tuning.copy(crossUserAllowedRepeats = it))
}
TuningSlider("Compare across users from", "${tuning.crossUserMinLength} chars", tuning.crossUserMinLength, 4..100) {
update(tuning.copy(crossUserMinLength = it))
}
TuningSlider("Near-copy match", "≥ ${tuning.repeatSimilarityPercent}%", tuning.repeatSimilarityPercent, 50..100) {
update(tuning.copy(repeatSimilarityPercent = it))
}
TuningSlider("Repeat window", "${tuning.repeatWindowMinutes} min", tuning.repeatWindowMinutes, 1..60) {
update(tuning.copy(repeatWindowMinutes = it))
}
TuningSlider("Repeated characters", "≥ ${tuning.selfMatchPercent}% self-match", tuning.selfMatchPercent, 50..100) {
update(tuning.copy(selfMatchPercent = it))
}
TuningSlider("Repeated characters from", "${tuning.selfMatchMinLength} chars", tuning.selfMatchMinLength, 8..200, step = 4) {
update(tuning.copy(selfMatchMinLength = it))
}
Row {
Spacer(Modifier.weight(1f))
TextButton(onClick = { update(SpamTuning.DEFAULT) }, enabled = tuning != SpamTuning.DEFAULT) {
Text("Reset to defaults", fontFamily = BitchatFontFamily, fontSize = 12.sp)
}
}
}
}
}
@Composable
private fun TuningSlider(
label: String,
valueText: String,
value: Int,
range: IntRange,
step: Int = 1,
onChange: (Int) -> Unit
) {
val colorScheme = MaterialTheme.colorScheme
Column {
Row {
Text(label, fontFamily = BitchatFontFamily, fontSize = 11.sp, color = colorScheme.onSurface.copy(alpha = 0.7f))
Spacer(Modifier.weight(1f))
Text(valueText, fontFamily = BitchatFontFamily, fontSize = 11.sp)
}
Slider(
value = value.toFloat(),
onValueChange = { raw ->
val snapped = range.first + ((raw - range.first) / step).roundToInt() * step
onChange(snapped.coerceIn(range))
},
valueRange = range.first.toFloat()..range.last.toFloat(),
steps = ((range.last - range.first) / step - 1).coerceAtLeast(0)
)
}
}

View File

@ -111,7 +111,7 @@ fun VoiceNotePlayer(
onSeek = seekTo
)
val durText = if (durationMs > 0) String.format("%02d:%02d", (durationMs / 1000) / 60, (durationMs / 1000) % 60) else "--:--"
Text(text = durText, fontFamily = BitchatFontFamily, fontSize = 12.sp)
Text(text = durText, fontFamily = BitchatFontFamily, fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurface)
}
}

View File

@ -2,6 +2,7 @@ package com.bitchat.android.ui.media
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.snapshots.SnapshotStateList
@ -50,6 +51,10 @@ fun WaveformPreview(
path: String,
sendProgress: Float?,
playbackProgress: Float?,
// Unplayed bars follow the surrounding text colour so they read on any bubble tint or
// surface; the played portion uses the theme accent, which is tuned per light/dark scheme.
baseColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.35f),
playedColor: Color = MaterialTheme.colorScheme.primary,
onLoaded: ((FloatArray) -> Unit)? = null,
onSeek: ((Float) -> Unit)? = null
) {
@ -74,10 +79,10 @@ fun WaveformPreview(
modifier = modifier,
samples = stateSamples,
fillProgress = if (stateSamples.isEmpty()) 0f else progress,
baseColor = Color(0x2200FF7F),
baseColor = baseColor,
fillColor = when {
sendProgress != null -> Color(0xFF1E88E5) // blue while sending
else -> Color(0xFF00C851) // green during playback
else -> playedColor
},
onSeek = onSeek
)

View File

@ -393,4 +393,6 @@
<string name="about_app_language">لغة التطبيق</string>
<string name="about_system_default">إعداد النظام الافتراضي</string>
<string name="about_select_language">اختر اللغة</string>
<string name="about_spam_filter_title">فلتر الرسائل المزعجة</string>
<string name="about_spam_filter_desc">إخفاء الرسائل العامة من الأشخاص الذين يرسلون بسرعة مفرطة أو يكررون رسائلهم أو يغرقون الدردشة بالروابط.</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">অ্যাপের ভাষা</string>
<string name="about_system_default">সিস্টেম ডিফল্ট</string>
<string name="about_select_language">ভাষা নির্বাচন করুন</string>
<string name="about_spam_filter_title">স্প্যাম ফিল্টার</string>
<string name="about_spam_filter_desc">যারা খুব দ্রুত বার্তা পাঠান, একই কথা বারবার বলেন বা অতিরিক্ত লিঙ্ক পাঠান, তাঁদের প্রকাশ্য বার্তা লুকান।</string>
</resources>

View File

@ -394,4 +394,6 @@
<string name="about_app_language">App-Sprache</string>
<string name="about_system_default">Systemstandard</string>
<string name="about_select_language">Sprache auswählen</string>
<string name="about_spam_filter_title">Spamfilter</string>
<string name="about_spam_filter_desc">Öffentliche Nachrichten von Personen ausblenden, die zu schnell schreiben, sich wiederholen oder zu viele Links senden.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">Idioma de la app</string>
<string name="about_system_default">Predeterminado del sistema</string>
<string name="about_select_language">Seleccionar idioma</string>
<string name="about_spam_filter_title">Filtro de spam</string>
<string name="about_spam_filter_desc">Oculta los mensajes públicos de quienes publican demasiado rápido, se repiten o envían demasiados enlaces.</string>
</resources>

View File

@ -619,4 +619,10 @@
<string name="about_app_language">Aplikazioaren hizkuntza</string>
<string name="about_system_default">Sistemaren lehenetsia</string>
<string name="about_select_language">Hautatu hizkuntza</string>
<string name="app_name">bitchat</string>
<string name="file_viewer_name">📄 %1$s</string>
<string name="version_prefix">v%1$s</string>
<string name="image_star">image/*</string>
<string name="about_spam_filter_title">Spam-iragazkia</string>
<string name="about_spam_filter_desc">Ezkutatu azkarregi idazten duten, mezuak errepikatzen dituzten edo esteka gehiegi bidaltzen dituzten pertsonen mezu publikoak.</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">زبان برنامه</string>
<string name="about_system_default">پیش‌فرض سیستم</string>
<string name="about_select_language">انتخاب زبان</string>
<string name="about_spam_filter_title">فیلتر هرزنامه</string>
<string name="about_spam_filter_desc">پیام‌های عمومی افرادی را که خیلی سریع پیام می‌فرستند، خود را تکرار می‌کنند یا پیوندهای زیادی می‌فرستند پنهان کن.</string>
</resources>

View File

@ -391,4 +391,6 @@
<string name="about_app_language">Wika ng app</string>
<string name="about_system_default">Default ng system</string>
<string name="about_select_language">Pumili ng wika</string>
<string name="about_spam_filter_title">Filter ng spam</string>
<string name="about_spam_filter_desc">Itago ang mga pampublikong mensahe mula sa mga taong masyadong mabilis mag-post, paulit-ulit ang sinasabi, o nagpapadala ng napakaraming link.</string>
</resources>

View File

@ -405,4 +405,6 @@
<string name="about_app_language">Langue de l’appli</string>
<string name="about_system_default">Langue du système</string>
<string name="about_select_language">Choisir la langue</string>
<string name="about_spam_filter_title">Filtre antispam</string>
<string name="about_spam_filter_desc">Masquer les messages publics des personnes qui publient trop vite, se répètent ou inondent le chat de liens.</string>
</resources>

View File

@ -392,4 +392,6 @@
<string name="about_app_language">שפת האפליקציה</string>
<string name="about_system_default">ברירת המחדל של המערכת</string>
<string name="about_select_language">בחירת שפה</string>
<string name="about_spam_filter_title">מסנן ספאם</string>
<string name="about_spam_filter_desc">הסתר הודעות ציבוריות מאנשים ששולחים מהר מדי, חוזרים על עצמם או מציפים בקישורים.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">ऐप की भाषा</string>
<string name="about_system_default">सिस्टम डिफ़ॉल्ट</string>
<string name="about_select_language">भाषा चुनें</string>
<string name="about_spam_filter_title">स्पैम फ़िल्टर</string>
<string name="about_spam_filter_desc">बहुत तेज़ी से संदेश भेजने, एक ही बात दोहराने या बहुत अधिक लिंक भेजने वाले लोगों के सार्वजनिक संदेश छिपाएँ।</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">Bahasa aplikasi</string>
<string name="about_system_default">Default sistem</string>
<string name="about_select_language">Pilih bahasa</string>
<string name="about_spam_filter_title">Filter spam</string>
<string name="about_spam_filter_desc">Sembunyikan pesan publik dari orang yang mengirim terlalu cepat, mengulang pesan, atau membanjiri obrolan dengan tautan.</string>
</resources>

View File

@ -420,4 +420,6 @@
<string name="about_app_language">Lingua dell’app</string>
<string name="about_system_default">Predefinita di sistema</string>
<string name="about_select_language">Seleziona lingua</string>
<string name="about_spam_filter_title">Filtro antispam</string>
<string name="about_spam_filter_desc">Nascondi i messaggi pubblici di chi scrive troppo velocemente, si ripete o invia troppi link.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">アプリの言語</string>
<string name="about_system_default">システムのデフォルト</string>
<string name="about_select_language">言語を選択</string>
<string name="about_spam_filter_title">スパムフィルター</string>
<string name="about_spam_filter_desc">短時間に大量のメッセージを送信したり、同じ内容を繰り返したり、リンクを大量に投稿したりする人の公開メッセージを非表示にします。</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">აპის ენა</string>
<string name="about_system_default">სისტემის ნაგულისხმევი</string>
<string name="about_select_language">ენის არჩევა</string>
<string name="about_spam_filter_title">სპამის ფილტრი</string>
<string name="about_spam_filter_desc">დამალე იმ ადამიანების საჯარო შეტყობინებები, რომლებიც ზედმეტად სწრაფად წერენ, იმეორებენ შეტყობინებებს ან აგზავნიან ზედმეტად ბევრ ბმულს.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">앱 언어</string>
<string name="about_system_default">시스템 기본값</string>
<string name="about_select_language">언어 선택</string>
<string name="about_spam_filter_title">스팸 필터</string>
<string name="about_spam_filter_desc">너무 빠르게 메시지를 보내거나, 같은 내용을 반복하거나, 링크를 과도하게 보내는 사람의 공개 메시지를 숨깁니다.</string>
</resources>

View File

@ -400,4 +400,6 @@
<string name="about_app_language">Fitenin’ny app</string>
<string name="about_system_default">Fitenin’ny rafitra</string>
<string name="about_select_language">Safidio ny fiteny</string>
<string name="about_spam_filter_title">Sivana spam</string>
<string name="about_spam_filter_desc">Afeno ny hafatra ampahibemaso avy amin’ireo olona mandefa haingana loatra, mamerimberina hafatra, na mandefa rohy be loatra.</string>
</resources>

View File

@ -433,4 +433,6 @@
<string name="about_app_language">Bahasa aplikasi</string>
<string name="about_system_default">Lalai sistem</string>
<string name="about_select_language">Pilih bahasa</string>
<string name="about_spam_filter_title">Penapis spam</string>
<string name="about_spam_filter_desc">Sembunyikan mesej awam daripada orang yang menghantar terlalu pantas, mengulang mesej atau membanjiri perbualan dengan pautan.</string>
</resources>

View File

@ -391,4 +391,6 @@
<string name="about_app_language">एपको भाषा</string>
<string name="about_system_default">प्रणालीको पूर्वनिर्धारित</string>
<string name="about_select_language">भाषा छान्नुहोस्</string>
<string name="about_spam_filter_title">स्प्याम फिल्टर</string>
<string name="about_spam_filter_desc">धेरै छिटो सन्देश पठाउने, एउटै कुरा दोहोर्‍याउने वा धेरै लिङ्क पठाउने व्यक्तिका सार्वजनिक सन्देशहरू लुकाउनुहोस्।</string>
</resources>

View File

@ -418,4 +418,6 @@
<string name="about_app_language">App-taal</string>
<string name="about_system_default">Systeemstandaard</string>
<string name="about_select_language">Taal selecteren</string>
<string name="about_spam_filter_title">Spamfilter</string>
<string name="about_spam_filter_desc">Verberg openbare berichten van mensen die te snel posten, zichzelf herhalen of te veel links sturen.</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">ایپ دی بولی</string>
<string name="about_system_default">سسٹم دی ڈیفالٹ</string>
<string name="about_select_language">بولی چݨو</string>
<string name="about_spam_filter_title">سپیم فلٹر</string>
<string name="about_spam_filter_desc">اوہناں لوکاں دے عوامی پیغام لُکاؤ جو بہت تیزی نال پیغام بھیجدے، گلّاں دُہراوندے یا بہت سارے لنک بھیجدے نیں۔</string>
</resources>

View File

@ -398,4 +398,6 @@
<string name="about_app_language">Język aplikacji</string>
<string name="about_system_default">Domyślny systemu</string>
<string name="about_select_language">Wybierz język</string>
<string name="about_spam_filter_title">Filtr spamu</string>
<string name="about_spam_filter_desc">Ukrywaj publiczne wiadomości od osób, które piszą zbyt szybko, powtarzają się lub wysyłają zbyt wiele linków.</string>
</resources>

View File

@ -392,4 +392,6 @@
<string name="about_app_language">Idioma do app</string>
<string name="about_system_default">Padrão do sistema</string>
<string name="about_select_language">Selecionar idioma</string>
<string name="about_spam_filter_title">Filtro de spam</string>
<string name="about_spam_filter_desc">Ocultar mensagens públicas de pessoas que enviam mensagens rápido demais, se repetem ou enviam links em excesso.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">Idioma da aplicação</string>
<string name="about_system_default">Predefinição do sistema</string>
<string name="about_select_language">Selecionar idioma</string>
<string name="about_spam_filter_title">Filtro de spam</string>
<string name="about_spam_filter_desc">Ocultar mensagens públicas de pessoas que publicam demasiado depressa, se repetem ou enviam demasiadas ligações.</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">Язык приложения</string>
<string name="about_system_default">Системный язык</string>
<string name="about_select_language">Выбрать язык</string>
<string name="about_spam_filter_title">Спам-фильтр</string>
<string name="about_spam_filter_desc">Скрывать публичные сообщения от тех, кто пишет слишком быстро, повторяется или отправляет слишком много ссылок.</string>
</resources>

View File

@ -378,4 +378,6 @@
<string name="about_app_language">Appspråk</string>
<string name="about_system_default">Systemstandard</string>
<string name="about_select_language">Välj språk</string>
<string name="about_spam_filter_title">Spamfilter</string>
<string name="about_spam_filter_desc">Dölj offentliga meddelanden från personer som skriver för snabbt, upprepar sig eller skickar för många länkar.</string>
</resources>

View File

@ -390,4 +390,6 @@
<string name="about_app_language">பயன்பாட்டு மொழி</string>
<string name="about_system_default">கணினி இயல்புநிலை</string>
<string name="about_select_language">மொழியைத் தேர்ந்தெடுக்கவும்</string>
<string name="about_spam_filter_title">ஸ்பேம் வடிகட்டி</string>
<string name="about_spam_filter_desc">மிக வேகமாகச் செய்திகளை அனுப்புபவர்கள், ஒரே கருத்தைத் திரும்பத் திரும்பக் கூறுபவர்கள் அல்லது அதிக இணைப்புகளை அனுப்புபவர்களின் பொதுச் செய்திகளை மறைக்கவும்.</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">ภาษาของแอป</string>
<string name="about_system_default">ค่าเริ่มต้นของระบบ</string>
<string name="about_select_language">เลือกภาษา</string>
<string name="about_spam_filter_title">ตัวกรองสแปม</string>
<string name="about_spam_filter_desc">ซ่อนข้อความสาธารณะจากผู้ที่ส่งข้อความเร็วเกินไป ส่งข้อความซ้ำ หรือส่งลิงก์จำนวนมากเกินไป</string>
</resources>

View File

@ -378,4 +378,6 @@
<string name="about_app_language">Uygulama dili</string>
<string name="about_system_default">Sistem varsayılanı</string>
<string name="about_select_language">Dil seçin</string>
<string name="about_spam_filter_title">Spam filtresi</string>
<string name="about_spam_filter_desc">Çok hızlı mesaj gönderen, kendini tekrarlayan veya çok fazla bağlantı paylaşan kişilerin herkese açık mesajlarını gizle.</string>
</resources>

View File

@ -400,4 +400,6 @@
<string name="about_app_language">Мова застосунку</string>
<string name="about_system_default">Системна мова</string>
<string name="about_select_language">Вибрати мову</string>
<string name="about_spam_filter_title">Спам-фільтр</string>
<string name="about_spam_filter_desc">Приховувати публічні повідомлення від тих, хто пише надто швидко, повторюється або надсилає забагато посилань.</string>
</resources>

View File

@ -393,4 +393,6 @@
<string name="about_app_language">ایپ کی زبان</string>
<string name="about_system_default">سسٹم ڈیفالٹ</string>
<string name="about_select_language">زبان منتخب کریں</string>
<string name="about_spam_filter_title">اسپام فلٹر</string>
<string name="about_spam_filter_desc">ایسے لوگوں کے عوامی پیغامات چھپائیں جو بہت تیزی سے پیغامات بھیجتے ہیں، اپنی بات دہراتے ہیں یا بہت زیادہ لنکس بھیجتے ہیں۔</string>
</resources>

View File

@ -380,4 +380,6 @@
<string name="about_app_language">Ngôn ngữ ứng dụng</string>
<string name="about_system_default">Mặc định hệ thống</string>
<string name="about_select_language">Chọn ngôn ngữ</string>
<string name="about_spam_filter_title">Bộ lọc thư rác</string>
<string name="about_spam_filter_desc">Ẩn tin nhắn công khai từ những người gửi quá nhanh, lặp lại nội dung hoặc gửi quá nhiều liên kết.</string>
</resources>

View File

@ -388,4 +388,6 @@
<string name="about_app_language">应用语言</string>
<string name="about_system_default">跟随系统</string>
<string name="about_select_language">选择语言</string>
<string name="about_spam_filter_title">垃圾消息过滤器</string>
<string name="about_spam_filter_desc">隐藏发送过快、重复发言或发送过多链接的用户的公开消息。</string>
</resources>

View File

@ -389,4 +389,6 @@
<string name="about_app_language">應用程式語言</string>
<string name="about_system_default">跟隨系統</string>
<string name="about_select_language">選擇語言</string>
<string name="about_spam_filter_title">垃圾訊息篩選器</string>
<string name="about_spam_filter_desc">隱藏傳送過快、重複發言或傳送過多連結的使用者的公開訊息。</string>
</resources>

View File

@ -404,4 +404,6 @@
<string name="about_app_language">应用语言</string>
<string name="about_system_default">跟随系统</string>
<string name="about_select_language">选择语言</string>
<string name="about_spam_filter_title">垃圾消息过滤器</string>
<string name="about_spam_filter_desc">隐藏发送过快、重复发言或发送过多链接的用户的公开消息。</string>
</resources>

View File

@ -209,6 +209,8 @@
<string name="about_pow_off">PoW Off</string>
<string name="about_pow_on">PoW On</string>
<string name="about_pow_tip">Add proof of work to geohash messages for spam deterrence.</string>
<string name="about_spam_filter_title">Spam Filter</string>
<string name="about_spam_filter_desc">Hide public messages from people who post too fast, repeat themselves, or flood links.</string>
<string name="about_pow_difficulty">Difficulty: %1$d bits (~%2$s)</string>
<string name="about_pow_difficulty_attempts">Difficulty %1$d requires ~%2$s hash attempts</string>
<string name="about_pow_desc_none">No proof of work required</string>

View File

@ -0,0 +1,432 @@
package com.bitchat.android.spam
import com.bitchat.android.model.BitchatMessage
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import kotlin.random.Random
class SpamFilterTest {
private var now = 1_000_000_000L
private var nextId = 0
private val random = Random(7)
private val filter = SpamFilter(clock = { now })
private fun send(
content: String = prose(),
sender: String = "alice",
timestampMs: Long = now,
id: String = "m${nextId++}"
) = filter.check(sender, id, content, timestampMs)
/** Unrelated synthetic words, so only the rule under test can fire. */
private fun prose(length: Int = 40): String = buildString {
while (this.length < length) {
if (isNotEmpty()) append(' ')
repeat(random.nextInt(2, 8)) { append('a' + random.nextInt(26)) }
}
}.take(length)
private fun advanceSeconds(seconds: Int) {
now += seconds * 1000L
}
@Test
fun `sixth message in a burst is shown and seventh is hidden`() {
repeat(6) { assertEquals(SpamVerdict.ALLOWED, send()) }
assertEquals(SpamVerdict.MESSAGE_RATE, send())
}
@Test
fun `message budget refills one token every ten seconds`() {
repeat(6) { send() }
advanceSeconds(9)
assertEquals(SpamVerdict.MESSAGE_RATE, send())
advanceSeconds(1)
assertEquals(SpamVerdict.ALLOWED, send())
assertEquals(SpamVerdict.MESSAGE_RATE, send())
}
@Test
fun `hidden messages do not drain the budget of a flooding sender`() {
var shown = 0
repeat(120) {
if (send() == SpamVerdict.ALLOWED) shown++
advanceSeconds(1)
}
// A burst of 6, then one every 10 seconds for the remaining two minutes.
assertEquals(6 + 11, shown)
}
@Test
fun `character budget allows twelve hundred characters per minute`() {
assertEquals(SpamVerdict.ALLOWED, send(prose(600)))
assertEquals(SpamVerdict.ALLOWED, send(prose(600)))
assertEquals(SpamVerdict.CHARACTER_RATE, send(prose(20)))
advanceSeconds(2)
assertEquals(SpamVerdict.ALLOWED, send(prose(20)))
}
@Test
fun `message longer than the character budget is hidden`() {
assertEquals(SpamVerdict.CHARACTER_RATE, send(prose(1201)))
}
@Test
fun `characters are counted as code points`() {
val emoji = "😀"
val sentence = (0 until 1200).joinToString("") { if (it % 3 == 0) emoji else "${'a' + random.nextInt(26)}" }
assertEquals(SpamVerdict.ALLOWED, send(sentence))
}
@Test
fun `third link within a minute is hidden`() {
assertEquals(SpamVerdict.ALLOWED, send("see https://example.org/a for details"))
assertEquals(SpamVerdict.ALLOWED, send("mirror at www.example.net too"))
assertEquals(SpamVerdict.LINK_RATE, send("and example.com/promo as well"))
assertEquals(SpamVerdict.ALLOWED, send("no link in this one at all"))
advanceSeconds(30)
assertEquals(SpamVerdict.ALLOWED, send("ok one more https://example.org/b"))
}
@Test
fun `message with three links is hidden even for a fresh sender`() {
assertEquals(
SpamVerdict.LINK_RATE,
send("https://alpha.example/one and bravo.example plus www.charlie.example/three")
)
}
@Test
fun `link counter ignores email addresses and abbreviations`() {
assertEquals(0, LinkCounter.count("mail me at someone@example.com e.g. today"))
assertEquals(0, LinkCounter.count("pi is 3.14, version v1.2.3"))
assertEquals(3, LinkCounter.count("https://a.example/x, www.b.example and c.example."))
}
@Test
fun `third copy of a message within ten minutes is hidden`() {
val text = "anyone near the north gate tonight?"
assertEquals(SpamVerdict.ALLOWED, send(text))
advanceSeconds(60)
assertEquals(SpamVerdict.ALLOWED, send(text))
advanceSeconds(60)
assertEquals(SpamVerdict.REPEATED_MESSAGE, send(text))
}
@Test
fun `near copies with a changing suffix count as repeats`() {
assertEquals(SpamVerdict.ALLOWED, send("FREE SATS at the link below 101"))
advanceSeconds(20)
assertEquals(SpamVerdict.ALLOWED, send("free sats at the link below 202"))
advanceSeconds(20)
assertEquals(SpamVerdict.REPEATED_MESSAGE, send("free sats at the link below 303"))
}
@Test
fun `repeats spread beyond the history window are allowed`() {
val text = "good morning everyone, coffee anyone?"
send(text)
advanceSeconds(11 * 60)
send(text)
advanceSeconds(11 * 60)
assertEquals(SpamVerdict.ALLOWED, send(text))
}
@Test
fun `short replies may repeat freely`() {
repeat(5) {
assertEquals(SpamVerdict.ALLOWED, send("lol yes"))
advanceSeconds(10)
}
}
@Test
fun `message repeating a character is hidden`() {
assertEquals(SpamVerdict.REPEATED_CHARACTERS, send("A".repeat(40)))
assertEquals(SpamVerdict.REPEATED_CHARACTERS, send("hey " + "!".repeat(60)))
}
@Test
fun `message repeating a phrase is hidden`() {
assertEquals(SpamVerdict.REPEATED_CHARACTERS, send("buy now ".repeat(6)))
}
@Test
fun `ordinary long sentence is not mistaken for repetition`() {
val text = "The market by the old bridge opens at nine, bring cash because the card reader is flaky."
assertTrue(RepetitionDetector.selfSimilarity(RepetitionDetector.normalize(text, 1000)) < 0.3)
assertEquals(SpamVerdict.ALLOWED, send(text))
}
@Test
fun `senders have independent budgets`() {
repeat(6) { send(sender = "alice") }
assertEquals(SpamVerdict.MESSAGE_RATE, send(sender = "alice"))
assertEquals(SpamVerdict.ALLOWED, send(sender = "bob"))
}
@Test
fun `same message ID arriving twice is judged once`() {
repeat(5) { send() }
assertEquals(SpamVerdict.ALLOWED, send(id = "dup"))
assertEquals(SpamVerdict.ALLOWED, send(id = "dup"))
assertEquals(SpamVerdict.MESSAGE_RATE, send())
}
@Test
fun `backfilled history delivered newest first is shown`() {
// Ten messages, one per minute over the last ten minutes, arriving in one burst.
val verdicts = (0 until 10).map { minutesAgo ->
send(timestampMs = now - minutesAgo * 60_000L)
}
assertTrue(verdicts.all { it == SpamVerdict.ALLOWED })
}
@Test
fun `backfilled burst is still rate limited on its own timestamps`() {
val start = now - 5 * 60_000L
val verdicts = (0 until 10).map { send(timestampMs = start + it * 1000L) }
assertEquals(6, verdicts.count { it == SpamVerdict.ALLOWED })
}
@Test
fun `backfill cannot be squeezed in between already shown messages`() {
repeat(6) { send(timestampMs = now - 60_000L) }
// A message claiming to be from just before the burst would overdraw the bucket.
assertEquals(SpamVerdict.MESSAGE_RATE, send(timestampMs = now - 61_000L))
}
@Test
fun `timestamps older than the backdate window are clamped`() {
// Spreading fake timestamps over days must not buy extra budget.
val verdicts = (0 until 10).map { send(timestampMs = now - (10 - it) * 86_400_000L) }
assertEquals(6, verdicts.count { it == SpamVerdict.ALLOWED })
}
@Test
fun `future timestamps are clamped to now`() {
val verdicts = (0 until 10).map { send(timestampMs = now + it * 60_000L) }
assertEquals(6, verdicts.count { it == SpamVerdict.ALLOWED })
}
@Test
fun `disabled filter allows everything`() {
filter.enabled = false
repeat(20) { assertEquals(SpamVerdict.ALLOWED, send("A".repeat(40))) }
}
@Test
fun `limits are configurable`() {
val strict = SpamFilter(
config = SpamFilterConfig(messageBucket = TokenBucketConfig(capacity = 2.0, refillPerSecond = 1.0)),
clock = { now }
)
assertEquals(SpamVerdict.ALLOWED, strict.check("a", "1", prose(), now))
assertEquals(SpamVerdict.ALLOWED, strict.check("a", "2", prose(), now))
assertEquals(SpamVerdict.MESSAGE_RATE, strict.check("a", "3", prose(), now))
advanceSeconds(1)
assertEquals(SpamVerdict.ALLOWED, strict.check("a", "4", prose(), now))
}
@Test
fun `debug bypass shows spam and records why it would be hidden`() {
filter.debugBypass = true
val messages = (1..7).map {
BitchatMessage(id = "b$it", sender = "flooder", content = "hi $it", timestamp = java.util.Date(now))
}
val hidden = messages.map { filter.shouldHide(it, "flooder") }
assertTrue(hidden.none { it })
assertEquals(mapOf("b7" to SpamVerdict.MESSAGE_RATE), filter.flagged.value)
}
@Test
fun `spam is hidden without the debug bypass`() {
val messages = (1..7).map {
BitchatMessage(id = "h$it", sender = "flooder", content = "hi $it", timestamp = java.util.Date(now))
}
assertEquals(listOf(false, false, false, false, false, false, true), messages.map { filter.shouldHide(it, "flooder") })
assertTrue(filter.flagged.value.isEmpty())
}
@Test
fun `changing limits starts every sender with a full budget`() {
repeat(6) { send() }
assertEquals(SpamVerdict.MESSAGE_RATE, send())
filter.config = SpamTuning.DEFAULT.copy(messageBurst = 10).toConfig()
repeat(10) { assertEquals(SpamVerdict.ALLOWED, send()) }
assertEquals(SpamVerdict.MESSAGE_RATE, send())
}
@Test
fun `default tuning matches the code defaults`() {
assertEquals(SpamFilterConfig(), SpamTuning.DEFAULT.toConfig())
assertFalse(SpamTuning.DEFAULT.copy(linksPerMinute = 5).toConfig() == SpamFilterConfig())
}
@Test
fun `same text from many throwaway identities is hidden after three copies`() {
val text = "Free sats for everyone at freesats dot example"
val verdicts = (1..6).map { send(text, sender = "npub$it") }
assertEquals(
listOf(SpamVerdict.ALLOWED, SpamVerdict.ALLOWED, SpamVerdict.ALLOWED) +
List(3) { SpamVerdict.REPEATED_ACROSS_USERS },
verdicts
)
}
@Test
fun `near copies with small changes from different identities are hidden`() {
// Each variant is one small edit (a suffix, an insertion, a substitution) off the first.
val variants = listOf(
"Free sats for everyone, claim yours now",
"free sats for everyone, claim yours now 1",
"Free sats for everyone!! claim yours now",
"free sats 4 everyone, claim yours now",
"free sats for everyone, claim yours now 55"
)
val verdicts = variants.mapIndexed { i, text -> send(text, sender = "npub$i") }
assertEquals(List(4) { SpamVerdict.ALLOWED } + SpamVerdict.REPEATED_ACROSS_USERS, verdicts)
}
@Test
fun `short common replies from many users are not compared`() {
repeat(8) { assertEquals(SpamVerdict.ALLOWED, send("good morning!", sender = "user$it")) }
}
@Test
fun `different messages on the same topic from many users are allowed`() {
val messages = listOf(
"is the bakery on the corner still open?",
"the bakery closes at eight I think",
"bakery was still open when I walked by",
"anyone know if the bakery has bread left?",
"the corner bakery is open until eight"
)
messages.forEachIndexed { i, text -> assertEquals(SpamVerdict.ALLOWED, send(text, sender = "user$i")) }
}
@Test
fun `copies from other users outside the window do not count`() {
val text = "anyone up for chess at the tables later?"
repeat(3) {
send(text, sender = "user$it")
advanceSeconds(4 * 60)
}
// Only the copy from 4 minutes ago and the one from 8 minutes ago are in the window.
assertEquals(SpamVerdict.ALLOWED, send(text, sender = "late"))
}
@Test
fun `a sender's own copies do not count toward the cross-user limit`() {
val text = "anyone near the north gate tonight?"
send(text, sender = "alice")
advanceSeconds(60)
send(text, sender = "alice")
assertEquals(SpamVerdict.ALLOWED, send(text, sender = "bob"))
assertEquals(SpamVerdict.REPEATED_ACROSS_USERS, send(text, sender = "carol"))
}
@Test
fun `best alignment reports where the texts line up`() {
val a = RepetitionDetector.normalize("xx hello world", 1000)
val b = RepetitionDetector.normalize("hello world", 1000)
val alignment = RepetitionDetector.align(a, b)
assertEquals(3, alignment.peaks.first().shift)
assertEquals(11, alignment.matches)
}
@Test
fun `an inserted character does not break the match`() {
val a = RepetitionDetector.normalize("free sats for everyone, claim yours now", 1000)
val b = RepetitionDetector.normalize("free sats for everyone!!, claim yours now", 1000)
assertTrue(RepetitionDetector.align(a, b, maxPeaks = 1).score < 0.8)
assertTrue(RepetitionDetector.align(a, b).score > 0.9)
}
@Test
fun `unrelated sentences stay far below the near-copy threshold`() {
val a = RepetitionDetector.normalize("the bakery closes at eight I think", 1000)
val b = RepetitionDetector.normalize("the corner bakery is open until eight", 1000)
assertTrue(RepetitionDetector.align(a, b).score < 0.6)
}
private val disguiseBase = "Free sats for everyone, claim yours now"
/** The same text a reader sees, written so that a plain comparison would miss it. */
private val disguises = mapOf(
"zero-width spaces" to disguiseBase.map { "$it​" }.joinToString(""),
"mixed invisible characters" to disguiseBase.mapIndexed { i, c ->
"$c" + listOf("‌", "‍", "⁠", "­", "‎")[i % 5]
}.joinToString(""),
"an accent on every letter" to disguiseBase.map { if (it.isLetter()) "$it́" else "$it" }.joinToString(""),
"math bold letters" to disguiseBase.map { c ->
when (c) {
in 'a'..'z' -> String(Character.toChars(0x1D41A + (c - 'a')))
in 'A'..'Z' -> String(Character.toChars(0x1D400 + (c - 'A')))
else -> "$c"
}
}.joinToString(""),
"fullwidth letters" to disguiseBase.map { c -> if (c in '!'..'~') (c.code + 0xFEE0).toChar() else c }.joinToString(""),
"non-breaking spaces" to disguiseBase.replace(' ', ' ')
)
@Test
fun `normalization folds disguises to the text a reader sees`() {
val plain = RepetitionDetector.normalize(disguiseBase, 1000).toList()
disguises.forEach { (label, text) ->
assertEquals(label, plain, RepetitionDetector.normalize(text, 1000).toList())
}
}
@Test
fun `disguised copies from other users are still caught`() {
disguises.forEach { (label, text) ->
val fresh = SpamFilter(clock = { now })
repeat(3) { fresh.check("npub$it", "$label-$it", disguiseBase, now) }
assertEquals(label, SpamVerdict.REPEATED_ACROSS_USERS, fresh.check("npub9", "$label-x", text, now))
}
}
@Test
fun `disguised copies from the same user are still caught`() {
disguises.forEach { (label, text) ->
val fresh = SpamFilter(clock = { now })
fresh.check("alice", "$label-1", disguiseBase, now)
fresh.check("alice", "$label-2", disguiseBase, now)
assertEquals(label, SpamVerdict.REPEATED_MESSAGE, fresh.check("alice", "$label-3", text, now))
}
}
@Test
fun `invisible characters cannot break up a repeated character run`() {
assertEquals(SpamVerdict.REPEATED_CHARACTERS, send("A​".repeat(40)))
}
@Test
fun `ordinary text with accents and other scripts is still allowed`() {
listOf(
"Tiếng Việt có dấu, chúng tôi đang ở quán cà phê gần chợ",
"क्या कोई पास में चाय की दुकान जानता है? मैं स्टेशन के पास हूँ",
"Café au lait, crème brûlée, s'il vous plaît",
"家族と一緒に駅の近くのカフェにいます、誰か来ますか"
).forEachIndexed { i, text -> assertEquals(text, SpamVerdict.ALLOWED, send(text, sender = "user$i")) }
}
@Test
fun `different sentences in another script are not mistaken for copies`() {
assertEquals(SpamVerdict.ALLOWED, send("क्या कोई पास में चाय की दुकान जानता है?", sender = "a"))
assertEquals(SpamVerdict.ALLOWED, send("मैं स्टेशन के पास हूँ, बारिश हो रही है", sender = "b"))
assertEquals(SpamVerdict.ALLOWED, send("आज बाज़ार कितने बजे बंद होगा?", sender = "c"))
assertEquals(SpamVerdict.ALLOWED, send("क्या किसी के पास छाता है जो मैं ले सकूँ?", sender = "d"))
}
}

View File

@ -0,0 +1,118 @@
package com.bitchat.android.ui
import android.app.Application
import android.content.ComponentName
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasScrollToIndexAction
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToIndex
import androidx.compose.ui.unit.dp
import com.bitchat.android.mesh.MeshService
import com.bitchat.android.model.BitchatMessage
import java.util.Date
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExternalResource
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
/**
* The message list follows new arrivals while the reader is at the latest message.
*
* Public, channel and geohash timelines are capped, so once one is full every arrival evicts the
* oldest message and the list size stays constant. Following must not depend on the size changing.
*/
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [35], application = Application::class)
class MessagesListFollowTest {
private val compose = createComposeRule()
// Unit tests run without merged Android resources, so Robolectric does not know the host
// activity the compose rule launches. Register it before the rule starts.
@get:Rule
val rules: RuleChain = RuleChain
.outerRule(object : ExternalResource() {
override fun before() {
val app = RuntimeEnvironment.getApplication()
shadowOf(app.packageManager)
.addActivityIfNotPresent(ComponentName(app, ComponentActivity::class.java))
}
})
.around(compose)
private val meshService = mock<MeshService> { on { myPeerID } doReturn "0000000000000000" }
private var messages by mutableStateOf(List(TimelineSize) { msg(it) })
private var scrolledUp: Boolean? = null
private fun setContent() {
compose.setContent {
MessagesList(
messages = messages,
currentUserNickname = "me",
meshService = meshService,
modifier = Modifier.size(360.dp, 640.dp),
conversationKey = "geo:test",
onScrolledUpChanged = { scrolledUp = it }
)
}
compose.waitForIdle()
}
/** Mirrors the timeline retention cap: the oldest message makes room for the new one. */
private fun arriveAtCapacity(count: Int) {
repeat(count) {
messages = messages.drop(1) + msg(TimelineSize + it)
compose.waitForIdle()
}
}
@Test
fun `a full timeline keeps following arrivals that evict the oldest message`() {
setContent()
arriveAtCapacity(10)
assertFalse("following stopped once the size stopped changing", scrolledUp ?: false)
compose.onNodeWithText(body(TimelineSize + 9), substring = true).assertIsDisplayed()
}
@Test
fun `a full timeline does not pull a reader back down after they scroll away`() {
setContent()
compose.onNode(hasScrollToIndexAction()).performScrollToIndex(TimelineSize - 1)
compose.waitForIdle()
arriveAtCapacity(3)
assertTrue("reader was yanked back to the latest message", scrolledUp ?: false)
}
private fun msg(index: Int) = BitchatMessage(
id = "m$index",
sender = "alice",
content = body(index),
timestamp = Date(index * 1_000L),
)
private fun body(index: Int) = "synthetic message $index"
private companion object {
const val TimelineSize = 40
}
}

View File

@ -10,6 +10,10 @@ import com.bitchat.android.noise.NoiseSessionError
import com.bitchat.android.protocol.BitchatPacket
import com.bitchat.android.protocol.MessageType
import kotlinx.coroutines.runBlocking
import java.util.Collections
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@ -99,6 +103,75 @@ class SecurityManagerTest {
}
}
@Test
fun `cleanup serializes timestamp traversal with concurrent packet tracking`() {
assertCleanupSerializesWrites("messageTimestamps")
}
@Test
fun `cleanup serializes timestamp traversal with concurrent handshake tracking`() {
assertCleanupSerializesWrites("keyExchangeTimestamps")
}
private fun assertCleanupSerializesWrites(fieldName: String) {
val allowWrite = CountDownLatch(1)
val writeFinished = CountDownLatch(1)
lateinit var timestamps: MutableMap<String, Long>
val backing = object : LinkedHashMap<String, Long>() {
override val entries: MutableSet<MutableMap.MutableEntry<String, Long>>
get() {
val original = super.entries
return object : AbstractMutableSet<MutableMap.MutableEntry<String, Long>>() {
override val size get() = original.size
override fun add(element: MutableMap.MutableEntry<String, Long>) =
error("Not used")
override fun iterator(): MutableIterator<MutableMap.MutableEntry<String, Long>> {
val iterator = original.iterator()
var first = true
return object : MutableIterator<MutableMap.MutableEntry<String, Long>> {
override fun hasNext() = iterator.hasNext()
override fun remove() = iterator.remove()
override fun next(): MutableMap.MutableEntry<String, Long> {
if (first) {
first = false
allowWrite.countDown()
// A correct synchronized traversal blocks the writer. An
// unguarded traversal deterministically exposes the CME.
if (!Thread.holdsLock(timestamps)) {
assertTrue(writeFinished.await(5, TimeUnit.SECONDS))
}
}
return iterator.next()
}
}
}
}
}
}
timestamps = Collections.synchronizedMap(backing)
timestamps["expired-a"] = 1L
timestamps["expired-b"] = 2L
SecurityManager::class.java.getDeclaredField(fieldName).apply {
isAccessible = true
set(securityManager, timestamps)
}
val now = 10_000_000L
val executor = Executors.newSingleThreadExecutor()
val writer = executor.submit {
check(allowWrite.await(5, TimeUnit.SECONDS))
timestamps["fresh"] = now
writeFinished.countDown()
}
try {
securityManager.cleanupOldData(now)
writer.get(5, TimeUnit.SECONDS)
assertTrue(timestamps.keys == setOf("fresh"))
} finally {
allowWrite.countDown()
executor.shutdownNow()
}
}
@Test
fun `validatePacket - rejects packet with missing signature`() {
val packet = BitchatPacket(

View File

@ -0,0 +1,79 @@
package com.bitchat.android.nostr
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import com.bitchat.android.spam.SpamFilter
import com.bitchat.android.ui.ChatState
import com.bitchat.android.ui.DataManager
import com.bitchat.android.ui.MessageManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class GeohashMessageHandlerSpamTest {
private val application: Application = ApplicationProvider.getApplicationContext()
@OptIn(ExperimentalCoroutinesApi::class)
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val geohash = "zzzzzz"
private lateinit var chatState: ChatState
private lateinit var handler: GeohashMessageHandler
@Before
fun setup() {
com.bitchat.android.services.AppStateStore.clear()
SpamFilter.shared.reset()
chatState = ChatState(scope = testScope)
val dataManager = DataManager(context = application)
handler = GeohashMessageHandler(
application = application,
repo = GeohashRepository(application, chatState, dataManager, testScope),
scope = testScope,
dataManager = dataManager,
addChannelMessage = MessageManager(state = chatState)::addChannelMessage,
signatureVerificationDispatcher = testDispatcher,
spamFilterDispatcher = testDispatcher
)
}
@After
fun tearDown() {
SpamFilter.shared.reset()
com.bitchat.android.services.AppStateStore.clear()
}
private fun event(identity: NostrIdentity, content: String) = NostrEvent(
pubkey = identity.publicKeyHex,
createdAt = (System.currentTimeMillis() / 1000).toInt(),
kind = NostrKind.EPHEMERAL_EVENT,
tags = listOf(listOf("g", geohash)),
content = content
).sign(identity.privateKeyHex)
@Test
fun `flood from one pubkey is cut off after the burst`() {
val flooder = NostrIdentity.generate()
(1..8).forEach { handler.onEvent(event(flooder, "hi $it"), geohash) }
assertEquals(6, chatState.getChannelMessagesValue()["geo:$geohash"]?.size)
}
@Test
fun `same text from many pubkeys is cut off after three copies`() {
repeat(5) {
handler.onEvent(event(NostrIdentity.generate(), "Free sats for everyone, claim yours now"), geohash)
}
assertEquals(3, chatState.getChannelMessagesValue()["geo:$geohash"]?.size)
}
}

View File

@ -3,6 +3,7 @@ package com.bitchat.android.nostr
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import com.bitchat.android.services.AppStateStore
import com.bitchat.android.spam.SpamFilter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@ -23,12 +24,17 @@ class NostrBackgroundEventProcessorTest {
@Before
fun setUp() {
AppStateStore.clear()
// This test floods one sender to measure throughput; that is exactly what the spam
// filter hides, so it is covered by SpamFilterTest instead.
SpamFilter.shared.enabled = false
scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
}
@After
fun tearDown() {
scope.cancel()
SpamFilter.shared.enabled = true
SpamFilter.shared.reset()
AppStateStore.clear()
}

View File

@ -0,0 +1,55 @@
package com.bitchat.android.nostr
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import okhttp3.WebSocket
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class NostrRelaySnapshotTest {
@Test
fun `subscription tolerates last relay disconnecting between size and iteration`() {
val manager = NostrRelayManager::class.java.getDeclaredConstructor().apply {
isAccessible = true
}.newInstance()
val errors = mutableListOf<Throwable>()
val scope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob() +
CoroutineExceptionHandler { _, error -> errors.add(error) })
val connections = object : ConcurrentHashMap<String, WebSocket>() {
override val size: Int
get() {
val previous = super.size
// The real concurrent key view delegates size() to its backing map.
// Disconnect exactly after observing the last connection.
clear()
return previous
}
}
NostrRelayManager::class.java.getDeclaredField("scope").apply {
isAccessible = true
(get(manager) as CoroutineScope).cancel()
set(manager, scope)
}
NostrRelayManager::class.java.getDeclaredField("connections").apply {
isAccessible = true
set(manager, connections)
}
connections["wss://relay.example"] = mock()
try {
manager.subscribe(NostrFilter(kinds = listOf(NostrKind.TEXT_NOTE)),
id = "synthetic-subscription", handler = {})
assertTrue("Subscription coroutine failed: $errors", errors.isEmpty())
} finally {
scope.cancel()
connections.clear()
}
}
}

View File

@ -276,6 +276,65 @@ class IncomingMessageAdmissionTest {
assertTrue(state.getPrivateChatsValue().isEmpty())
}
@Test
fun `public flood from one peer is hidden while private messages still arrive`() {
val peer = "flood-${UUID.randomUUID()}"
val admitted = (1..7).map { index ->
IncomingMessageAdmission.admitToAppState(
BitchatMessage(
id = "$peer-$index",
sender = "flooder",
content = "hi $index",
timestamp = Date(),
senderPeerID = peer
)
)
}
assertEquals(listOf(true, true, true, true, true, true, false), admitted)
assertFalse(AppStateStore.publicMessages.value.any { it.id == "$peer-7" })
assertTrue(
IncomingMessageAdmission.admitToAppState(
privateMessage(id = "$peer-dm").copy(senderPeerID = peer)
)
)
}
@Test
fun `peer calling itself system is still spam filtered`() {
val peer = "system-nick-${UUID.randomUUID()}"
val admitted = (1..7).map { index ->
IncomingMessageAdmission.admitToAppState(
BitchatMessage(
id = "$peer-$index",
sender = "system",
content = "hi $index",
timestamp = Date(),
senderPeerID = peer
)
)
}
assertFalse(admitted.last())
}
@Test
fun `local system notices are never spam filtered`() {
val run = UUID.randomUUID().toString()
val admitted = (1..10).map { index ->
IncomingMessageAdmission.admitToAppState(
BitchatMessage(
id = "notice-$run-$index",
sender = "system",
content = "peer$index favorited you — favorite back to continue DMs later.",
timestamp = Date()
)
)
}
assertTrue(admitted.all { it })
}
private fun privateMessage(id: String) = BitchatMessage(
id = id,
sender = "alice",

View File

@ -298,6 +298,7 @@ python3 tools/release_gate/mesh_lab.py scenario all \
| `dm` | Noise handshake both ways, encrypted DM round trips with content match |
| `favorite_verification` | favorite signal, orange-outline/filled mutual state, and peer fingerprint verification |
| `broadcast` | public mesh message A→B |
| `broadcast_spam` | spam filter on B: two near-copies from A shown, the third hidden, A's next ordinary message shown (B needs the filter on, default limits, debug bypass off) |
| `ptt_dm` | Noise-encrypted 440 Hz PTT in both directions; asserts real-time capture, zero sequence gaps, decoded PCM duration/energy/continuity, and finalized-note absorption |
| `ptt_broadcast` | signed public 440 Hz PTT with the same bidirectional packet and decoded-audio quality assertions |
| `file` | 1 KB broadcast file, receiver SHA-256 matches fixture |

View File

@ -39,6 +39,11 @@
<sha256 value="5b2d4387f384e5913a4aec979a20ece13bb5ce992bc90a962dea1feea7fa89e9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.annotation" name="annotation" version="1.2.0">
<artifact name="annotation-1.2.0.module">
<sha256 value="2efcab81ef91b211bacd206eaacd995a51f633a2e96b57a8fc00144c5f9c56b3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.annotation" name="annotation" version="1.3.0">
<artifact name="annotation-1.3.0.module">
<sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle"/>
@ -64,6 +69,11 @@
<sha256 value="f204b05b728a97561718bc716242e47c629c0085a80ee74fca53d4d638bcbe3f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.annotation" name="annotation-experimental" version="1.1.0">
<artifact name="annotation-experimental-1.1.0.module">
<sha256 value="0361d1526a4d7501255e19779e09e93cdbd07fee0e2f5c50b7a137432d510119" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.annotation" name="annotation-experimental" version="1.4.1">
<artifact name="annotation-experimental-1.4.1.aar">
<sha256 value="6bd4c7c7476f8260cd3bdbb81183583e93fc9f790c27dea7dc314181cbf87aa0" origin="Generated by Gradle"/>
@ -1760,6 +1770,11 @@
<sha256 value="b97dd112cd684ad3686f5ccf581b92b106ca76565cf02ccfece060af3bc50743" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="monitor" version="1.6.0">
<artifact name="monitor-1.6.0.pom">
<sha256 value="e6e32c3c34a33e81a64fa79ddd06cd47484594e40aea400bbb370ba102bea238" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="monitor" version="1.6.1">
<artifact name="monitor-1.6.1.aar">
<sha256 value="2985ce8556989baf7c84342e7f687713c037a39a922e614d1a3ddf1ca3777079" origin="Generated by Gradle"/>
@ -4784,6 +4799,11 @@
<sha256 value="f65860bce58a2bfadf05fdc516d38333421f0e387fef87e24449c0394c80d254" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.6.4">
<artifact name="kotlinx-coroutines-bom-1.6.4.pom">
<sha256 value="ab2614855fba66aa8a42514dbe3d5a884315ffe1ed63f5932e710a8006245ce1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.7.3">
<artifact name="kotlinx-coroutines-bom-1.7.3.pom">
<sha256 value="4e5d1900e6379ef3f5970d04a8f30529adc82f859e8cc107c21ce8149ef666c4" origin="Generated by Gradle"/>
@ -4794,6 +4814,11 @@
<sha256 value="1239e9dbe1397cd5971342956b2511bc3ace7b641842e4372a088dcfa8b9ad55" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.8.1">
<artifact name="kotlinx-coroutines-bom-1.8.1.pom">
<sha256 value="563e4aa29fa8fe09a6e1746d0a5b51308f7c7dedc468dc5a0ca810a485877030" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.9.0">
<artifact name="kotlinx-coroutines-bom-1.9.0.pom">
<sha256 value="bea5511e9001f2c593ab5080df131b219ab5e2085cc5979ce583eacd4946fd78" origin="Generated by Gradle"/>

View File

@ -532,6 +532,30 @@ def scenario_broadcast(a: Device, b: Device) -> dict:
return {"send": send_result, "recv": recv_result}
def scenario_broadcast_spam(a: Device, b: Device) -> dict:
"""Spam filter on B: two near-copies from A are shown, the third is hidden, and A's next
ordinary message is still shown. Needs B's spam filter on, default limits, bypass off."""
id_a = whoami(a)["peer_id"]
# A fresh 32-character body per run keeps earlier runs out of B's ten-minute repeat window.
token = uuid.uuid4().hex[:16]
copies = [f"{token}{token} copy {mark}" for mark in "ABC"]
control = f"control {token} with different words, still delivered"
expected = [True, True, False, True]
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
recvs = [pool.submit(b.cmd, "msg_recv", 45_000, contains=text) for text in [*copies, control]]
time.sleep(2)
for text in [*copies, control]:
a.cmd_ok("broadcast_msg", 30_000, content=text)
results = [recv.result() for recv in recvs]
delivered = [result.get("status") == "ok" for result in results]
if delivered != expected:
raise MeshLabError(f"expected delivery {expected} for copies A-C and control, got {delivered}")
for result in results:
if result.get("status") == "ok":
assert result["from"] == id_a, result
return {"delivered": delivered}
def _ptt_one_way(
sender: Device,
receiver: Device,
@ -869,6 +893,7 @@ SCENARIOS = {
"dm": scenario_dm,
"favorite_verification": scenario_favorite_verification,
"broadcast": scenario_broadcast,
"broadcast_spam": scenario_broadcast_spam,
"ptt_dm": scenario_ptt_dm,
"ptt_broadcast": scenario_ptt_broadcast,
# Broadcast transfers are receiver-capped at 256 fragments (~120 KB); only
@ -899,6 +924,7 @@ WATCH_SCENARIOS = [
"dm",
"favorite_verification",
"broadcast",
"broadcast_spam",
"ptt_dm",
"ptt_broadcast",
"raw",

View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Reproduce/verify the real scanner entry point after production R8 shrinking.
# Usage: bash tools/release_gate/scanner_release_regression.sh <disposable-emulator>
# Requires JDK 21, the pinned Android SDK, and adb on PATH. Never use publishable
# signing keys here: -PreleaseUiTests=true uses the debug key for test APKs only.
set -euo pipefail
if [[ $# != 1 || "$1" != emulator-* ]]; then
echo "Usage: $0 <disposable-emulator>" >&2
exit 2
fi
scanner_emulator="$1"
if [[ "$(adb -s "$scanner_emulator" shell getprop ro.kernel.qemu | tr -d '\r')" != 1 ]]; then
echo "Refusing to install: target is not an emulator." >&2
exit 2
fi
scanner_abi="$(adb -s "$scanner_emulator" shell getprop ro.product.cpu.abi | tr -d '\r')"
case "$scanner_abi" in
arm64-v8a|x86_64|armeabi-v7a|x86) ;;
*) echo "Unsupported emulator ABI." >&2; exit 2 ;;
esac
cd "$(git rev-parse --show-toplevel)"
# This must be a disposable emulator. Keep the fixture offline and location off;
# no real camera, identities, messages, or transport startup are involved.
adb -s "$scanner_emulator" shell svc wifi disable
adb -s "$scanner_emulator" shell svc data disable
adb -s "$scanner_emulator" shell settings put secure location_mode 0
./gradlew -PreleaseUiTests=true :app:assembleRelease :app:assembleReleaseAndroidTest
adb -s "$scanner_emulator" install -r "app/build/outputs/apk/release/app-${scanner_abi}-release.apk"
adb -s "$scanner_emulator" install -r app/build/outputs/apk/androidTest/release/app-release-androidTest.apk
scanner_evidence="$(mktemp -d "${TMPDIR:-/tmp}/scanner-release.XXXXXX")"
chmod 700 "$scanner_evidence"
adb -s "$scanner_emulator" shell am instrument -w -r \
com.bitchat.droid.test/com.bitchat.android.ui.ReleaseScannerTestRunner \
> "$scanner_evidence/result.txt"
cat "$scanner_evidence/result.txt"
# adb itself exits successfully even when instrumentation tests fail.
grep -q '^INSTRUMENTATION_RESULT: stream=Tests run: 2, failures: 0' "$scanner_evidence/result.txt"
grep -q '^INSTRUMENTATION_CODE: -1' "$scanner_evidence/result.txt"
echo "Release scanner creation, synthetic QR decoding, and frame cleanup passed."

View File

@ -105,6 +105,7 @@ val sharedSourceIncludes = listOf(
"com/bitchat/android/services/VerificationService.kt",
"com/bitchat/android/services/meshgraph/**",
"com/bitchat/android/service/TransportBridgeService.kt",
"com/bitchat/android/spam/**",
"com/bitchat/android/nostr/Bech32.kt",
"com/bitchat/android/nostr/GeohashAliasRegistry.kt",
"com/bitchat/android/features/file/FileUtils.kt",
@ -119,6 +120,8 @@ val sharedSourceIncludes = listOf(
)
val sharedSourceExcludes = listOf(
"com/bitchat/android/model/FileSharingManager.kt",
// The watch has no spam filter toggle, so it keeps the filter's default (on).
"com/bitchat/android/spam/SpamFilterPreferences.kt",
// Legacy phone monolith and Wi-Fi Aware multiplexer; the watch composes its own service
// (MeshCore-style) in M2 instead of reusing these.
"com/bitchat/android/mesh/BluetoothMeshService.kt",

View File

@ -24,6 +24,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
@ -39,6 +40,8 @@ import androidx.wear.compose.material3.OutlinedButton
import com.bitchat.watch.mesh.WearMeshService
import com.bitchat.watch.notification.WearNotificationCoordinator
import com.bitchat.watch.service.WearMeshForegroundService
import com.bitchat.watch.ui.ChatHomePager
import com.bitchat.watch.ui.ChatMenuScreen
import com.bitchat.watch.ui.ChatScreen
import com.bitchat.watch.ui.DmScreen
import com.bitchat.watch.ui.NicknameSetupScreen
@ -110,6 +113,7 @@ class MainActivity : ComponentActivity() {
)
else -> WearNavHost(
launchRequest = pendingLaunchRequest,
onQuit = ::quit,
onLaunchRequestHandled = { requestID ->
if (pendingLaunchRequest?.id == requestID) {
pendingLaunchRequest = null
@ -177,6 +181,15 @@ class MainActivity : ComponentActivity() {
startForegroundService(Intent(this, WearMeshForegroundService::class.java))
}
/**
* Quit from the in-app menu: stopping the foreground service tears the mesh down (see
* [WearMeshForegroundService.onDestroy]); the next launch starts it again.
*/
private fun quit() {
stopService(Intent(this, WearMeshForegroundService::class.java))
finishAndRemoveTask()
}
private fun consumePrivateMessagePeer(intent: Intent?): String? {
val peerID = privateMessagePeerFromIntent(intent)
intent?.removeExtra(WearNotificationCoordinator.EXTRA_OPEN_DM)
@ -370,6 +383,7 @@ internal class WearNavigationState(
@Composable
internal fun WearNavHost(
launchRequest: WearLaunchRequest?,
onQuit: () -> Unit,
onLaunchRequestHandled: (Long) -> Unit
) {
val navigation = rememberSaveable(saver = WearNavigationState.Saver) {
@ -397,10 +411,20 @@ internal fun WearNavHost(
label = "screenTransition"
) { current ->
when (current) {
is WearScreen.Chat -> ChatScreen(
onOpenPeople = { navigation.navigate(WearScreen.People) },
onOpenTextInput = { navigation.navigate(WearScreen.TextInput(null)) }
)
is WearScreen.Chat -> {
var chatOwnsGesture by remember { mutableStateOf(false) }
ChatHomePager(
swipeEnabled = !chatOwnsGesture,
menu = { ChatMenuScreen(onQuit = onQuit) },
chat = {
ChatScreen(
onOpenPeople = { navigation.navigate(WearScreen.People) },
onOpenTextInput = { navigation.navigate(WearScreen.TextInput(null)) },
onHorizontalGestureActive = { chatOwnsGesture = it }
)
}
)
}
is WearScreen.People -> PeopleScreen(
onOpenDm = { navigation.navigate(WearScreen.Dm(it)) },
onEditNickname = { navigation.navigate(WearScreen.Nickname) }

View File

@ -13,6 +13,7 @@ import com.bitchat.android.mesh.MeshTransport
import com.bitchat.android.model.RoutedPacket
import com.bitchat.android.protocol.BitchatPacket
import com.bitchat.android.services.AppStateStore
import com.bitchat.android.spam.SpamFilter
import com.bitchat.android.sync.GossipSyncManager
import com.bitchat.android.util.AppConstants
import kotlinx.coroutines.CoroutineScope
@ -238,6 +239,8 @@ class WearMeshService private constructor(private val context: Context) {
try { onPrivateMessage?.invoke(message) } catch (_: Exception) { }
true
}
!SpamFilter.isLocalNotice(message) &&
SpamFilter.shared.shouldHide(message, SpamFilter.meshSenderKey(message)) -> false
message.channel != null -> {
AppStateStore.addChannelMessage(message.channel, message)
true

View File

@ -0,0 +1,153 @@
package com.bitchat.watch.ui
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PowerSettingsNew
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.foundation.pager.HorizontalPager
import androidx.wear.compose.foundation.pager.rememberPagerState
import androidx.wear.compose.material3.AlertDialog
import androidx.wear.compose.material3.AlertDialogDefaults
import androidx.wear.compose.material3.FilledTonalButton
import androidx.wear.compose.material3.HorizontalPagerScaffold
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.Text
import com.bitchat.android.services.AppStateStore
import com.bitchat.watch.ui.theme.ChatVisualTokens
import com.bitchat.watch.ui.theme.LocalBitchatPalette
private const val MENU_PAGE = 0
private const val CHAT_PAGE = 1
/**
* Home of the app: the public mesh chat, with an app menu one page to its left. Swiping the
* chat to the right reveals the menu (the Wear pattern used by Fitbit, Health and similar
* apps); swiping right again from the menu's left edge falls through to the system
* swipe-to-dismiss, so leaving the app still works as everywhere else on the watch.
*
* [swipeEnabled] is false while the chat owns a horizontal gesture (push-to-talk drag,
* full-screen image), so the pager never steals the pointer mid-gesture.
*/
@Composable
fun ChatHomePager(
swipeEnabled: Boolean,
menu: @Composable () -> Unit,
chat: @Composable () -> Unit,
) {
val pagerState = rememberPagerState(initialPage = CHAT_PAGE) { 2 }
// No page indicator: the menu is a peek-behind page, not a peer of the chat, and the
// dots would crowd the chat's edge action bar.
HorizontalPagerScaffold(pagerState = pagerState, pageIndicator = null) {
HorizontalPager(
state = pagerState,
userScrollEnabled = swipeEnabled,
// The crown scrolls chat history, never pages.
rotaryScrollableBehavior = null,
) { page ->
when (page) {
MENU_PAGE -> menu()
CHAT_PAGE -> chat()
}
}
}
}
@Composable
fun ChatMenuScreen(onQuit: () -> Unit) {
val palette = LocalBitchatPalette.current
val peers by AppStateStore.peers.collectAsState()
val listState = rememberScalingLazyListState()
var confirmQuit by rememberSaveable { mutableStateOf(false) }
ScreenScaffold(scrollState = listState) { scaffoldPadding ->
ScalingLazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
contentPadding = scaffoldPadding,
) {
item {
ListHeader {
Text(
text = "bitchat",
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
)
}
}
item {
Text(
text = if (peers.isEmpty()) "Mesh running" else meshPeersText(peers.size),
style = ChatVisualTokens.SystemActionStyle,
color = palette.textTertiary,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp),
)
}
item {
FilledTonalButton(
onClick = { confirmQuit = true },
modifier = Modifier.fillMaxWidth(),
icon = {
Icon(
imageVector = Icons.Filled.PowerSettingsNew,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
)
},
label = { Text("Quit bitchat") },
secondaryLabel = {
Text(
text = "Stop the mesh",
color = palette.textTertiary,
)
},
)
}
}
}
AlertDialog(
visible = confirmQuit,
onDismissRequest = { confirmQuit = false },
icon = {
Icon(
imageVector = Icons.Filled.PowerSettingsNew,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(32.dp),
)
},
title = { Text("Quit bitchat?") },
confirmButton = {
AlertDialogDefaults.ConfirmButton(
onClick = {
confirmQuit = false
onQuit()
},
)
},
dismissButton = {
AlertDialogDefaults.DismissButton(onClick = { confirmQuit = false })
},
)
}
private fun meshPeersText(count: Int): String =
if (count == 1) "Mesh running · 1 peer" else "Mesh running · $count peers"

View File

@ -57,7 +57,11 @@ import java.util.Date
import java.util.Locale
@Composable
fun ChatScreen(onOpenPeople: () -> Unit, onOpenTextInput: () -> Unit) {
fun ChatScreen(
onOpenPeople: () -> Unit,
onOpenTextInput: () -> Unit,
onHorizontalGestureActive: (Boolean) -> Unit = {},
) {
val context = LocalContext.current
val messages by AppStateStore.publicMessages.collectAsState()
val peers by AppStateStore.peers.collectAsState()
@ -82,6 +86,11 @@ fun ChatScreen(onOpenPeople: () -> Unit, onOpenTextInput: () -> Unit) {
}
) { path -> mesh?.let { sendVoiceNote(it, null, path) } }
// Push-to-talk drags and the image viewer own the whole screen; the home pager must not
// turn them into a page swipe.
val horizontalGestureActive = voice.recording || viewerPath != null
LaunchedEffect(horizontalGestureActive) { onHorizontalGestureActive(horizontalGestureActive) }
androidx.compose.runtime.DisposableEffect(liveVoiceManager) {
liveVoiceManager.showPublicMesh()
onDispose { liveVoiceManager.clearVisibleConversation() }