Support incoming audio only calls

This commit is contained in:
Valere 2026-03-04 08:56:33 +01:00
parent 5491040ac5
commit 7ef43abd57
22 changed files with 156 additions and 53 deletions

View file

@ -56,6 +56,7 @@ sealed interface NotificationContent {
data class RtcNotification(
val senderId: UserId,
val type: RtcNotificationType,
val callIntent: CallIntent,
val expirationTimestampMillis: Long
) : MessageLike
@ -127,3 +128,8 @@ enum class RtcNotificationType {
RING,
NOTIFY
}
enum class CallIntent {
AUDIO,
VIDEO
}

View file

@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.notification
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.notification.CallIntent
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.RtcNotificationType
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
@ -20,6 +21,7 @@ import org.matrix.rustcomponents.sdk.StateEventContent
import org.matrix.rustcomponents.sdk.TimelineEvent
import org.matrix.rustcomponents.sdk.TimelineEventContent
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.RtcCallIntent as SdkRtcCallIntent
import org.matrix.rustcomponents.sdk.RtcNotificationType as SdkRtcNotificationType
class TimelineEventToNotificationContentMapper {
@ -83,6 +85,7 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon
is MessageLikeEventContent.RtcNotification -> NotificationContent.MessageLike.RtcNotification(
senderId = senderId,
type = notificationType.map(),
callIntent = callIntent.map(),
expirationTimestampMillis = expirationTs.toLong()
)
MessageLikeEventContent.KeyVerificationAccept -> NotificationContent.MessageLike.KeyVerificationAccept
@ -111,3 +114,8 @@ private fun SdkRtcNotificationType.map(): RtcNotificationType = when (this) {
SdkRtcNotificationType.NOTIFICATION -> RtcNotificationType.NOTIFY
SdkRtcNotificationType.RING -> RtcNotificationType.RING
}
private fun SdkRtcCallIntent?.map(): CallIntent = when (this) {
SdkRtcCallIntent.AUDIO -> CallIntent.AUDIO
else -> CallIntent.VIDEO
}

View file

@ -14,8 +14,8 @@ import org.matrix.rustcomponents.sdk.AccountManagementAction as RustAccountManag
fun AccountManagementAction.toRustAction(): RustAccountManagementAction {
return when (this) {
AccountManagementAction.Profile -> RustAccountManagementAction.Profile
is AccountManagementAction.SessionEnd -> RustAccountManagementAction.SessionEnd(deviceId.value)
is AccountManagementAction.SessionView -> RustAccountManagementAction.SessionView(deviceId.value)
AccountManagementAction.SessionsList -> RustAccountManagementAction.SessionsList
is AccountManagementAction.SessionEnd -> RustAccountManagementAction.DeviceDelete(deviceId.value)
is AccountManagementAction.SessionView -> RustAccountManagementAction.DeviceView(deviceId.value)
AccountManagementAction.SessionsList -> RustAccountManagementAction.DevicesList
}
}

View file

@ -14,6 +14,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.exception.NotificationResolverException
import io.element.android.libraries.matrix.api.notification.CallIntent
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.notification.RtcNotificationType
@ -90,6 +91,7 @@ class DefaultCallNotificationEventResolver(
notificationData.run {
if (content.type == RtcNotificationType.RING && isRoomCallActive && !forceNotify) {
Timber.d("Ringing call notification intent ${content.callIntent} in room $roomId")
NotifiableRingingCallEvent(
sessionId = sessionId,
roomId = roomId,
@ -100,10 +102,18 @@ class DefaultCallNotificationEventResolver(
timestamp = this.timestamp,
isRedacted = false,
isUpdated = false,
description = stringProvider.getString(R.string.notification_incoming_call),
description = if (content.callIntent ==
CallIntent.AUDIO) {
stringProvider.getString(R.string.notification_incoming_audio_call)
} else {
stringProvider.getString(
R.string.notification_incoming_call
)
},
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
roomAvatarUrl = roomAvatarUrl,
rtcNotificationType = content.type,
callIntent = content.callIntent,
senderId = content.senderId,
senderAvatarUrl = senderAvatarUrl,
expirationTimestamp = content.expirationTimestampMillis,
@ -119,7 +129,11 @@ class DefaultCallNotificationEventResolver(
noisy = true,
timestamp = this.timestamp,
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = stringProvider.getString(R.string.notification_incoming_call),
body = if (content.callIntent == CallIntent.VIDEO) {
stringProvider.getString(R.string.notification_incoming_call)
} else {
stringProvider.getString(R.string.notification_incoming_audio_call)
},
roomName = roomDisplayName,
roomIsDm = isDm,
roomAvatarPath = roomAvatarUrl,

View file

@ -12,6 +12,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.notification.CallIntent
import io.element.android.libraries.matrix.api.notification.RtcNotificationType
data class NotifiableRingingCallEvent(
@ -29,6 +30,7 @@ data class NotifiableRingingCallEvent(
val senderAvatarUrl: String?,
val roomAvatarUrl: String? = null,
val rtcNotificationType: RtcNotificationType,
val callIntent: CallIntent,
val timestamp: Long,
val expirationTimestamp: Long,
) : NotifiableEvent

View file

@ -19,6 +19,7 @@ import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.exception.NotificationResolverException
import io.element.android.libraries.matrix.api.notification.CallIntent
import io.element.android.libraries.push.api.push.NotificationEventRequest
import io.element.android.libraries.push.api.push.SyncOnNotifiableEvent
import io.element.android.libraries.push.impl.history.PushHistoryService
@ -303,8 +304,7 @@ class DefaultPushHandler(
callType = CallType.RoomCall(
notifiableEvent.sessionId,
notifiableEvent.roomId,
// TODO
voiceIntent = false
voiceIntent = notifiableEvent.callIntent == CallIntent.AUDIO
),
eventId = notifiableEvent.eventId,
senderId = notifiableEvent.senderId,

View file

@ -19,6 +19,7 @@
<item quantity="one">"You have %d new message."</item>
<item quantity="other">"You have %d new messages."</item>
</plurals>
<string name="notification_incoming_audio_call">"📞 Incoming call"</string>
<string name="notification_incoming_call">"📹 Incoming call"</string>
<string name="notification_inline_reply_failed">"** Failed to send - please open room"</string>
<string name="notification_invitation_action_join">"Join"</string>