Skip to content

Commit a64bf79

Browse files
authored
Merge pull request #5995 from element-hq/valere/rtc/voice_call
Support for Voice Call only (no video), parity with web
2 parents 73961b4 + 9504984 commit a64bf79

File tree

121 files changed

+598
-230
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+598
-230
lines changed

features/call/api/src/main/kotlin/io/element/android/features/call/api/CallType.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ sealed interface CallType : NodeInputs, Parcelable {
2626
data class RoomCall(
2727
val sessionId: SessionId,
2828
val roomId: RoomId,
29+
val isAudioCall: Boolean
2930
) : CallType {
3031
override fun toString(): String {
31-
return "RoomCall(sessionId=$sessionId, roomId=$roomId)"
32+
return "RoomCall(sessionId=$sessionId, roomId=$roomId, isAudioCall=$isAudioCall)"
3233
}
3334
}
3435
}

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/DefaultElementCallEntryPoint.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class DefaultElementCallEntryPoint(
5858
expirationTimestamp = expirationTimestamp,
5959
notificationChannelId = notificationChannelId,
6060
textContent = textContent,
61+
audioOnly = callType.isAudioCall
6162
)
6263
activeCallManager.registerIncomingCall(notificationData = incomingCallNotificationData)
6364
}

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/CallNotificationData.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ data class CallNotificationData(
2929
val textContent: String?,
3030
// Expiration timestamp in millis since epoch
3131
val expirationTimestamp: Long,
32+
val audioOnly: Boolean,
3233
) : Parcelable

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class RingingCallNotificationCreator(
6969
timestamp: Long,
7070
expirationTimestamp: Long,
7171
textContent: String?,
72+
audioOnly: Boolean,
7273
): Notification? {
7374
val matrixClient = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null
7475
val imageLoader = imageLoaderHolder.get(matrixClient)
@@ -88,7 +89,7 @@ class RingingCallNotificationCreator(
8889
.setImportant(true)
8990
.build()
9091

91-
val answerIntent = IntentProvider.getPendingIntent(context, CallType.RoomCall(sessionId, roomId))
92+
val answerIntent = IntentProvider.getPendingIntent(context, CallType.RoomCall(sessionId, roomId, isAudioCall = audioOnly))
9293
val notificationData = CallNotificationData(
9394
sessionId = sessionId,
9495
roomId = roomId,
@@ -101,6 +102,7 @@ class RingingCallNotificationCreator(
101102
timestamp = timestamp,
102103
textContent = textContent,
103104
expirationTimestamp = expirationTimestamp,
105+
audioOnly = audioOnly,
104106
)
105107

106108
val declineIntent = PendingIntentCompat.getBroadcast(
@@ -127,7 +129,11 @@ class RingingCallNotificationCreator(
127129
.setSmallIcon(CommonDrawables.ic_notification)
128130
.setPriority(NotificationCompat.PRIORITY_MAX)
129131
.setCategory(NotificationCompat.CATEGORY_CALL)
130-
.setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, declineIntent, answerIntent).setIsVideo(true))
132+
.setStyle(
133+
NotificationCompat.CallStyle
134+
.forIncomingCall(caller, declineIntent, answerIntent)
135+
.setIsVideo(!audioOnly)
136+
)
131137
.addPerson(caller)
132138
.setAutoCancel(true)
133139
.setWhen(timestamp)

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/receivers/DeclineCallBroadcastReceiver.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ class DeclineCallBroadcastReceiver : BroadcastReceiver() {
4545
callType = CallType.RoomCall(
4646
sessionId = notificationData.sessionId,
4747
roomId = notificationData.roomId,
48-
),
49-
notificationData = notificationData,
48+
isAudioCall = notificationData.audioOnly
49+
)
5050
)
5151
}
5252
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2026 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.call.impl.ui
9+
10+
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
11+
import io.element.android.features.call.impl.notifications.CallNotificationData
12+
import io.element.android.libraries.matrix.api.core.EventId
13+
import io.element.android.libraries.matrix.api.core.RoomId
14+
import io.element.android.libraries.matrix.api.core.SessionId
15+
import io.element.android.libraries.matrix.api.core.UserId
16+
17+
open class CallNotificationDataProvider : PreviewParameterProvider<CallNotificationData> {
18+
override val values: Sequence<CallNotificationData>
19+
get() = sequenceOf(
20+
aCallNotificationData(
21+
audioOnly = false
22+
),
23+
aCallNotificationData(
24+
audioOnly = true
25+
),
26+
)
27+
}
28+
29+
internal fun aCallNotificationData(
30+
audioOnly: Boolean
31+
): CallNotificationData {
32+
return CallNotificationData(
33+
sessionId = SessionId("@alice:matrix.org"),
34+
roomId = RoomId("!1234:matrix.org"),
35+
eventId = EventId("\$asdadadsad:matrix.org"),
36+
senderId = UserId("@bob:matrix.org"),
37+
roomName = "A room",
38+
senderName = "Bob",
39+
avatarUrl = null,
40+
notificationChannelId = "incoming_call",
41+
timestamp = 0L,
42+
textContent = null,
43+
expirationTimestamp = 1000L,
44+
audioOnly = audioOnly
45+
)
46+
}

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class CallScreenPresenter(
226226
sessionId = inputs.sessionId,
227227
roomId = inputs.roomId,
228228
clientId = UUID.randomUUID().toString(),
229+
isAudioCall = inputs.isAudioCall,
229230
languageTag = languageTag,
230231
theme = theme,
231232
).getOrThrow()

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,13 @@ class IncomingCallActivity : AppCompatActivity() {
112112
}
113113

114114
private fun onAnswer(notificationData: CallNotificationData) {
115-
elementCallEntryPoint.startCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
115+
elementCallEntryPoint.startCall(
116+
CallType.RoomCall(
117+
notificationData.sessionId,
118+
notificationData.roomId,
119+
isAudioCall = notificationData.audioOnly
120+
)
121+
)
116122
}
117123

118124
private fun onCancel() {

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallScreen.kt

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
3030
import androidx.compose.ui.res.stringResource
3131
import androidx.compose.ui.text.style.TextAlign
3232
import androidx.compose.ui.text.style.TextOverflow
33+
import androidx.compose.ui.tooling.preview.PreviewParameter
3334
import androidx.compose.ui.unit.Dp
3435
import androidx.compose.ui.unit.dp
3536
import io.element.android.compound.theme.ElementTheme
@@ -45,10 +46,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
4546
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
4647
import io.element.android.libraries.designsystem.theme.components.Icon
4748
import io.element.android.libraries.designsystem.theme.components.Text
48-
import io.element.android.libraries.matrix.api.core.EventId
49-
import io.element.android.libraries.matrix.api.core.RoomId
50-
import io.element.android.libraries.matrix.api.core.SessionId
51-
import io.element.android.libraries.matrix.api.core.UserId
5249
import io.element.android.libraries.ui.strings.CommonStrings
5350

5451
/**
@@ -103,7 +100,7 @@ internal fun IncomingCallScreen(
103100
ActionButton(
104101
size = 64.dp,
105102
onClick = { onAnswer(notificationData) },
106-
icon = CompoundIcons.VoiceCallSolid(),
103+
icon = if (notificationData.audioOnly) CompoundIcons.VoiceCallSolid() else CompoundIcons.VideoCallSolid(),
107104
title = stringResource(CommonStrings.action_accept),
108105
backgroundColor = ElementTheme.colors.iconSuccessPrimary,
109106
borderColor = ElementTheme.colors.borderSuccessSubtle
@@ -163,21 +160,11 @@ private fun ActionButton(
163160

164161
@PreviewsDayNight
165162
@Composable
166-
internal fun IncomingCallScreenPreview() = ElementPreview {
163+
internal fun IncomingCallScreenPreview(
164+
@PreviewParameter(CallNotificationDataProvider::class) state: CallNotificationData,
165+
) = ElementPreview {
167166
IncomingCallScreen(
168-
notificationData = CallNotificationData(
169-
sessionId = SessionId("@alice:matrix.org"),
170-
roomId = RoomId("!1234:matrix.org"),
171-
eventId = EventId("\$asdadadsad:matrix.org"),
172-
senderId = UserId("@bob:matrix.org"),
173-
roomName = "A room",
174-
senderName = "Bob",
175-
avatarUrl = null,
176-
notificationChannelId = "incoming_call",
177-
timestamp = 0L,
178-
textContent = null,
179-
expirationTimestamp = 1000L,
180-
),
167+
notificationData = state,
181168
onAnswer = {},
182169
onCancel = {},
183170
)

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class DefaultActiveCallManager(
146146
callType = CallType.RoomCall(
147147
sessionId = notificationData.sessionId,
148148
roomId = notificationData.roomId,
149+
isAudioCall = notificationData.audioOnly,
149150
),
150151
callState = CallState.Ringing(notificationData),
151152
)
@@ -273,6 +274,7 @@ class DefaultActiveCallManager(
273274
timestamp = notificationData.timestamp,
274275
textContent = notificationData.textContent,
275276
expirationTimestamp = notificationData.expirationTimestamp,
277+
audioOnly = notificationData.audioOnly,
276278
) ?: return
277279
runCatchingExceptions {
278280
notificationManagerCompat.notify(

0 commit comments

Comments
 (0)