diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ai/MockWritingAssistant.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ai/MockWritingAssistant.kt index 8945613fe..80b547a09 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ai/MockWritingAssistant.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ai/MockWritingAssistant.kt @@ -93,5 +93,7 @@ class MockWritingAssistant : WritingAssistant { .replace("wont ", "won't ") .replace("i ", "I ") - override fun close() {} + override fun close() { + // no-op: mock holds no native handles or background workers to release. + } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt index 423656ec0..142a3dee6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt @@ -113,7 +113,9 @@ class CallAudioManager( override fun onAccuracyChanged( sensor: Sensor?, accuracy: Int, - ) {} + ) { + // no-op: proximity is a binary near/far signal; accuracy changes don't affect routing. + } } fun startRinging() { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt index b262114f5..d9d338811 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt @@ -74,7 +74,9 @@ class WebRtcCallSession( candidate?.let { this@WebRtcCallSession.onIceCandidate(it) } } - override fun onIceCandidatesRemoved(candidates: Array?) {} + override fun onIceCandidatesRemoved(candidates: Array?) { + // no-op: removed candidates are diagnostic only; ICE state transitions handle reconnects. + } override fun onSignalingChange(state: PeerConnection.SignalingState?) { Log.d(TAG) { "Signaling state changed: $state" } @@ -137,9 +139,13 @@ class WebRtcCallSession( stream?.videoTracks?.firstOrNull()?.let { onRemoteVideoTrack(it) } } - override fun onRemoveStream(stream: MediaStream?) {} + override fun onRemoveStream(stream: MediaStream?) { + // no-op: Plan-B legacy callback. UNIFIED_PLAN delivers track-level events via onAddTrack instead. + } - override fun onDataChannel(channel: DataChannel?) {} + override fun onDataChannel(channel: DataChannel?) { + // no-op: this session uses audio/video tracks only; data channels are not negotiated. + } override fun onRenegotiationNeeded() { Log.d(TAG) { "Renegotiation needed" } @@ -209,9 +215,13 @@ class WebRtcCallSession( error?.let { onError("Create offer failed: $it") } } - override fun onSetSuccess() {} + override fun onSetSuccess() { + // no-op: createOffer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } - override fun onSetFailure(error: String?) {} + override fun onSetFailure(error: String?) { + // no-op: createOffer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } }, constraints, ) @@ -239,9 +249,13 @@ class WebRtcCallSession( error?.let { onError("Create answer failed: $it") } } - override fun onSetSuccess() {} + override fun onSetSuccess() { + // no-op: createAnswer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } - override fun onSetFailure(error: String?) {} + override fun onSetFailure(error: String?) { + // no-op: createAnswer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } }, constraints, ) @@ -251,9 +265,13 @@ class WebRtcCallSession( Log.d(TAG) { "setRemoteDescription type=${sdp.type} sdpLength=${sdp.description.length}" } peerConnection?.setRemoteDescription( object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) {} + override fun onCreateSuccess(sdp: SessionDescription?) { + // no-op: setRemoteDescription fires only onSet*. + } - override fun onCreateFailure(error: String?) {} + override fun onCreateFailure(error: String?) { + // no-op: setRemoteDescription fires only onSet*. + } override fun onSetSuccess() { Log.d(TAG) { "setRemoteDescription SUCCESS (type=${sdp.type})" } @@ -301,9 +319,13 @@ class WebRtcCallSession( onDisconnected() } - override fun onSetSuccess() {} + override fun onSetSuccess() { + // no-op: createOffer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } - override fun onSetFailure(error: String?) {} + override fun onSetFailure(error: String?) { + // no-op: createOffer fires only onCreate*; setLocalDescription uses loggingSdpObserver. + } }, constraints, ) @@ -328,9 +350,13 @@ class WebRtcCallSession( val rollbackSdp = SessionDescription(SessionDescription.Type.ROLLBACK, "") peerConnection?.setLocalDescription( object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) {} + override fun onCreateSuccess(sdp: SessionDescription?) { + // no-op: setLocalDescription fires only onSet*. + } - override fun onCreateFailure(error: String?) {} + override fun onCreateFailure(error: String?) { + // no-op: setLocalDescription fires only onSet*. + } override fun onSetSuccess() { Log.d(TAG) { "Rollback SUCCESS" } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreenPreviews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreenPreviews.kt index 99c9d9708..27e519daf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreenPreviews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreenPreviews.kt @@ -48,6 +48,9 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn +private const val LABEL_HANG_UP = "Hang up" +private const val LABEL_CALLING = "Calling…" + // ---- Shared building blocks for previews ---- @Composable @@ -172,7 +175,7 @@ private fun PreviewCallControls( shape = CircleShape, modifier = Modifier.size(64.dp), ) { - Icon(MaterialSymbols.CallEnd, "Hang up", tint = Color.White, modifier = Modifier.size(32.dp)) + Icon(MaterialSymbols.CallEnd, LABEL_HANG_UP, tint = Color.White, modifier = Modifier.size(32.dp)) } } } @@ -207,7 +210,7 @@ private fun PreviewCallInProgress( shape = CircleShape, modifier = Modifier.size(64.dp), ) { - Icon(MaterialSymbols.CallEnd, "Hang up", tint = Color.White, modifier = Modifier.size(32.dp)) + Icon(MaterialSymbols.CallEnd, LABEL_HANG_UP, tint = Color.White, modifier = Modifier.size(32.dp)) } } } @@ -217,7 +220,7 @@ private fun PreviewCallInProgress( @Composable fun PreviewCallingScreen() { ThemeComparisonColumn { - PreviewCallInProgress("Alice", "Calling\u2026") + PreviewCallInProgress("Alice", LABEL_CALLING) } } @@ -242,7 +245,7 @@ fun PreviewCallingGroupScreen() { Spacer(modifier = Modifier.height(16.dp)) Text("Alice, Bob +1", fontWeight = FontWeight.Bold, fontSize = 20.sp) Spacer(modifier = Modifier.height(8.dp)) - Text("Calling\u2026", color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 16.sp) + Text(LABEL_CALLING, color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 16.sp) Spacer(modifier = Modifier.height(48.dp)) FloatingActionButton( onClick = {}, @@ -250,7 +253,7 @@ fun PreviewCallingGroupScreen() { shape = CircleShape, modifier = Modifier.size(64.dp), ) { - Icon(MaterialSymbols.CallEnd, "Hang up", tint = Color.White, modifier = Modifier.size(32.dp)) + Icon(MaterialSymbols.CallEnd, LABEL_HANG_UP, tint = Color.White, modifier = Modifier.size(32.dp)) } } } @@ -580,7 +583,7 @@ fun PreviewPipCallUI() { ) { Icon(MaterialSymbols.Person, contentDescription = null, modifier = Modifier.size(48.dp)) Spacer(modifier = Modifier.height(4.dp)) - Text("Calling\u2026", fontSize = 10.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text(LABEL_CALLING, fontSize = 10.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/BouncingIntentNav.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/BouncingIntentNav.kt index 3ea057b81..d1937f06d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/BouncingIntentNav.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/BouncingIntentNav.kt @@ -39,6 +39,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass +private const val NOSTR_URI_PREFIX = "nostr:" + /** * INav for screens that live inside a free-standing Activity (no Compose * NavHost in scope) and need to dispatch navigation requests back to @@ -127,31 +129,31 @@ class BouncingIntentNav( private fun routeToBouncingUri(route: Route): String? = when (route) { is Route.Profile -> { - "nostr:" + NPub.create(route.id) + NOSTR_URI_PREFIX + NPub.create(route.id) } is Route.Note -> { - "nostr:" + NEvent.create(route.id, null, null, null) + NOSTR_URI_PREFIX + NEvent.create(route.id, null, null, null) } is Route.EventRedirect -> { - "nostr:" + NEvent.create(route.id, null, null, null) + NOSTR_URI_PREFIX + NEvent.create(route.id, null, null, null) } is Route.Hashtag -> { - "nostr:hashtag?id=" + route.hashtag + NOSTR_URI_PREFIX + "hashtag?id=" + route.hashtag } is Route.LiveActivityChannel -> { - "nostr:" + NAddress.create(route.kind, route.pubKeyHex, route.dTag, null) + NOSTR_URI_PREFIX + NAddress.create(route.kind, route.pubKeyHex, route.dTag, null) } is Route.Community -> { - "nostr:" + NAddress.create(route.kind, route.pubKeyHex, route.dTag, null) + NOSTR_URI_PREFIX + NAddress.create(route.kind, route.pubKeyHex, route.dTag, null) } is Route.PublicChatChannel -> { - "nostr:" + NEvent.create(route.id, null, ChannelCreateEvent.KIND, null) + NOSTR_URI_PREFIX + NEvent.create(route.id, null, ChannelCreateEvent.KIND, null) } else -> { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/EmptyNav.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/EmptyNav.kt index 94c3f86a8..ddea6f7f5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/EmptyNav.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/EmptyNav.kt @@ -39,21 +39,33 @@ class EmptyNav : INav { override fun openDrawer() = runBlocking { drawerState.open() } // All navigation methods are intentionally no-op; this is a stub for previews and tests - override fun nav(route: Route) {} + override fun nav(route: Route) { + // no-op: EmptyNav is a preview/test stub with no navigation host. + } - override fun nav(computeRoute: suspend () -> Route?) {} + override fun nav(computeRoute: suspend () -> Route?) { + // no-op: EmptyNav is a preview/test stub with no navigation host. + } - override fun newStack(route: Route) {} + override fun newStack(route: Route) { + // no-op: EmptyNav is a preview/test stub with no back stack to reset. + } - override fun navBottomBar(route: Route) {} + override fun navBottomBar(route: Route) { + // no-op: EmptyNav is a preview/test stub with no bottom-bar host. + } @Composable override fun canPop(): Boolean = false - override fun popBack() {} + override fun popBack() { + // no-op: EmptyNav is a preview/test stub with no back stack to pop. + } override fun popUpTo( route: Route, klass: KClass, - ) {} + ) { + // no-op: EmptyNav is a preview/test stub with no back stack. + } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt index 28eaac2d2..aebdf4308 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt @@ -179,12 +179,16 @@ interface EventHandler { event: T, eventNote: Note, publicNote: Note, - ) {} + ) { + // no-op default: handlers that only care about deletes (or only adds) skip overriding this. + } suspend fun delete( event: T, eventNote: Note, - ) {} + ) { + // no-op default: handlers that only care about adds (or only deletes) skip overriding this. + } } class ChatHandler( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt index 35784afd4..a1961d9c6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt @@ -44,11 +44,16 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +private const val SAMPLE_PUBKEY_FRAGMENT = + "\"pubkey\":\"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c\"," + +private const val SAMPLE_KIND_20_FRAGMENT = "\"kind\":20," + private const val SAMPLE_PICTURE_EVENT_JSON = "{\"id\":\"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\"," + - "\"pubkey\":\"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c\"," + + SAMPLE_PUBKEY_FRAGMENT + "\"created_at\":1708695717," + - "\"kind\":20," + + SAMPLE_KIND_20_FRAGMENT + "\"tags\":[[\"title\",\"Sunset at the Beach\"]," + "[\"imeta\",\"url https://image.nostr.build/sample-sunset.jpg\"," + "\"m image/jpeg\",\"dim 1200x800\",\"alt A beautiful sunset over the ocean\"," + @@ -58,9 +63,9 @@ private const val SAMPLE_PICTURE_EVENT_JSON = private const val SAMPLE_PICTURE_EVENT_NO_TITLE_JSON = "{\"id\":\"b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3\"," + - "\"pubkey\":\"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c\"," + + SAMPLE_PUBKEY_FRAGMENT + "\"created_at\":1708695000," + - "\"kind\":20," + + SAMPLE_KIND_20_FRAGMENT + "\"tags\":[[\"imeta\",\"url https://image.nostr.build/sample-mountain.jpg\"," + "\"m image/jpeg\",\"dim 1080x1080\",\"alt Mountain landscape\"]]," + "\"content\":\"Mountain vibes today. Fresh air and clear skies.\"," + @@ -68,9 +73,9 @@ private const val SAMPLE_PICTURE_EVENT_NO_TITLE_JSON = private const val SAMPLE_MULTI_IMAGE_EVENT_JSON = "{\"id\":\"c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4\"," + - "\"pubkey\":\"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c\"," + + SAMPLE_PUBKEY_FRAGMENT + "\"created_at\":1708694000," + - "\"kind\":20," + + SAMPLE_KIND_20_FRAGMENT + "\"tags\":[[\"title\",\"Travel Photos\"]," + "[\"imeta\",\"url https://image.nostr.build/sample-travel1.jpg\"," + "\"m image/jpeg\",\"dim 800x600\",\"alt City street\"]," + diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt index b51eb899c..e14ca5c66 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt @@ -98,9 +98,16 @@ fun RelayUrlEditFieldPreview() { override val results: Flow> = MutableStateFlow(suggestions) - override fun processInput(input: String) {} + override fun processInput(input: String) { + // No-op: this anonymous IRelaySuggestionState is preview-only and + // serves the static suggestions flow above. Live input filtering + // would only matter in an interactive run, which a @Preview never + // reaches. + } - override fun reset() {} + override fun reset() { + // No-op: see processInput above. + } } } diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/Output.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/Output.kt index 634ca29e1..60b27319a 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/Output.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/Output.kt @@ -44,6 +44,8 @@ import java.time.format.DateTimeFormatter * - paints the result with ANSI colour when stdout is a TTY (disabled * by `NO_COLOR`, forced on by `CLICOLOR_FORCE`). */ +private const val EMPTY_LABEL = "(empty)" + object Output { enum class Mode { TEXT, JSON } @@ -137,7 +139,7 @@ object Output { .append(coloredKey) .append(":") .append(padding) - out.append(' ').append(color.dim("(empty)")).append('\n') + out.append(' ').append(color.dim(EMPTY_LABEL)).append('\n') } else { out.append(prefix).append(coloredKey).append(":\n") renderMapBody(out, v, "$prefix ", color) @@ -190,7 +192,7 @@ object Output { .append(prefix) .append(dash) .append(' ') - .append(color.dim("(empty)")) + .append(color.dim(EMPTY_LABEL)) .append('\n') continue } @@ -224,7 +226,7 @@ object Output { .append(':') .append(rPad) .append(' ') - .append(color.dim("(empty)")) + .append(color.dim(EMPTY_LABEL)) .append('\n') } else { out.append("$prefix ").append(color.bold(rKey)).append(":\n") diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/secrets/SecretStore.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/secrets/SecretStore.kt index ef1f57e11..12a830daf 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/secrets/SecretStore.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/secrets/SecretStore.kt @@ -211,6 +211,7 @@ internal object MacosKeychainBackend : SecretBackend { internal object SecretServiceBackend : SecretBackend { const val BACKEND_ID = "secret-service" private const val SERVICE_ATTR = "amy-nostr" + private const val SECRET_TOOL_BIN = "secret-tool" override val name: String = "keychain:$BACKEND_ID" @@ -224,7 +225,7 @@ internal object SecretServiceBackend : SecretBackend { ) { return false } - return which("secret-tool") != null + return which(SECRET_TOOL_BIN) != null } override fun store( @@ -233,7 +234,7 @@ internal object SecretServiceBackend : SecretBackend { ): IdentitySecret { val res = runProc( - "secret-tool", + SECRET_TOOL_BIN, "store", "--label=amy Nostr identity ${pubKeyHex.take(8)}", "service", @@ -250,7 +251,7 @@ internal object SecretServiceBackend : SecretBackend { override fun resolve(secret: IdentitySecret): String { require(secret is IdentitySecret.Keychain) - val res = runProc("secret-tool", "lookup", "service", secret.service, "account", secret.account) + val res = runProc(SECRET_TOOL_BIN, "lookup", "service", secret.service, "account", secret.account) if (res.exit != 0 || res.stdout.isBlank()) { throw RuntimeException("secret-tool lookup failed (exit=${res.exit}): ${res.stderr.trim()}") } @@ -259,7 +260,7 @@ internal object SecretServiceBackend : SecretBackend { override fun delete(secret: IdentitySecret) { require(secret is IdentitySecret.Keychain) - runProc("secret-tool", "clear", "service", secret.service, "account", secret.account) + runProc(SECRET_TOOL_BIN, "clear", "service", secret.service, "account", secret.account) } private fun which(cmd: String): File? {