Commit Graph

10653 Commits

Author SHA1 Message Date
Claude 4e2143641f feat: render group member pictures in call UI, capped at 4 + remaining count
- Add GroupCallPictures composable showing up to 4 user avatars in
  a grid layout (same pattern as NonClickableUserPictures in chat)
- When >4 members, show 3 avatars + a "+N" badge in the 4th slot
- Add GroupCallNames composable showing up to 2 names + "+N" remaining
- Update CallInProgressUI, IncomingCallUI, ConnectedCallUI, PipCallUI,
  and PipConnectedCallUI to pass full peerPubKeys/groupMembers sets
  instead of only .first()

https://claude.ai/code/session_01LR8NmFGdMoDTVcfKjL7HWR
2026-04-03 00:22:37 +00:00
Claude 6946a5a263 fix: add missing call permissions for all supported Android versions
- Add MODIFY_AUDIO_SETTINGS for audio routing (speaker, earpiece, BT SCO)
- Add BLUETOOTH permission for API 26-30 (maxSdkVersion=30)
- Request BLUETOOTH_CONNECT at runtime on API 31+ for BT audio
- Add FOREGROUND_SERVICE_CAMERA for video calls on API 34+
- Update foreground service type to microphone|camera for video calls
- Guard Bluetooth operations with permission check in CallAudioManager
- Add uses-feature for microphone with required=false

https://claude.ai/code/session_01LR8NmFGdMoDTVcfKjL7HWR
2026-04-03 00:13:55 +00:00
Vitor Pamplona 70b205326c Merge pull request #2087 from vitorpamplona/claude/enable-group-calls-hv7v8
Add group call support to WebRTC call system
2026-04-02 20:00:26 -04:00
Claude dca38f7e01 fix: guard foreground service start against missing RECORD_AUDIO permission
On SDK 36 starting a foreground service with type microphone requires
RECORD_AUDIO to be granted at the moment startForeground() is called.
When the permission isn't available (e.g. revoked between call start
and peer connection) the service now falls back to a generic foreground
type instead of crashing with a SecurityException.

https://claude.ai/code/session_01WafqMofFQfWCQA5TcLvHRb
2026-04-02 23:56:04 +00:00
Claude 5b7e47d87e feat: add call buttons to group chat header
Adds voice and video call buttons to the group DM top bar, matching
the existing 1-on-1 DM header.  When tapped in a group chat, the
buttons invoke initiateGroupCall() which sends gift-wrapped offers
to every group member under a single call-id.

https://claude.ai/code/session_01WafqMofFQfWCQA5TcLvHRb
2026-04-02 22:30:50 +00:00
Claude f25c1df0e2 feat: enable group calls via gift wraps to each group member
Extends the NIP-AC P2P call signaling to support group calls by
sending individual gift-wrapped offers to each member of the group,
all sharing the same call-id.

- CallOfferEvent: add multi-pubkey build overload, groupMembers(),
  isGroupCall(), and recipientPubKeys() helpers
- WebRtcCallFactory: add GroupResult type, createGroupCallOffer(),
  createGroupHangup(), and createGroupReject() methods
- CallState: change peerPubKey to peerPubKeys (Set<HexKey>) across
  Offering, Connecting, Connected, and Ended states; add groupMembers
  field to IncomingCall
- CallManager: add initiateGroupCall() that creates a single signed
  offer with p tags for every callee and gift-wraps it individually;
  hangup sends to all peers in a group call
- CallController: add initiateGroupCall() entry point
- Tests: add group call offer tests for p tags, call-id, call-type,
  and expiration

https://claude.ai/code/session_01WafqMofFQfWCQA5TcLvHRb
2026-04-02 22:13:48 +00:00
Vitor Pamplona c1ef8792e9 Merge pull request #2086 from vitorpamplona/claude/review-calls-nip-ac-YY1sj
NIP-AC: Document mid-call renegotiation and multi-device support
2026-04-02 17:49:58 -04:00
Vitor Pamplona 19115ee5de Merge pull request #2085 from vitorpamplona/claude/improve-video-quality-6JY0R
Improve video call quality with 720p resolution and bitrate limiting
2026-04-02 17:44:02 -04:00
Claude 2646d280a0 docs: update NIP-AC to document renegotiation answer flow and multi-device support
The code implements two protocol features not previously documented in
the NIP: (1) renegotiation responses use CallAnswer (kind 25051) to
complete the SDP handshake, and (2) self-addressed answer/reject events
notify other devices of the same user to stop ringing.

https://claude.ai/code/session_01XSjhzuVn8N4Q2hZAYMRGqM
2026-04-02 21:42:51 +00:00
Claude 07c78905e2 feat: improve video call quality from 480p to 720p
Increase camera capture resolution from 640x480 to 1280x720 at 30fps
and set a 1.5 Mbps max bitrate on the video sender for good 720p quality.

https://claude.ai/code/session_01Co48ZKvPT5GX3mAjGHkNza
2026-04-02 21:40:32 +00:00
Vitor Pamplona cea630e0fd Merge pull request #2084 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-02 17:30:26 -04:00
Crowdin Bot d9f13a34f5 New Crowdin translations by GitHub Action 2026-04-02 21:10:00 +00:00
Vitor Pamplona 15a3a028b7 Merge pull request #2083 from vitorpamplona/claude/callscreen-separate-activity-JfFYu
Implement Picture-in-Picture mode for call activity
2026-04-02 17:08:25 -04:00
Claude 989f10c6e2 fix: notification Accept opens CallActivity directly (Android 12+)
Android 12+ blocks starting activities from BroadcastReceivers used
as notification trampolines. The Accept action now uses
PendingIntent.getActivity to launch CallActivity directly with
EXTRA_ACCEPT_CALL. CallActivity.onCreate/onNewIntent checks this
extra and accepts the incoming call.

The Reject action remains as a BroadcastReceiver since it doesn't
need to start an activity.

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 21:01:31 +00:00
Claude 80151965c7 fix: critical lifecycle bugs found during code review
1. onStop() no longer hangs up call when user switches apps
   - Added wasInPipMode flag to track if the activity was ever in PiP
   - onStop only hangs up + finishes when PiP was dismissed (swiped away),
     not when the user simply presses Home from the full-screen call UI
   - Pressing Home from full-screen enters PiP via onUserLeaveHint

2. Removed triple-hangup from overlapping lifecycle methods
   - onPictureInPictureModeChanged now only updates UI state
   - onStop handles PiP dismissal only
   - onDestroy only cleans up the PiP receiver (hangup already handled
     by CallController's state collector on Ended)

3. CallNotificationReceiver guards against stale notifications
   - Returns early if callManager/callController are null
   - Only launches CallActivity if the call is still IncomingCall
   - Cancels notification before any other action to prevent re-taps

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:55:00 +00:00
Claude 724fd10405 feat: full-screen incoming call UI over lock screen
Enable proper full-screen call experience when the phone is locked
or the app is in the background:

- CallActivity shows over lock screen (setShowWhenLocked/turnScreenOn)
- Manifest adds showOnLockScreen and turnScreenOn attributes
- Remove setSilent(true) from notification — it was blocking the
  full-screen intent from triggering (channel silence is enough)
- Populate ActiveCallHolder eagerly in initCallController() so the
  full-screen intent can launch CallActivity even when the Compose
  UI is paused in the background

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:47:56 +00:00
Claude dbdd53c6c3 fix: load caller name and profile picture in call notification
Both CallController and EventNotificationConsumer now load the
caller's profile picture via Coil before showing the notification,
so the large icon displays the caller's avatar instead of being
empty. The caller name was already loaded from LocalCache but now
the bitmap accompanies it.

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:34:07 +00:00
Vitor Pamplona dff1692374 Merge pull request #2082 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-02 16:33:44 -04:00
Claude 7024994b9a fix: PiP aspect ratio matches remote video when available
- Track remote video aspect ratio in CallController via VideoFrame
  rotatedWidth/rotatedHeight
- PiP uses video's aspect ratio when remote video is active, falls
  back to 9:16 portrait for audio-only calls
- Observe isRemoteVideoActive to update PiP params when video
  starts/stops during a call

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:30:36 +00:00
Claude 47ce858481 fix: PiP portrait aspect ratio and PiP close (X) button hangup
- Restore Rational(9, 16) portrait aspect ratio for PiP window
- Add onStop handler: when PiP is dismissed via X button, hang up
  the call and finish the activity (mirrors PipVideoActivity pattern)
- onPictureInPictureModeChanged also hangs up if activity is finishing

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:27:00 +00:00
Crowdin Bot 23e9ffa6b2 New Crowdin translations by GitHub Action 2026-04-02 20:05:19 +00:00
Claude 4526a16ff9 fix: notification actions, PiP hangup, PiP compact UI, and PiP remote actions
1. Notification: silence channel sound (CallAudioManager handles ringtone),
   use CallNotificationReceiver for accept/reject actions instead of
   launching MainActivity
2. Cancel call notification when call is accepted (Connecting state)
3. PiP: hang up call when PiP is dismissed (activity destroyed)
4. PiP: add RemoteAction buttons (hangup, mute toggle) since Compose
   buttons are not interactive in PiP mode
5. PiP: show compact UI with smaller avatar (48dp) and smaller text
   when in picture-in-picture mode

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 20:04:16 +00:00
Vitor Pamplona b604e62f17 Merge pull request #2081 from vitorpamplona/claude/audit-context-activity-leaks-P3jg2
Fix resource leaks and improve coroutine lifecycle management
2026-04-02 16:03:44 -04:00
Vitor Pamplona ef61dd86c2 Merge pull request #2080 from vitorpamplona/claude/preload-bookmark-events-hq9nL
Preload bookmark events to improve scrolling performance
2026-04-02 15:42:56 -04:00
Claude 0c2a68b86e feat: preload bookmark events via EventFinderFilterAssembler
Subscribe all bookmark and pinned note IDs to the EventFinderFilterAssembler
when the bookmark screen opens, so events are fetched in bulk via a single
REQ rather than loading one-by-one as the user scrolls.

Applied to both BookmarkListScreen (new bookmarks + pinned notes) and
OldBookmarkListScreen (legacy bookmarks).

https://claude.ai/code/session_01C8gevBfB8vLDFoBW3hnPeU
2026-04-02 19:36:32 +00:00
Claude ede5706582 feat: move CallScreen to its own activity for independent PiP
Separate the call UI into a dedicated CallActivity so it can enter
Picture-in-Picture mode independently of the main activity, allowing
users to continue browsing the app during an active call.

- Add CallActivity with PiP support via onUserLeaveHint
- Add ActiveCallHolder singleton to share call state between activities
- Launch CallActivity from call buttons and incoming call observer
- Remove in-app nav route for ActiveCall (now a separate activity)
- Remove EnterPipOnLeave composable (activity handles PiP directly)

https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
2026-04-02 19:32:45 +00:00
Vitor Pamplona 72d4c5107c Merge pull request #2078 from vitorpamplona/claude/disable-video-near-ear-de4vB
Add proximity sensor support to auto-pause video during calls
2026-04-02 15:18:53 -04:00
Vitor Pamplona 7f64d4ad5a Merge pull request #2079 from vitorpamplona/claude/fix-pip-crash-eZBgt
Clamp PiP aspect ratio to Android system limits
2026-04-02 15:17:26 -04:00
Claude 293901a87e fix: add system bar insets to call screen elements
Call screen elements were too close to the screen edges because they
didn't account for status bar and navigation bar insets. Apply
WindowInsets.statusBars and WindowInsets.navigationBars padding to
all call screen layouts while keeping video backgrounds full-bleed.

https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
2026-04-02 19:16:50 +00:00
Claude 0f5cb10a2a fix: clamp PiP aspect ratio to Android's allowed range
Videos with extreme aspect ratios (e.g. very tall or very wide) cause
an IllegalArgumentException when entering PiP mode because Android
requires the ratio to be between ~0.4184 and 2.39. Clamp the ratio
before passing it to setAspectRatio.

https://claude.ai/code/session_018uqcACcXpLQxwzVFhePisJ
2026-04-02 19:12:48 +00:00
Claude 0a7577dc21 fix: show incoming call notification when app is in background
When the app is alive but backgrounded, the relay subscription delivers
the call event first, marking it as consumed. The push notification
path then skips showing the notification because the event is already
consumed. This left the phone ringing with no notification and no way
to bring the app to the foreground.

Now CallController shows the call notification directly when entering
IncomingCall state, regardless of how the event was received. The
notification's fullScreenIntent brings the app to the foreground.

https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
2026-04-02 19:09:40 +00:00
Claude cee43bda2e fix: use setCommunicationDevice API for audio routing on Android 12+
AudioManager.isSpeakerphoneOn is deprecated since API 31 and silently
ignored on modern devices. Switch to setCommunicationDevice() which
properly routes call audio to speaker, earpiece, or Bluetooth on
Android 12+, with fallback to the legacy API on older versions.

https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
2026-04-02 18:54:54 +00:00
Claude 0f533ea43a fix: guard against stale SDP answers in wrong signaling state
Our own renegotiation answers can echo back through the relay and
get processed as if from the peer. Check that the peer connection
is in HAVE_LOCAL_OFFER state before applying a remote answer SDP,
preventing the "Called in wrong state: stable" error.

https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
2026-04-02 18:42:43 +00:00
Claude ea96dc9fb9 feat: disable outgoing video when phone is near ear during calls
Uses the proximity sensor to detect when the phone is held to the ear
and automatically pauses the camera/video track. Video resumes when the
phone is moved away, but only if the user had video enabled.

https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
2026-04-02 18:35:59 +00:00
Vitor Pamplona 2cb88185e2 Merge pull request #2077 from vitorpamplona/claude/arti-proxy-port-change-fiNca
Implement Tor SOCKS port retry logic with dynamic port allocation
2026-04-02 14:16:49 -04:00
Claude 58df705be6 fix: change Arti SOCKS proxy default port to 17392 and retry on busy port
Change default port from 19050 to 17392 to avoid conflicts with other
Tor-using apps. When binding fails (address in use), increment the port
and retry up to 10 times before giving up.

https://claude.ai/code/session_01JEzjceY3ZaCHz5kL4DsKic
2026-04-02 18:13:18 +00:00
Vitor Pamplona 1d4778b4e0 Merge pull request #2076 from vitorpamplona/claude/fix-multi-device-call-ringing-m7FYV
Add multi-device call state synchronization
2026-04-02 14:11:34 -04:00
Claude 500928f1b3 fix: stop ringing on other devices when call is answered or rejected
When a user is logged in on multiple devices, all devices ring on an
incoming call. Previously, accepting or rejecting on one device left the
others still ringing.

Now, acceptCall() and rejectCall() also publish a self-addressed
gift-wrapped event (to the user's own pubkey) so other devices receive
it and transition out of the IncomingCall state. Added ANSWERED_ELSEWHERE
end reason and handling in onCallAnswered/onCallRejected for IncomingCall
state.

https://claude.ai/code/session_01X9juzyPYWqxRMCgDWEw8R1
2026-04-02 18:09:33 +00:00
Vitor Pamplona 13de5d5eee Merge pull request #2075 from vitorpamplona/claude/fix-video-freeze-on-share-stop-V8BX3
Add remote video activity monitoring to detect peer disconnections
2026-04-02 13:49:59 -04:00
Claude fe1a009fb4 fix: wire SDP renegotiation so video toggle works for remote peer
When toggling video mid-call, addVideoTrack triggers
onRenegotiationNeeded but the callback was empty — the remote peer
never received the updated SDP, so video never appeared on their side.

This wires up the full renegotiation flow using the existing
CallRenegotiateEvent (kind 25055):
- WebRtcCallSession: forward onRenegotiationNeeded to a callback
- CallController: create and send renegotiation offers, handle
  incoming renegotiation offers by creating answers
- CallManager: route CallRenegotiateEvent, allow CallAnswerEvent
  during Connected state for renegotiation answers

https://claude.ai/code/session_01WdmqktFBjXKB6PYRZ5b4FD
2026-04-02 17:05:10 +00:00
Vitor Pamplona 3d91fb75d4 Merge pull request #2074 from vitorpamplona/claude/add-ephemeral-giftwrap-event-PiIXY
Add support for NIP-59 Ephemeral Gift Wrap events (kind 21059)
2026-04-02 12:00:24 -04:00
Claude ec99373ae3 fix: show voice call UI when remote peer stops sharing video
When a peer disables their camera, the remote side was showing a frozen
last frame because the VideoTrack reference remained non-null. This adds
a VideoSink-based frame monitor that detects when frames stop arriving
and transitions the UI back to the voice call avatar view.

https://claude.ai/code/session_01WdmqktFBjXKB6PYRZ5b4FD
2026-04-02 15:58:03 +00:00
Vitor Pamplona 3ac271bf7b Merge pull request #2073 from vitorpamplona/claude/improve-markdown-typography-VZDUg
Improve markdown rendering with enhanced typography and styling
2026-04-02 11:55:46 -04:00
Claude 6116f45888 feat: add EphemeralGiftWrapEvent (kind 21059)
Adds an ephemeral variant of GiftWrap for transient encrypted messages
that relays don't need to persist. EphemeralGiftWrapEvent extends
GiftWrapEvent so all existing processing paths (decryption, routing,
notifications, call signaling) automatically handle it via type
hierarchy. Relay subscription filters are updated to request both kinds.

https://claude.ai/code/session_0157X96G6HLTzYxkdX9pyTSJ
2026-04-02 15:55:44 +00:00
Vitor Pamplona e33a1b600d Merge pull request #2072 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-02 11:31:25 -04:00
Crowdin Bot 94fc8dc38e New Crowdin translations by GitHub Action 2026-04-02 15:30:15 +00:00
Vitor Pamplona 90288a1c78 Merge pull request #2068 from vitorpamplona/claude/add-webrtc-calls-4kBSR
Add WebRTC-based peer-to-peer voice and video calling via NIP-AC
2026-04-02 11:28:23 -04:00
Claude a1b2c785aa fix: hide local video PiP when camera is disabled
When the user disables video, the SurfaceViewRenderer kept showing
the last captured frame (frozen). Now the local video PiP is only
rendered when isVideoEnabled is true — when camera is off, the PiP
disappears entirely revealing the black background.

https://claude.ai/code/session_017hZm7yu7CzmcQgZGSaqSXS
2026-04-02 14:55:36 +00:00
Vitor Pamplona b631c6b148 Merge pull request #2071 from vitorpamplona/claude/redesign-article-screen-GwS0M
Enhance long-form article editor with banner image and tags UI
2026-04-02 10:54:04 -04:00
Claude eb19fc69ae fix: disable slug field when editing an existing article
The d-tag is the article's identity in Nostr addressable events.
Changing it on edit would create a new article instead of updating
the existing one. The slug field is now disabled when isEditing is true.

https://claude.ai/code/session_011JuQbdA12WdPxC7aGvUBqJ
2026-04-02 14:50:51 +00:00