Merge pull request #6649 from element-hq/feature/valere/call/decline_timeline_rendering

feat: Update call started timeline item + declined support
This commit is contained in:
Valere Fedronic 2026-05-12 16:32:51 +02:00 committed by GitHub
commit 9ca0b9e898
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 360 additions and 56 deletions

View file

@ -54,6 +54,7 @@ class DefaultRoomLatestEventFormatter(
private val roomMembershipContentFormatter: RoomMembershipContentFormatter,
private val profileChangeContentFormatter: ProfileChangeContentFormatter,
private val stateContentFormatter: StateContentFormatter,
private val rtcNotificationContentFormatter: RtcNotificationContentFormatter,
private val permalinkParser: PermalinkParser,
) : RoomLatestEventFormatter {
override fun format(
@ -121,7 +122,7 @@ class DefaultRoomLatestEventFormatter(
message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing)
}
is LegacyCallInviteContent -> sp.getString(CommonStrings.common_unsupported_call)
is CallNotifyContent -> sp.getString(CommonStrings.common_call_started)
is CallNotifyContent -> rtcNotificationContentFormatter.format(content, isDmRoom)
}?.take(DEFAULT_SAFE_LENGTH)
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.eventformatter.impl
import dev.zacsweers.metro.Inject
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.toolbox.api.strings.StringProvider
@Inject
class RtcNotificationContentFormatter(
private val matrixClient: MatrixClient,
private val sp: StringProvider,
) {
fun format(
content: CallNotifyContent,
isDm: Boolean,
): CharSequence {
return if (isDm) {
val isDeclined = content.declinedBy.isNotEmpty()
val isDeclinedByMe = content.declinedBy.any { matrixClient.isMe(it) }
if (isDeclinedByMe) {
sp.getString(CommonStrings.common_call_you_declined)
} else if (isDeclined) {
sp.getString(CommonStrings.common_call_declined)
} else {
sp.getString(CommonStrings.common_call_started)
}
} else {
sp.getString(CommonStrings.common_call_started)
}
}
}

View file

@ -74,7 +74,8 @@ class DefaultRoomLatestEventFormatterTest {
roomMembershipContentFormatter = RoomMembershipContentFormatter(fakeMatrixClient, stringProvider),
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
stateContentFormatter = StateContentFormatter(stringProvider),
permalinkParser = FakePermalinkParser(),
rtcNotificationContentFormatter = RtcNotificationContentFormatter(fakeMatrixClient, stringProvider),
permalinkParser = FakePermalinkParser()
)
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.eventformatter.impl
import android.content.Context
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.notification.CallIntent
import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.A_USER_ID_3
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import kotlin.toString
@Suppress("LargeClass")
@RunWith(RobolectricTestRunner::class)
class RtcNotificationContentFormatterTest {
private lateinit var context: Context
private lateinit var fakeMatrixClient: FakeMatrixClient
private lateinit var formatter: RtcNotificationContentFormatter
@Before
fun setup() {
context = RuntimeEnvironment.getApplication() as Context
fakeMatrixClient = FakeMatrixClient()
val stringProvider = AndroidStringProvider(context.resources)
formatter = RtcNotificationContentFormatter(
fakeMatrixClient,
stringProvider
)
}
@Test
@Config(qualifiers = "en")
fun `Should not display declined info in rooms`() {
val result = formatter.format(
CallNotifyContent(
CallIntent.VIDEO,
declinedBy = listOf(A_USER_ID_2, A_USER_ID_3)
),
false
)
val expected = "Call started"
assertThat(result.toString()).isEqualTo(expected)
}
@Test
@Config(qualifiers = "en")
fun `Declined by me variant`() {
val result = formatter.format(
CallNotifyContent(
CallIntent.VIDEO,
declinedBy = listOf(fakeMatrixClient.sessionId)
),
true
)
val expected = "You declined a call"
assertThat(result.toString()).isEqualTo(expected)
}
@Test
@Config(qualifiers = "en")
fun `Declined by other variant`() {
val result = formatter.format(
CallNotifyContent(
CallIntent.VIDEO,
declinedBy = listOf(A_USER_ID_2)
),
true
)
val expected = "Call declined"
assertThat(result.toString()).isEqualTo(expected)
}
@Test
@Config(qualifiers = "en")
fun `Call started in DM`() {
val result = formatter.format(
CallNotifyContent(
CallIntent.AUDIO,
declinedBy = listOf()
),
true
)
val expected = "Call started"
assertThat(result.toString()).isEqualTo(expected)
}
}

View file

@ -119,7 +119,8 @@ data class LiveLocationContent(
data object LegacyCallInviteContent : EventContent
data class CallNotifyContent(
val callIntent: CallIntent
val callIntent: CallIntent,
val declinedBy: List<UserId>
) : EventContent
data object UnknownContent : EventContent

View file

@ -153,7 +153,8 @@ class TimelineEventContentMapper(
CallIntent.AUDIO
} else {
CallIntent.VIDEO
}
},
declinedBy = it.declinedBy.map(::UserId)
)
}
}

View file

@ -62,7 +62,7 @@ class DefaultEventItemFactoryTest {
fun `create check all null cases`() {
val factory = createEventItemFactory()
val contents = listOf(
CallNotifyContent(callIntent = CallIntent.VIDEO),
CallNotifyContent(callIntent = CallIntent.VIDEO, emptyList()),
FailedToParseMessageLikeContent("", ""),
FailedToParseStateContent("", "", ""),
LegacyCallInviteContent,