Replaces the Google Drive backup flow (#528) with deterministic key derivation. The user's Nostr identity IS their Google account — no encrypted blobs to store, no backup events to publish, nothing for Google or any third party to retain. privkey = SHA-256("wisp-account-v1:" || sub || ":" || accountIndex) Properties: - Same Google account always derives the same nsec on any device - No backup to lose: signing in regenerates the keys - No `drive.appdata` OAuth scope, no scary Drive consent dialog - Anyone with access to the Google account can derive every nsec. Bounded by Google account security — same trade-off as #528, with a much simpler attack surface and no third-party storage layer Discovery on sign-in: - Derive candidate keypairs for indices 0..15 from the user's `sub` - One REQ to relay.damus.io, relay.primal.net, nos.lol, nostr.wine, relay.wisp.talk, relay.ditto.pub asking for kind 0/3/10002 events from those pubkeys - Pubkeys with any activity = "in use" accounts that go in the chooser; avatar + display name come from the same kind-0 events - "Create another account" derives the next-unused index Code shrinkage: DriveBackupService is gone, BackupCrypto's encryption helpers are gone, the play-services-auth dependency is gone, and the Drive-related ProGuard rules are gone. The whole flow is ~200 fewer lines than #528 and easier to audit — the derivation is one line of SHA-256. Splash button switches to Google's dark-mode brand variant (#131314 container, full-color G, #8E918F stroke) per Sign in with Google spec. No migration needed: nobody is on the #528 flow yet.
121 lines
3.8 KiB
Kotlin
121 lines
3.8 KiB
Kotlin
import java.util.Properties
|
|
|
|
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.android)
|
|
alias(libs.plugins.kotlin.compose)
|
|
alias(libs.plugins.kotlin.serialization)
|
|
alias(libs.plugins.objectbox)
|
|
}
|
|
|
|
android {
|
|
val baseApplicationId = rootProject.extra["baseApplicationId"] as String
|
|
val debugApplicationIdSuffix = rootProject.extra["debugApplicationIdSuffix"] as String
|
|
|
|
namespace = "com.wisp.app"
|
|
compileSdk = 35
|
|
|
|
defaultConfig {
|
|
applicationId = baseApplicationId
|
|
minSdk = 26
|
|
targetSdk = 35
|
|
versionCode = 79
|
|
versionName = "1.0.5"
|
|
resValue("string", "app_name", "Wisp")
|
|
|
|
ndk {
|
|
abiFilters += "arm64-v8a"
|
|
}
|
|
|
|
val localProps = rootProject.file("local.properties")
|
|
val breezApiKey = if (localProps.exists()) {
|
|
val props = Properties()
|
|
localProps.inputStream().use { props.load(it) }
|
|
props.getProperty("breez.api.key", "")
|
|
} else ""
|
|
buildConfigField("String", "BREEZ_API_KEY", "\"$breezApiKey\"")
|
|
buildConfigField("String", "BREEZ_SDK_VERSION", "\"${libs.versions.breez.sdk.spark.get()}\"")
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
applicationIdSuffix = debugApplicationIdSuffix
|
|
resValue("string", "app_name", "Wisp Debug")
|
|
}
|
|
|
|
release {
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = "17"
|
|
}
|
|
|
|
packaging {
|
|
jniLibs.useLegacyPackaging = false
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
testImplementation(libs.kotlinx.serialization.json)
|
|
testImplementation("junit:junit:4.13.2")
|
|
|
|
implementation(platform(libs.compose.bom))
|
|
implementation(libs.compose.ui)
|
|
implementation(libs.compose.ui.graphics)
|
|
implementation(libs.compose.material3)
|
|
implementation(libs.compose.icons.extended)
|
|
implementation(libs.activity.compose)
|
|
implementation(libs.navigation.compose)
|
|
implementation(libs.lifecycle.viewmodel.compose)
|
|
implementation(libs.lifecycle.runtime.compose)
|
|
implementation(libs.okhttp)
|
|
implementation(libs.kotlinx.serialization.json)
|
|
implementation(libs.kotlinx.coroutines.android)
|
|
implementation(libs.secp256k1.kmp)
|
|
implementation(libs.secp256k1.kmp.jni.android)
|
|
implementation(libs.coil.compose)
|
|
implementation(libs.coil.gif)
|
|
implementation(libs.coil.network.okhttp)
|
|
implementation(libs.coil.video)
|
|
implementation(libs.security.crypto)
|
|
implementation(libs.bouncycastle)
|
|
implementation(libs.media3.exoplayer)
|
|
implementation(libs.media3.exoplayer.hls)
|
|
implementation(libs.media3.datasource.okhttp)
|
|
implementation(libs.media3.ui)
|
|
implementation(libs.media3.session)
|
|
implementation(libs.biometric)
|
|
implementation(libs.splashscreen)
|
|
implementation(libs.profileinstaller)
|
|
implementation(libs.zxing.core)
|
|
implementation(libs.mlkit.translate)
|
|
implementation(libs.mlkit.language.id)
|
|
implementation(libs.kotlinx.coroutines.play.services)
|
|
implementation(libs.objectbox.android)
|
|
implementation(libs.objectbox.kotlin)
|
|
implementation(libs.breez.sdk.spark)
|
|
implementation(libs.mlkit.barcode)
|
|
implementation(libs.camerax.core)
|
|
implementation(libs.camerax.camera2)
|
|
implementation(libs.camerax.lifecycle)
|
|
implementation(libs.camerax.view)
|
|
implementation(libs.camerax.mlkit)
|
|
implementation(libs.androidx.credentials)
|
|
implementation(libs.androidx.credentials.play.services.auth)
|
|
implementation(libs.googleid)
|
|
}
|