diff --git a/README.md b/README.md index d0115124..e5359e89 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,6 @@ Wisp implements a full NIP-65 outbox/inbox model with relay scoring: - **Persistent + ephemeral pool** — `RelayPool` maintains up to 30 persistent connections plus up to 50 short-lived ephemeral ones, with idle cleanup and per-relay cooldowns after failures - **NIP-42 authentication** — signs AUTH challenges only for relays the user has explicitly approved, with persisted approvals, and waits for AUTH before sending sensitive publishes (DMs, group events) - **NIP-11 relay info** — fetches and respects relay metadata, capabilities, and limitations -- **Tor support** — route relay traffic through an embedded Tor client for enhanced privacy ### Privacy & Private Messaging @@ -164,7 +163,7 @@ Wisp follows an MVVM architecture with clear layer separation: │ Relay Layer │ │ RelayPool, OutboxRouter, RelayScoreBoard, │ │ SubscriptionManager, RelayHealthTracker, │ -│ TorManager, Relay (OkHttp WebSocket) │ +│ Relay (OkHttp WebSocket) │ └──────────────────────────────────────────────────────┘ ``` @@ -182,7 +181,7 @@ Wisp follows an MVVM architecture with clear layer separation: ``` app/src/main/kotlin/com/wisp/app/ ├── nostr/ # Protocol implementations (NipXX.kt objects) -├── relay/ # WebSocket relay, pool, outbox router, scoring, Tor +├── relay/ # WebSocket relay, pool, outbox router, scoring ├── repo/ # Data repositories, caches, and persistence wrappers ├── db/ # ObjectBox entities (EventEntity, GroupMessageEntity...) ├── ml/ # On-device nspam LightGBM classifier @@ -342,7 +341,6 @@ Contributions are welcome. Wisp is open source and community help makes it bette | Lightning | Breez SDK Spark + NWC (NIP-47) | | Media | Media3 / ExoPlayer | | QR Codes | ZXing | -| Privacy Network | Embedded Tor (optional) | | Build | Gradle 8.x / AGP 8.x | | Min SDK | Android 8.0 (API 26) | | Target SDK | Android 15 (API 35) | diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0be89d08..32a03896 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -102,8 +102,6 @@ dependencies { implementation(libs.splashscreen) implementation(libs.profileinstaller) implementation(libs.zxing.core) - implementation(libs.kmp.tor.runtime) - implementation(libs.kmp.tor.resource.exec) implementation(libs.mlkit.translate) implementation(libs.mlkit.language.id) implementation(libs.kotlinx.coroutines.play.services) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 4b539fd9..4a173d9b 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -47,10 +47,6 @@ -keep class androidx.camera.** { *; } -dontwarn androidx.camera.** -# kmp-tor --keep class io.matthewnelson.kmp.tor.** { *; } --dontwarn io.matthewnelson.kmp.tor.** - # ML Kit -keep class com.google.mlkit.** { *; } -dontwarn com.google.mlkit.** @@ -66,7 +62,3 @@ # JNA (used by Breez SDK UniFFI) -keep class com.sun.jna.** { *; } -dontwarn com.sun.jna.** - -# java.lang.management (not available on Android) --dontwarn java.lang.management.ManagementFactory --dontwarn java.lang.management.RuntimeMXBean diff --git a/app/src/main/kotlin/com/wisp/app/Navigation.kt b/app/src/main/kotlin/com/wisp/app/Navigation.kt index b8fa873b..4658784d 100644 --- a/app/src/main/kotlin/com/wisp/app/Navigation.kt +++ b/app/src/main/kotlin/com/wisp/app/Navigation.kt @@ -314,47 +314,6 @@ fun WispNavHost( } } - // Tor state - val torPrefs = remember { context.getSharedPreferences("wisp_settings", android.content.Context.MODE_PRIVATE) } - val torStatus by com.wisp.app.relay.TorManager.status.collectAsState() - val isTorEnabled = torStatus != com.wisp.app.relay.TorStatus.DISABLED - - // Auto-start Tor if previously enabled - val torScope = androidx.compose.runtime.rememberCoroutineScope() - LaunchedEffect(Unit) { - if (torPrefs.getBoolean("tor_enabled", false)) { - com.wisp.app.relay.TorManager.start() - } - } - // When Tor finishes connecting/disconnecting, swap the relay pool client. - // Skip the initial DISABLED state on first composition — only react to changes. - var torStatusInitialized by remember { mutableStateOf(false) } - LaunchedEffect(torStatus) { - if (!torStatusInitialized) { - torStatusInitialized = true - return@LaunchedEffect - } - if (torStatus == com.wisp.app.relay.TorStatus.CONNECTED || - torStatus == com.wisp.app.relay.TorStatus.DISABLED) { - if (authViewModel.isLoggedIn) { - feedViewModel.lifecycleManager.onTorSwitch( - savedConfigs = feedViewModel.keyRepo.getRelays(), - savedDmUrls = feedViewModel.keyRepo.getDmRelays() - ) - } - } - } - val onToggleTor: (Boolean) -> Unit = { enabled -> - torPrefs.edit().putBoolean("tor_enabled", enabled).apply() - torScope.launch { - if (enabled) { - com.wisp.app.relay.TorManager.start() - } else { - com.wisp.app.relay.TorManager.stop() - } - } - } - val startDestination = rememberSaveable { when { !authViewModel.isLoggedIn -> Routes.SPLASH @@ -674,9 +633,6 @@ fun WispNavHost( composable(Routes.SPLASH) { SplashScreen( viewModel = splashViewModel, - isTorEnabled = isTorEnabled, - torStatus = torStatus, - onToggleTor = onToggleTor, onSignUp = { if (authViewModel.signUp()) { navController.navigate(Routes.ONBOARDING_PROFILE) { @@ -693,9 +649,6 @@ fun WispNavHost( composable(Routes.AUTH) { AuthScreen( viewModel = authViewModel, - isTorEnabled = isTorEnabled, - torStatus = torStatus, - onToggleTor = onToggleTor, showSignUp = false, onAuthenticated = { isNewAccount -> val wasAddingAccount = authViewModel.isAddingAccount @@ -784,9 +737,6 @@ fun WispNavHost( viewModel = feedViewModel, isDarkTheme = isDarkTheme, onToggleTheme = onToggleTheme, - isTorEnabled = isTorEnabled, - torStatus = torStatus, - onToggleTor = onToggleTor, scrollToTopTrigger = scrollToTopTrigger, onCompose = { replyTarget = null diff --git a/app/src/main/kotlin/com/wisp/app/WispApp.kt b/app/src/main/kotlin/com/wisp/app/WispApp.kt index 245b60b8..42bdcf49 100644 --- a/app/src/main/kotlin/com/wisp/app/WispApp.kt +++ b/app/src/main/kotlin/com/wisp/app/WispApp.kt @@ -10,11 +10,9 @@ import coil3.network.okhttp.OkHttpNetworkFetcherFactory import coil3.request.crossfade import com.wisp.app.db.WispObjectBox import com.wisp.app.relay.HttpClientFactory -import com.wisp.app.relay.TorManager import com.wisp.app.repo.DiagnosticLogger import com.wisp.app.repo.ExchangeRateRepository import com.wisp.app.repo.ZapSender -import okhttp3.Call class WispApp : Application(), SingletonImageLoader.Factory { @@ -23,20 +21,16 @@ class WispApp : Application(), SingletonImageLoader.Factory { CrashHandler.install(this) DiagnosticLogger.init(this) WispObjectBox.init(this) - TorManager.initialize(this) ZapSender.init(this) ExchangeRateRepository.init(this) } override fun newImageLoader(context: android.content.Context): ImageLoader { - val torAwareCallFactory = Call.Factory { request -> - HttpClientFactory.getImageClient().newCall(request) - } return ImageLoader.Builder(context) .components { add(AnimatedImageDecoder.Factory()) add(VideoFrameDecoder.Factory()) - add(OkHttpNetworkFetcherFactory(callFactory = { torAwareCallFactory })) + add(OkHttpNetworkFetcherFactory(callFactory = { HttpClientFactory.getImageClient() })) } .memoryCache { MemoryCache.Builder() diff --git a/app/src/main/kotlin/com/wisp/app/nostr/Nip65.kt b/app/src/main/kotlin/com/wisp/app/nostr/Nip65.kt index c3b8313f..dc1daf15 100644 --- a/app/src/main/kotlin/com/wisp/app/nostr/Nip65.kt +++ b/app/src/main/kotlin/com/wisp/app/nostr/Nip65.kt @@ -1,6 +1,5 @@ package com.wisp.app.nostr -import android.util.Log import com.wisp.app.relay.RelayConfig object Nip65 { @@ -17,18 +16,13 @@ object Nip65 { write = marker == null || marker == "write" ) } - val deduped = result.groupBy { it.url }.map { (url, configs) -> + return result.groupBy { it.url }.map { (url, configs) -> RelayConfig( url = url, read = configs.any { it.read }, write = configs.any { it.write } ) } - val onionRelays = deduped.filter { RelayConfig.isOnionUrl(it.url) } - if (onionRelays.isNotEmpty()) { - Log.d("TorRelay", "[Nip65] parseRelayList: pubkey=${event.pubkey.take(8)}… has ${onionRelays.size} .onion relay(s): ${onionRelays.map { it.url }}") - } - return deduped } fun buildRelayTags(relays: List): List> { diff --git a/app/src/main/kotlin/com/wisp/app/relay/HttpClientFactory.kt b/app/src/main/kotlin/com/wisp/app/relay/HttpClientFactory.kt index ecb7b5bd..04a80074 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/HttpClientFactory.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/HttpClientFactory.kt @@ -5,31 +5,12 @@ import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory import okhttp3.Dispatcher -import okhttp3.Dns import okhttp3.OkHttpClient -import java.net.InetAddress import java.util.concurrent.TimeUnit object HttpClientFactory { - private val TOR_TIMEOUT_MULTIPLIER = 3L - - /** - * DNS resolver that forces all hostname resolution through the SOCKS5 proxy. - * When Tor is active, returns an unresolved InetAddress so OkHttp sends the - * hostname through the SOCKS tunnel and the Tor exit node resolves DNS. - * This prevents DNS leaks. - */ - private val torSafeDns = object : Dns { - override fun lookup(hostname: String): List { - return listOf(InetAddress.getByAddress(hostname, byteArrayOf(0, 0, 0, 0))) - } - } - fun createRelayClient(): OkHttpClient { - val isTor = TorManager.isEnabled() - val connectTimeout = if (isTor) 30L else 10L - // OkHttp's default Dispatcher.maxRequests is 64, which caps concurrent // WebSocket upgrade requests. With outbox routing creating 50+ ephemeral // connections, new user-initiated connections get queued and time out. @@ -38,9 +19,9 @@ object HttpClientFactory { maxRequestsPerHost = 10 } - val builder = OkHttpClient.Builder() + return OkHttpClient.Builder() .dispatcher(dispatcher) - .connectTimeout(connectTimeout, TimeUnit.SECONDS) + .connectTimeout(10, TimeUnit.SECONDS) .pingInterval(30, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.MILLISECONDS) // Strip permessage-deflate from the REQUEST so the server never negotiates @@ -54,29 +35,17 @@ object HttpClientFactory { .build() chain.proceed(request) } - - if (isTor) { - TorManager.proxy?.let { builder.proxy(it) } - builder.dns(torSafeDns) - } - - return builder.build() + .build() } private var imageClient: OkHttpClient? = null - private var imageClientBuiltWithTor: Boolean = false fun getImageClient(): OkHttpClient { - val torNow = TorManager.isEnabled() - val client = imageClient - if (client != null && imageClientBuiltWithTor == torNow) return client + imageClient?.let { return it } return createHttpClient( connectTimeoutSeconds = 10, readTimeoutSeconds = 30 - ).also { - imageClient = it - imageClientBuiltWithTor = torNow - } + ).also { imageClient = it } } fun createExoPlayer(context: Context): ExoPlayer { @@ -106,21 +75,13 @@ object HttpClientFactory { writeTimeoutSeconds: Long = 0, followRedirects: Boolean = true ): OkHttpClient { - val isTor = TorManager.isEnabled() - val multiplier = if (isTor) TOR_TIMEOUT_MULTIPLIER else 1L - val builder = OkHttpClient.Builder() - .connectTimeout(connectTimeoutSeconds * multiplier, TimeUnit.SECONDS) - .readTimeout(readTimeoutSeconds * multiplier, TimeUnit.SECONDS) + .connectTimeout(connectTimeoutSeconds, TimeUnit.SECONDS) + .readTimeout(readTimeoutSeconds, TimeUnit.SECONDS) .followRedirects(followRedirects) if (writeTimeoutSeconds > 0) { - builder.writeTimeout(writeTimeoutSeconds * multiplier, TimeUnit.SECONDS) - } - - if (isTor) { - TorManager.proxy?.let { builder.proxy(it) } - builder.dns(torSafeDns) + builder.writeTimeout(writeTimeoutSeconds, TimeUnit.SECONDS) } return builder.build() diff --git a/app/src/main/kotlin/com/wisp/app/relay/Relay.kt b/app/src/main/kotlin/com/wisp/app/relay/Relay.kt index c4dd497d..4130f45d 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/Relay.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/Relay.kt @@ -132,15 +132,9 @@ class Relay( } val socketId = System.nanoTime() Log.d("RLC", "[Relay] connect() creating ws#$socketId for ${config.url}") - if (config.url.contains(".onion")) { - Log.d("TorRelay", "[Relay] connect() .onion relay: ${config.url} proxy=${client.proxy} connectTimeout=${client.connectTimeoutMillis}ms") - } webSocket = client.newWebSocket(request, object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { Log.d("RLC", "[Relay] ws#$socketId onOpen ${config.url} | isConnected was=$isConnected") - if (config.url.contains(".onion")) { - Log.d("TorRelay", "[Relay] .onion connection SUCCESS: ${config.url}") - } isConnected = true // Successful connection — reset attempt tracking synchronized(attemptLock) { connectAttempts.clear() } @@ -164,9 +158,6 @@ class Relay( override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { val isCurrent = synchronized(connectLock) { this@Relay.webSocket === webSocket } Log.e("RLC", "[Relay] ws#$socketId onFailure ${config.url}: ${t.javaClass.simpleName}: ${t.message} | httpCode=${response?.code} | isCurrent=$isCurrent isConnected=$isConnected") - if (config.url.contains(".onion")) { - Log.e("TorRelay", "[Relay] .onion connection FAILED: ${config.url} | error=${t.javaClass.simpleName}: ${t.message}", t) - } synchronized(connectLock) { if (this@Relay.webSocket === webSocket) { isConnected = false diff --git a/app/src/main/kotlin/com/wisp/app/relay/RelayConfig.kt b/app/src/main/kotlin/com/wisp/app/relay/RelayConfig.kt index 4c45dd26..a09ba2b6 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/RelayConfig.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/RelayConfig.kt @@ -52,20 +52,11 @@ data class RelayConfig( private val IP_HOST_REGEX = Regex("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$") - fun isOnionUrl(url: String): Boolean = url.contains(".onion") - /** * Structural URL validation — can this URL be stored in a relay list? - * Always allows .onion addresses (with ws:// or wss://) regardless of Tor state. - * Rejects: localhost, IP addresses, URLs with ports (unless .onion). + * Rejects: non-wss schemes, localhost, IP addresses, URLs with ports. */ fun isValidUrl(url: String): Boolean { - if (isOnionUrl(url)) { - // .onion relays can use ws:// (TLS redundant over Tor) or wss:// - if (!url.startsWith("wss://") && !url.startsWith("ws://")) return false - return true - } - if (!url.startsWith("wss://")) return false val afterScheme = url.removePrefix("wss://") val hostPort = afterScheme.split("/", limit = 2)[0] @@ -76,16 +67,6 @@ data class RelayConfig( return true } - /** - * Returns true if the relay URL can be connected to right now. - * .onion addresses require Tor to be active. - */ - fun isConnectableUrl(url: String): Boolean { - if (!isValidUrl(url)) return false - if (isOnionUrl(url) && !TorManager.isEnabled()) return false - return true - } - private val LOCAL_HOST_REGEX = Regex( "^ws://(" + "localhost|" + diff --git a/app/src/main/kotlin/com/wisp/app/relay/RelayLifecycleManager.kt b/app/src/main/kotlin/com/wisp/app/relay/RelayLifecycleManager.kt index f941fb46..bd9db504 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/RelayLifecycleManager.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/RelayLifecycleManager.kt @@ -170,24 +170,6 @@ class RelayLifecycleManager( } } - /** - * Handle Tor on/off switch. Swaps the OkHttpClient, reconnects all relays, - * and re-establishes subscriptions via [onReconnected]. - * Pass the full saved relay list so .onion relays are included when Tor turns on. - */ - fun onTorSwitch( - savedConfigs: List? = null, - savedDmUrls: List? = null - ) { - reconnectJob?.cancel() - reconnectJob = scope.launch { - relayPool.swapClientAndReconnect(savedConfigs, savedDmUrls) - relayPool.awaitAnyConnected(minCount = 3, timeoutMs = 10_000) - relayPool.appIsActive = true - onReconnected(true) - } - } - /** * Stop observing. Call on account switch or cleanup. */ diff --git a/app/src/main/kotlin/com/wisp/app/relay/RelayPool.kt b/app/src/main/kotlin/com/wisp/app/relay/RelayPool.kt index 6e584c19..7717226d 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/RelayPool.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/RelayPool.kt @@ -36,7 +36,6 @@ class RelayPool(private val prefs: SharedPreferences? = null) { private set private var client: OkHttpClient = Relay.createClient() - private var clientBuiltWithTor: Boolean = TorManager.isEnabled() private val relays = CopyOnWriteArrayList() private val dmRelays = CopyOnWriteArrayList() /** Persistent connections for NIP-29 group chat relays — auto-reconnect enabled. */ @@ -112,18 +111,6 @@ class RelayPool(private val prefs: SharedPreferences? = null) { userApprovedAuthRelays.add(url) } - /** - * Ensure the OkHttpClient matches the current Tor state. - * If Tor was enabled/disabled since the client was built, rebuild it. - */ - private fun ensureClientCurrent() { - val torNow = TorManager.isEnabled() - if (torNow != clientBuiltWithTor) { - client = HttpClientFactory.createRelayClient() - clientBuiltWithTor = torNow - } - } - @Volatile var appIsActive = false set(value) { field = value @@ -256,10 +243,9 @@ class RelayPool(private val prefs: SharedPreferences? = null) { } fun updateRelays(configs: List) { - ensureClientCurrent() val badRelays = healthTracker?.getBadRelays() ?: emptySet() val filtered = configs.filter { - it.url !in blockedUrls && it.url !in badRelays && RelayConfig.isConnectableUrl(it.url) + it.url !in blockedUrls && it.url !in badRelays && RelayConfig.isValidUrl(it.url) }.take(MAX_PERSISTENT) // Disconnect removed relays @@ -288,8 +274,7 @@ class RelayPool(private val prefs: SharedPreferences? = null) { } fun updateDmRelays(urls: List) { - ensureClientCurrent() - val filtered = urls.filter { it !in blockedUrls && RelayConfig.isConnectableUrl(it) }.take(MAX_DM_RELAYS) + val filtered = urls.filter { it !in blockedUrls && RelayConfig.isValidUrl(it) }.take(MAX_DM_RELAYS) val currentUrls = filtered.toSet() dmRelays.filter { it.config.url !in currentUrls }.forEach { it.disconnect() @@ -324,8 +309,7 @@ class RelayPool(private val prefs: SharedPreferences? = null) { * Must be called before sending subscriptions so the relay survives disconnection. */ fun ensureGroupRelay(url: String) { - ensureClientCurrent() - if (url in blockedUrls || !RelayConfig.isConnectableUrl(url)) return + if (url in blockedUrls || !RelayConfig.isValidUrl(url)) return // Chat relay AUTH challenges route through the tier-2 prompt flow so the user // can decide to reveal their pubkey for private/hidden group access. groupRelayAuthTargets.add(url) @@ -861,9 +845,8 @@ class RelayPool(private val prefs: SharedPreferences? = null) { * handshake before sending an event that requires authentication. */ fun connectEphemeralRelay(url: String) { - ensureClientCurrent() if (url in blockedUrls) return - if (!RelayConfig.isConnectableUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return + if (!RelayConfig.isValidUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return if (ephemeralRelays.containsKey(url) || relayIndex.containsKey(url)) return if (ephemeralRelays.size >= MAX_EPHEMERAL) return ephemeralRelays.computeIfAbsent(url) { @@ -885,10 +868,9 @@ class RelayPool(private val prefs: SharedPreferences? = null) { write: Boolean = false, autoReconnect: Boolean = false ): Boolean { - ensureClientCurrent() if (url in blockedUrls) return false if (!skipBadCheck && healthTracker?.isBad(url) == true) return false - if (!RelayConfig.isConnectableUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return false + if (!RelayConfig.isValidUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return false // Check cooldown for failed relays val cooldownUntil = relayCooldowns[url] @@ -1216,9 +1198,8 @@ class RelayPool(private val prefs: SharedPreferences? = null) { * the WebSocket connection early so it's ready when the first REQ arrives. */ fun preConnectEphemeral(url: String) { - ensureClientCurrent() if (url in blockedUrls) return - if (!RelayConfig.isConnectableUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return + if (!RelayConfig.isValidUrl(url) && !RelayConfig.isLocalRelayUrl(url)) return if (ephemeralRelays.containsKey(url)) return if (ephemeralRelays.size >= MAX_EPHEMERAL) return val relay = Relay(RelayConfig(url, read = true, write = false), client, scope) @@ -1291,29 +1272,6 @@ class RelayPool(private val prefs: SharedPreferences? = null) { } } - /** - * Rebuild the OkHttpClient (e.g. after Tor toggle) and reconnect all relays. - * When [allConfigs] / [allDmUrls] are provided (e.g. from saved prefs), they - * are used instead of the currently-connected set so that .onion relays are - * picked up when Tor turns on. - */ - fun swapClientAndReconnect( - allConfigs: List? = null, - allDmUrls: List? = null - ) { - val configs = allConfigs ?: relays.map { it.config }.toList() - val dmUrls = allDmUrls ?: dmRelays.map { it.config.url }.toList() - Log.d("RLC", "[Pool] swapClientAndReconnect — persistent=${configs.size} dm=${dmUrls.size}") - - val oldClient = client - disconnectAll() - client = HttpClientFactory.createRelayClient() - clientBuiltWithTor = TorManager.isEnabled() - updateRelays(configs) - updateDmRelays(dmUrls) - scope.launch { HttpClientFactory.safeShutdownClient(oldClient) } - } - // --- Local relay management --- fun updateLocalRelay(config: LocalRelayConfig?, userPubkey: String?) { diff --git a/app/src/main/kotlin/com/wisp/app/relay/RelayScoreBoard.kt b/app/src/main/kotlin/com/wisp/app/relay/RelayScoreBoard.kt index 4e56a2c4..64d6bbbc 100644 --- a/app/src/main/kotlin/com/wisp/app/relay/RelayScoreBoard.kt +++ b/app/src/main/kotlin/com/wisp/app/relay/RelayScoreBoard.kt @@ -83,19 +83,9 @@ class RelayScoreBoard( } } - val onionRelays = newRelayAuthors.keys.filter { RelayConfig.isOnionUrl(it) } Log.d(TAG, "recompute(): ${follows.size} follows, $knownCount have relay lists, " + "${follows.size - knownCount} missing, ${newRelayAuthors.size} unique relays" + if (excludeRelays.isNotEmpty()) ", ${excludeRelays.size} excluded" else "") - if (onionRelays.isNotEmpty()) { - Log.d("TorRelay", "[ScoreBoard] recompute: ${onionRelays.size} .onion relays found: $onionRelays") - for (url in onionRelays) { - val authors = newRelayAuthors[url]?.size ?: 0 - Log.d("TorRelay", "[ScoreBoard] $url — $authors author(s)") - } - } else { - Log.d("TorRelay", "[ScoreBoard] recompute: no .onion relays found among ${newRelayAuthors.size} unique relays") - } if (newRelayAuthors.isEmpty()) { scoredRelays = emptyList() diff --git a/app/src/main/kotlin/com/wisp/app/relay/TorManager.kt b/app/src/main/kotlin/com/wisp/app/relay/TorManager.kt deleted file mode 100644 index 87938a46..00000000 --- a/app/src/main/kotlin/com/wisp/app/relay/TorManager.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.wisp.app.relay - -import android.content.Context -import android.util.Log -import io.matthewnelson.kmp.tor.resource.exec.tor.ResourceLoaderTorExec -import io.matthewnelson.kmp.tor.runtime.Action.Companion.startDaemonAsync -import io.matthewnelson.kmp.tor.runtime.Action.Companion.stopDaemonAsync -import io.matthewnelson.kmp.tor.runtime.TorRuntime -import io.matthewnelson.kmp.tor.runtime.RuntimeEvent -import io.matthewnelson.kmp.tor.runtime.core.OnEvent -import io.matthewnelson.kmp.tor.runtime.core.TorEvent -import io.matthewnelson.kmp.tor.runtime.core.config.TorOption -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import java.net.InetSocketAddress -import java.net.Proxy - -enum class TorStatus { DISABLED, STARTING, CONNECTED, ERROR } - -object TorManager { - private const val TAG = "TorManager" - - private val _status = MutableStateFlow(TorStatus.DISABLED) - val status: StateFlow = _status - - private val _socksPort = MutableStateFlow(null) - val socksPort: StateFlow = _socksPort - - val proxy: Proxy? - get() { - val port = _socksPort.value ?: return null - if (_status.value != TorStatus.CONNECTED) return null - return Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", port)) - } - - fun isEnabled(): Boolean = _status.value == TorStatus.CONNECTED - - private var runtime: TorRuntime? = null - - fun initialize(context: Context) { - if (runtime != null) return - - val workDir = context.filesDir.resolve("tor") - val cacheDir = context.cacheDir.resolve("tor") - workDir.mkdirs() - cacheDir.mkdirs() - - val environment = TorRuntime.Environment.Builder( - workDir, cacheDir, ResourceLoaderTorExec::getOrCreate - ) - - runtime = TorRuntime.Builder(environment) { - observerStatic(RuntimeEvent.LISTENERS, OnEvent.Executor.Immediate) { listeners -> - val socksLine = listeners.toString() - Log.d(TAG, "Listeners update: $socksLine") - val port = parseSocksPort(socksLine) - if (port != null) { - _socksPort.value = port - if (_status.value == TorStatus.STARTING) { - _status.value = TorStatus.CONNECTED - Log.d(TAG, "Tor connected on SOCKS port $port") - } - } - } - - observerStatic(RuntimeEvent.STATE, OnEvent.Executor.Immediate) { state -> - Log.d(TAG, "State: $state") - } - - TorEvent.entries().forEach { event -> - observerStatic(event, OnEvent.Executor.Immediate) { data -> - Log.d(TAG, "TorEvent[$event]: $data") - } - } - - required(TorEvent.ERR) - required(TorEvent.WARN) - - config { environment -> - TorOption.SocksPort.configure { auto() } - } - } - } - - suspend fun start() { - val rt = runtime ?: run { - Log.e(TAG, "TorManager not initialized") - _status.value = TorStatus.ERROR - return - } - if (_status.value == TorStatus.CONNECTED || _status.value == TorStatus.STARTING) return - - _status.value = TorStatus.STARTING - _socksPort.value = null - try { - rt.startDaemonAsync() - } catch (e: Exception) { - Log.e(TAG, "Failed to start Tor: ${e.message}", e) - _status.value = TorStatus.ERROR - } - } - - suspend fun stop() { - val rt = runtime ?: return - try { - rt.stopDaemonAsync() - } catch (e: Exception) { - Log.e(TAG, "Failed to stop Tor: ${e.message}", e) - } - _socksPort.value = null - _status.value = TorStatus.DISABLED - } - - private fun parseSocksPort(listenersString: String): Int? { - // TorListeners.toString() contains socks addresses like "127.0.0.1:9050" - // Try to extract port from common patterns - val regex = Regex("""127\.0\.0\.1:(\d+)""") - return regex.find(listenersString)?.groupValues?.get(1)?.toIntOrNull() - } -} diff --git a/app/src/main/kotlin/com/wisp/app/ui/component/WispDrawerContent.kt b/app/src/main/kotlin/com/wisp/app/ui/component/WispDrawerContent.kt index 74ab5b06..eb21d6a4 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/component/WispDrawerContent.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/component/WispDrawerContent.kt @@ -22,11 +22,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.material.icons.outlined.Block import androidx.compose.material.icons.outlined.EmojiEmotions import androidx.compose.material.icons.outlined.Cloud -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import com.wisp.app.R -import com.wisp.app.relay.TorStatus import androidx.compose.material.icons.outlined.CurrencyBitcoin import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Email @@ -84,9 +81,6 @@ fun WispDrawerContent( pubkey: String?, isDarkTheme: Boolean = true, onToggleTheme: () -> Unit = {}, - isTorEnabled: Boolean = false, - torStatus: TorStatus = TorStatus.DISABLED, - onToggleTor: (Boolean) -> Unit = {}, accounts: List = emptyList(), onSwitchAccount: (String) -> Unit = {}, onAddAccount: () -> Unit = {}, @@ -150,28 +144,6 @@ fun WispDrawerContent( ProfilePicture(url = profile?.picture, size = 64) } Spacer(modifier = Modifier.weight(1f)) - IconButton(onClick = { onToggleTor(!isTorEnabled) }) { - Box(contentAlignment = Alignment.Center) { - if (torStatus == TorStatus.STARTING) { - CircularProgressIndicator( - modifier = Modifier.size(24.dp), - strokeWidth = 2.dp, - strokeCap = StrokeCap.Round - ) - } else { - Icon( - painter = painterResource(id = R.drawable.ic_tor_onion), - contentDescription = stringResource(R.string.cd_toggle_tor), - modifier = Modifier.size(24.dp), - tint = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } - } - } IconButton(onClick = onToggleTheme) { Icon( if (isDarkTheme) Icons.Outlined.DarkMode else Icons.Outlined.LightMode, diff --git a/app/src/main/kotlin/com/wisp/app/ui/screen/AuthScreen.kt b/app/src/main/kotlin/com/wisp/app/ui/screen/AuthScreen.kt index 1fed99ef..53c73909 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/screen/AuthScreen.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/screen/AuthScreen.kt @@ -4,8 +4,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -17,13 +15,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.material3.Button -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -67,15 +63,11 @@ import androidx.compose.ui.res.stringResource import com.wisp.app.R import com.wisp.app.nostr.RemoteSignerBridge import com.wisp.app.nostr.toHex -import com.wisp.app.relay.TorStatus import com.wisp.app.viewmodel.AuthViewModel @Composable fun AuthScreen( viewModel: AuthViewModel, - isTorEnabled: Boolean = false, - torStatus: TorStatus = TorStatus.DISABLED, - onToggleTor: (Boolean) -> Unit = {}, showSignUp: Boolean = true, onAuthenticated: (isNewAccount: Boolean) -> Unit ) { @@ -157,58 +149,6 @@ fun AuthScreen( Spacer(Modifier.height(16.dp)) - val pillBorderColor = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.outlineVariant - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .border(1.dp, pillBorderColor, RoundedCornerShape(24.dp)) - .clickable { onToggleTor(!isTorEnabled) } - .padding(horizontal = 16.dp, vertical = 8.dp) - ) { - Box(contentAlignment = Alignment.Center, modifier = Modifier.size(20.dp)) { - if (torStatus == TorStatus.STARTING) { - CircularProgressIndicator( - modifier = Modifier.size(18.dp), - strokeWidth = 2.dp, - color = MaterialTheme.colorScheme.primary - ) - } else { - Icon( - painter = painterResource(id = R.drawable.ic_tor_onion), - contentDescription = stringResource(R.string.cd_toggle_tor), - modifier = Modifier.size(18.dp), - tint = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } - } - Spacer(Modifier.width(8.dp)) - Text( - text = when (torStatus) { - TorStatus.STARTING -> stringResource(R.string.auth_connecting_to_tor) - TorStatus.CONNECTED -> stringResource(R.string.auth_connected_via_tor) - TorStatus.ERROR -> stringResource(R.string.auth_tor_error) - else -> stringResource(R.string.auth_connect_via_tor) - }, - style = MaterialTheme.typography.labelMedium, - color = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } - - Spacer(Modifier.height(16.dp)) - if (showSignUp) { Button( onClick = { diff --git a/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt b/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt index 9d059f69..d17f6542 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt @@ -149,9 +149,6 @@ fun FeedScreen( viewModel: FeedViewModel, isDarkTheme: Boolean = true, onToggleTheme: () -> Unit = {}, - isTorEnabled: Boolean = false, - torStatus: com.wisp.app.relay.TorStatus = com.wisp.app.relay.TorStatus.DISABLED, - onToggleTor: (Boolean) -> Unit = {}, onCompose: () -> Unit, onReply: (NostrEvent) -> Unit, onRelays: () -> Unit, @@ -656,9 +653,6 @@ fun FeedScreen( pubkey = userPubkey, isDarkTheme = isDarkTheme, onToggleTheme = onToggleTheme, - isTorEnabled = isTorEnabled, - torStatus = torStatus, - onToggleTor = onToggleTor, accounts = accounts, onSwitchAccount = { pubkeyHex -> scope.launch { drawerState.close() } diff --git a/app/src/main/kotlin/com/wisp/app/ui/screen/SplashScreen.kt b/app/src/main/kotlin/com/wisp/app/ui/screen/SplashScreen.kt index 9d52135d..deeb6523 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/screen/SplashScreen.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/screen/SplashScreen.kt @@ -2,9 +2,6 @@ package com.wisp.app.ui.screen import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column @@ -20,7 +17,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.Card -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton @@ -50,7 +46,6 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.res.stringResource import coil3.compose.AsyncImage import com.wisp.app.R -import com.wisp.app.relay.TorStatus import com.wisp.app.viewmodel.LiveMetrics import com.wisp.app.viewmodel.SplashViewModel @@ -60,9 +55,6 @@ private val AVATAR_GAP = 4.dp @Composable fun SplashScreen( viewModel: SplashViewModel, - isTorEnabled: Boolean = false, - torStatus: TorStatus = TorStatus.DISABLED, - onToggleTor: (Boolean) -> Unit = {}, onSignUp: () -> Unit, onLogIn: () -> Unit ) { @@ -206,58 +198,6 @@ fun SplashScreen( ) { Text(stringResource(R.string.splash_log_in)) } - - Spacer(Modifier.height(16.dp)) - - val pillBorderColor = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.outlineVariant - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .border(1.dp, pillBorderColor, RoundedCornerShape(24.dp)) - .clickable { onToggleTor(!isTorEnabled) } - .padding(horizontal = 16.dp, vertical = 8.dp) - ) { - Box(contentAlignment = Alignment.Center, modifier = Modifier.size(20.dp)) { - if (torStatus == TorStatus.STARTING) { - CircularProgressIndicator( - modifier = Modifier.size(18.dp), - strokeWidth = 2.dp, - color = MaterialTheme.colorScheme.primary - ) - } else { - Icon( - painter = painterResource(id = R.drawable.ic_tor_onion), - contentDescription = stringResource(R.string.cd_toggle_tor), - modifier = Modifier.size(18.dp), - tint = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } - } - Spacer(Modifier.width(8.dp)) - Text( - text = when (torStatus) { - TorStatus.STARTING -> stringResource(R.string.splash_connecting_tor) - TorStatus.CONNECTED -> stringResource(R.string.splash_connected_tor) - TorStatus.ERROR -> stringResource(R.string.splash_tor_error) - else -> stringResource(R.string.splash_connect_tor) - }, - style = MaterialTheme.typography.labelMedium, - color = when (torStatus) { - TorStatus.CONNECTED -> MaterialTheme.colorScheme.primary - TorStatus.ERROR -> MaterialTheme.colorScheme.error - else -> MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } } } } diff --git a/app/src/main/kotlin/com/wisp/app/viewmodel/FeedViewModel.kt b/app/src/main/kotlin/com/wisp/app/viewmodel/FeedViewModel.kt index 9a50012b..d051ff40 100644 --- a/app/src/main/kotlin/com/wisp/app/viewmodel/FeedViewModel.kt +++ b/app/src/main/kotlin/com/wisp/app/viewmodel/FeedViewModel.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.viewModelScope import com.wisp.app.nostr.NostrEvent import com.wisp.app.nostr.NostrSigner import com.wisp.app.relay.HttpClientFactory -import com.wisp.app.relay.TorManager import com.wisp.app.relay.Relay import com.wisp.app.relay.RelayLifecycleManager import com.wisp.app.relay.OutboxRouter @@ -467,32 +466,21 @@ class FeedViewModel(app: Application) : AndroidViewModel(app) { if (input.startsWith("ws://") || input.startsWith("wss://")) { return if (tryConnect(input)) input else null } - // Bare domain — try both protocols - val isOnion = input.contains(".onion") - // .onion relays typically use ws:// (TLS is redundant over Tor), try that first - if (isOnion) { - val wsUrl = "ws://$input" - if (tryConnect(wsUrl)) return wsUrl - val wssUrl = "wss://$input" - if (tryConnect(wssUrl)) return wssUrl - } else { - val wssUrl = "wss://$input" - if (tryConnect(wssUrl)) return wssUrl - val wsUrl = "ws://$input" - if (tryConnect(wsUrl)) return wsUrl - } + // Bare domain — try wss:// first, fall back to ws:// + val wssUrl = "wss://$input" + if (tryConnect(wssUrl)) return wssUrl + val wsUrl = "ws://$input" + if (tryConnect(wsUrl)) return wsUrl return null } private suspend fun tryConnect(url: String): Boolean { - val isTor = TorManager.isEnabled() - val timeoutMs = if (isTor) 15_000L else 4_000L val client = HttpClientFactory.createRelayClient() val relay = Relay(RelayConfig(url, read = true, write = false), client) relay.autoReconnect = false return try { relay.connect() - val connected = relay.awaitConnected(timeoutMs = timeoutMs) + val connected = relay.awaitConnected(timeoutMs = 4_000L) relay.disconnect() connected } catch (e: Exception) { diff --git a/app/src/main/res/drawable/ic_tor_onion.xml b/app/src/main/res/drawable/ic_tor_onion.xml deleted file mode 100644 index e69d6b0d..00000000 --- a/app/src/main/res/drawable/ic_tor_onion.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d2717862..b4d88ded 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -60,10 +60,6 @@ Mit Signer anmelden Oder melde dich mit deinem Schlüssel an nsec oder npub… - Via Tor verbinden - Verbindung zu Tor wird hergestellt… - Via Tor verbunden - Tor-Fehler wisp eine kleine Oberfläche zum Scrollen durch Posts Schlüssel verbergen @@ -161,7 +157,7 @@ Banner-URL NIP-05 (z.B. user@domain.com) Lightning-Adresse - wss:// oder ws://.onion + wss:// Nutzer und Notizen suchen Relay suchen wss://relay.beispiel.com @@ -234,7 +230,6 @@ Zurück - Tor umschalten Design umschalten QR-Code anzeigen Lightning-Adresse @@ -638,10 +633,6 @@ Konto erstellen Anmelden - Verbindung zu Tor wird hergestellt… - Mit Tor verbunden - Tor-Fehler - Mit Tor verbinden Jetzt online %d online in deinem Netzwerk %d online überall auf Nostr diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5820114a..489bcaa8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -60,10 +60,6 @@ Iniciar sesión con Firmador O inicia sesión con tu clave nsec o npub… - Conectar vía Tor - Conectando a Tor… - Conectado vía Tor - Error de Tor wisp una pequeña interfaz para desplazarte por publicaciones Ocultar clave @@ -161,7 +157,7 @@ URL de Banner NIP-05 (ej. usuario@dominio.com) Dirección Lightning - wss:// o ws://.onion + wss:// Buscar usuarios y notas Buscar relay wss://relay.ejemplo.com @@ -234,7 +230,6 @@ Atrás - Alternar Tor Alternar tema Mostrar código QR Dirección Lightning @@ -638,10 +633,6 @@ Crear cuenta Iniciar sesión - Conectando a Tor… - Conectado a través de Tor - Error de Tor - Conectar a través de Tor En línea ahora %d en línea en tu red %d en línea en todo Nostr diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a94a86e7..24921de5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -60,10 +60,6 @@ Connexion avec Signer Ou connectez-vous avec votre clé nsec ou npub… - Connexion via Tor - Connexion à Tor… - Connecté via Tor - Erreur Tor wisp une petite interface pour faire défiler les posts Masquer la clé @@ -161,7 +157,7 @@ URL de la Bannière NIP-05 (ex. utilisateur@domaine.com) Adresse Lightning - wss:// ou ws://.onion + wss:// Rechercher utilisateurs et notes Rechercher un relais wss://relais.exemple.com @@ -234,7 +230,6 @@ Retour - Basculer Tor Basculer le thème Afficher le code QR Adresse Lightning @@ -638,10 +633,6 @@ Créer un compte Se connecter - Connexion à Tor… - Connecté via Tor - Erreur Tor - Se connecter via Tor En ligne maintenant %d en ligne dans votre réseau %d en ligne sur tout Nostr diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 9746835d..7b427713 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -60,10 +60,6 @@ Accedi con Signer O accedi con la tua chiave nsec o npub… - Connetti via Tor - Connessione a Tor… - Connesso via Tor - Errore Tor wisp una piccola interfaccia per scorrere i post Nascondi chiave @@ -161,7 +157,7 @@ URL Banner NIP-05 (es. utente@dominio.com) Indirizzo Lightning - wss:// o ws://.onion + wss:// Cerca utenti e note Cerca relay wss://relay.esempio.com @@ -234,7 +230,6 @@ Indietro - Attiva/Disattiva Tor Attiva/Disattiva tema Mostra codice QR Indirizzo Lightning @@ -638,10 +633,6 @@ Crea account Accedi - Connessione a Tor… - Connesso tramite Tor - Errore Tor - Connetti tramite Tor Online ora %d online nella tua rete %d online su tutto Nostr diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8dd367aa..9ae7a78b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -60,10 +60,6 @@ サイナーでログイン または鍵でログイン nsecまたはnpub… - Tor経由で接続 - Torに接続中… - Tor経由で接続済み - Torエラー wisp 投稿をスクロールするための小さなインターフェース 鍵を隠す @@ -161,7 +157,7 @@ バナーのURL NIP-05(例:user@domain.com) Lightningアドレス - wss://またはws://.onion + wss:// ユーザーとノートを検索 リレーを検索 wss://relay.example.com @@ -234,7 +230,6 @@ 戻る - Tor切り替え テーマ切り替え QRコードを表示 Lightningアドレス @@ -638,10 +633,6 @@ アカウント作成 ログイン - Torに接続中… - Tor経由で接続済み - Torエラー - Tor経由で接続 オンライン中 ネットワーク内で%d人がオンライン Nostr全体で%d人がオンライン diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 8516ca23..484d43e5 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -60,10 +60,6 @@ 서명으로 로그인 또는 키로 로그인 nsec 또는 npub… - Tor로 연결 - Tor 연결 중… - Tor로 연결됨 - Tor 오류 wisp 게시물을 스크롤하는 작은 인터페이스 키 숨기기 @@ -161,7 +157,7 @@ 배너 URL NIP-05 (예: user@domain.com) 라이트닝 주소 - wss:// 또는 ws://.onion + wss:// 사용자와 노트 검색 릴레이 검색 wss://relay.example.com @@ -234,7 +230,6 @@ 뒤로 - Tor 전환 테마 전환 QR 코드 표시 라이트닝 주소 @@ -638,10 +633,6 @@ 계정 만들기 로그인 - Tor 연결 중… - Tor로 연결됨 - Tor 오류 - Tor로 연결 현재 온라인 네트워크에서 %d명 온라인 Nostr 전체에서 %d명 온라인 diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 13f9da21..52d53f09 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -60,10 +60,6 @@ Inloggen met Signer Of log in met je sleutel nsec of npub… - Verbind via Tor - Verbinden met Tor… - Verbonden via Tor - Tor fout wisp een kleine interface om door posts te scrollen Sleutel verbergen @@ -161,7 +157,7 @@ URL Banner NIP-05 (bijv. gebruiker@domein.com) Lightning Adres - wss:// of ws://.onion + wss:// Gebruikers en notities zoeken Relay zoeken wss://relay.voorbeeld.com @@ -234,7 +230,6 @@ Terug - Tor Toggle Theme Toggle QR-code Tonen Lightning Adres @@ -638,10 +633,6 @@ Account maken Inloggen - Verbinden met Tor… - Verbonden via Tor - Tor-fout - Verbinden via Tor Nu online %d online in je netwerk %d online overal op Nostr diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 6b49a8c8..0f935b04 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -60,10 +60,6 @@ Entrar com Assinante Ou entre com sua chave nsec ou npub… - Conectar via Tor - Conectando ao Tor… - Conectado via Tor - Erro do Tor wisp uma pequena interface pararolar publicações Ocultar chave @@ -161,7 +157,7 @@ URL do Banner NIP-05 (ex. usuario@dominio.com) Endereço Lightning - wss:// ou ws://.onion + wss:// Pesquisar usuários e notas Pesquisar relay wss://relay.exemplo.com @@ -234,7 +230,6 @@ Voltar - Alternar Tor Alternar tema Mostrar código QR Endereço Lightning @@ -638,10 +633,6 @@ Criar conta Entrar - Conectando ao Tor… - Conectado via Tor - Erro Tor - Conectar via Tor Online agora %d online na sua rede %d online em todo o Nostr diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4e79d541..eab0ff45 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -53,10 +53,6 @@ Войти через подписчик Или войдите по ключу nsec или npub… - Подключиться через Tor - Подключение к Tor… - Подключено через Tor - Ошибка Tor wisp небольшой интерфейс для прокрутки постов Скрыть ключ @@ -150,7 +146,7 @@ URL баннера NIP-05 (напр. user@domain.com) Lightning-адрес - wss:// или ws://.onion + wss:// Искать пользователей и заметки Искать рилэй wss://relay.example.com @@ -217,7 +213,6 @@ +Набор Подробнее Назад - Переключить Tor Переключить тему Показать QR-код Lightning-адрес @@ -599,10 +594,6 @@ Создать аккаунт Войти - Подключение к Tor… - Подключено через Tor - Ошибка Tor - Подключиться через Tor Сейчас онлайн %d онлайн в вашей сети %d онлайн во всём Nostr diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9927a553..27e1f114 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -60,10 +60,6 @@ 使用签名器登录 或使用密钥登录 nsec 或 npub… - 通过 Tor 连接 - 正在连接 Tor… - 已通过 Tor 连接 - Tor 错误 wisp 滚动帖子的轻量界面 隐藏密钥 @@ -161,7 +157,7 @@ 横幅 URL NIP-05(例如:user@domain.com) 闪电地址 - wss:// 或 ws://.onion + wss:// 搜索用户和笔记 搜索中继 wss://relay.example.com @@ -234,7 +230,6 @@ 返回 - 切换 Tor 切换主题 显示二维码 闪电地址 @@ -638,10 +633,6 @@ 创建账户 登录 - 正在连接Tor… - 已通过Tor连接 - Tor错误 - 通过Tor连接 当前在线 你的网络中有%d人在线 整个Nostr有%d人在线 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b74ec569..5dacdffe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -45,10 +45,6 @@ Create Account Log In - Connecting to Tor… - Connected via Tor - Tor error - Connect via Tor %d people online now Online Now %d online in your network @@ -61,10 +57,6 @@ Login with Signer Or log in with your key nsec or npub… - Connect via Tor - Connecting to Tor… - Connected via Tor - Tor error wisp a wee interface to scroll posts Hide key @@ -165,7 +157,7 @@ Banner URL NIP-05 (e.g. user@domain.com) Lightning Address - wss:// or ws://.onion + wss:// Search users and notes Search relay wss://relay.example.com @@ -252,7 +244,6 @@ Back - Toggle Tor Toggle theme Show QR code Lightning address diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 16654478..00b2aa61 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -3,7 +3,7 @@ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e5f76af0..26454974 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,8 +14,6 @@ coroutines = "1.9.0" profileinstaller = "1.4.1" splashscreen = "1.0.1" zxing = "3.5.3" -kmp-tor = "2.0.0" -kmp-tor-resource = "408.16.4" mlkit-translate = "17.0.3" mlkit-language-id = "17.0.6" mlkit-barcode = "17.3.0" @@ -56,8 +54,6 @@ biometric = { group = "androidx.biometric", name = "biometric", version.ref = "b profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" } splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splashscreen" } zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing" } -kmp-tor-runtime = { group = "io.matthewnelson.kmp-tor", name = "runtime", version.ref = "kmp-tor" } -kmp-tor-resource-exec = { group = "io.matthewnelson.kmp-tor", name = "resource-exec-tor", version.ref = "kmp-tor-resource" } mlkit-translate = { group = "com.google.mlkit", name = "translate", version.ref = "mlkit-translate" } mlkit-language-id = { group = "com.google.mlkit", name = "language-id", version.ref = "mlkit-language-id" } mlkit-barcode = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "mlkit-barcode" }