Delegate call notifications to Element Call, upgrade SDK and EC embedded (#5119)

* Stop sending call notifications manually: the Element Call widget can now assume responsibility for sending them when you start a call.

* Upgrade SDK version to `v25.8.5`, fix API breaks

* Upgrade Element Call embedded to `v0.14.1`

* Fix tests and lint issues

* Add `RoomListEntriesDynamicFilterKind.NonSpace` to avoid displaying spaces in the room list

---------

Co-authored-by: Robin <robin@robin.town>
This commit is contained in:
Jorge Martin Espinosa 2025-08-05 17:14:51 +02:00 committed by GitHub
parent bfdcc97985
commit dff295eadf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 64 additions and 174 deletions

View file

@ -428,12 +428,6 @@ class JoinedRustRoom(
}
}
override suspend fun sendCallNotificationIfNeeded(): Result<Boolean> = withContext(roomDispatcher) {
runCatchingExceptions {
innerRoom.sendCallNotificationIfNeeded()
}
}
override suspend fun setSendQueueEnabled(enabled: Boolean) {
withContext(roomDispatcher) {
Timber.d("setSendQueuesEnabled: $enabled")

View file

@ -29,6 +29,7 @@ import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
private val ROOM_LIST_RUST_FILTERS = listOf(
RoomListEntriesDynamicFilterKind.NonLeft,
RoomListEntriesDynamicFilterKind.NonSpace,
RoomListEntriesDynamicFilterKind.DeduplicateVersions
)

View file

@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
import timber.log.Timber
@ -49,13 +48,13 @@ internal class MatrixTimelineDiffProcessor(
}
private fun MutableList<MatrixTimelineItem>.applyDiff(diff: TimelineDiff) {
when (diff.change()) {
TimelineChange.APPEND -> {
val items = diff.append()?.map { it.asMatrixTimelineItem() } ?: return
when (diff) {
is TimelineDiff.Append -> {
val items = diff.values.map { it.asMatrixTimelineItem() }
addAll(items)
}
TimelineChange.PUSH_BACK -> {
val item = diff.pushBack()?.asMatrixTimelineItem() ?: return
is TimelineDiff.PushBack -> {
val item = diff.value.asMatrixTimelineItem()
if (item is MatrixTimelineItem.Event && item.event.content is RoomMembershipContent) {
// TODO - This is a temporary solution to notify the room screen about membership changes
// Ideally, this should be implemented by the Rust SDK
@ -63,41 +62,37 @@ internal class MatrixTimelineDiffProcessor(
}
add(item)
}
TimelineChange.PUSH_FRONT -> {
val item = diff.pushFront()?.asMatrixTimelineItem() ?: return
is TimelineDiff.PushFront -> {
val item = diff.value.asMatrixTimelineItem()
add(0, item)
}
TimelineChange.SET -> {
val updateAtData = diff.set() ?: return
val item = updateAtData.item.asMatrixTimelineItem()
set(updateAtData.index.toInt(), item)
is TimelineDiff.Set -> {
val item = diff.value.asMatrixTimelineItem()
set(diff.index.toInt(), item)
}
TimelineChange.INSERT -> {
val insertAtData = diff.insert() ?: return
val item = insertAtData.item.asMatrixTimelineItem()
add(insertAtData.index.toInt(), item)
is TimelineDiff.Insert -> {
val item = diff.value.asMatrixTimelineItem()
add(diff.index.toInt(), item)
}
TimelineChange.REMOVE -> {
val removeAtData = diff.remove() ?: return
removeAt(removeAtData.toInt())
is TimelineDiff.Remove -> {
removeAt(diff.index.toInt())
}
TimelineChange.RESET -> {
is TimelineDiff.Reset -> {
clear()
val items = diff.reset()?.map { it.asMatrixTimelineItem() } ?: return
val items = diff.values.map { it.asMatrixTimelineItem() }
addAll(items)
}
TimelineChange.POP_FRONT -> {
TimelineDiff.PopFront -> {
removeFirstOrNull()
}
TimelineChange.POP_BACK -> {
TimelineDiff.PopBack -> {
removeLastOrNull()
}
TimelineChange.CLEAR -> {
TimelineDiff.Clear -> {
clear()
}
TimelineChange.TRUNCATE -> {
val index = diff.truncate() ?: return
subList(index.toInt(), size).clear()
is TimelineDiff.Truncate -> {
subList(diff.length.toInt(), size).clear()
}
}
}

View file

@ -7,7 +7,6 @@
package io.element.android.libraries.matrix.impl.timeline
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
import uniffi.matrix_sdk_ui.EventItemOrigin
@ -17,25 +16,13 @@ import uniffi.matrix_sdk_ui.EventItemOrigin
* If there is multiple events in the diff, uses the first one as it should be a good indicator.
*/
internal fun TimelineDiff.eventOrigin(): EventItemOrigin? {
return when (change()) {
TimelineChange.APPEND -> {
append()?.firstOrNull()?.eventOrigin()
}
TimelineChange.PUSH_BACK -> {
pushBack()?.eventOrigin()
}
TimelineChange.PUSH_FRONT -> {
pushFront()?.eventOrigin()
}
TimelineChange.SET -> {
set()?.item?.eventOrigin()
}
TimelineChange.INSERT -> {
insert()?.item?.eventOrigin()
}
TimelineChange.RESET -> {
reset()?.firstOrNull()?.eventOrigin()
}
return when (this) {
is TimelineDiff.Append -> values.firstOrNull()?.eventOrigin()
is TimelineDiff.PushBack -> value.eventOrigin()
is TimelineDiff.PushFront -> value.eventOrigin()
is TimelineDiff.Set -> value.eventOrigin()
is TimelineDiff.Insert -> value.eventOrigin()
is TimelineDiff.Reset -> values.firstOrNull()?.eventOrigin()
else -> null
}
}

View file

@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.first
import org.matrix.rustcomponents.sdk.newVirtualElementCallWidget
import uniffi.matrix_sdk.EncryptionSystem
import uniffi.matrix_sdk.HeaderStyle
import uniffi.matrix_sdk.NotificationType
import uniffi.matrix_sdk.VirtualElementCallWidgetOptions
import javax.inject.Inject
import uniffi.matrix_sdk.Intent as CallIntent
@ -29,7 +30,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor(
private val callAnalyticsCredentialsProvider: CallAnalyticCredentialsProvider,
private val analyticsService: AnalyticsService,
) : CallWidgetSettingsProvider {
override suspend fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings {
override suspend fun provide(baseUrl: String, widgetId: String, encrypted: Boolean, direct: Boolean): MatrixWidgetSettings {
val isAnalyticsEnabled = analyticsService.userConsentFlow.first()
val options = VirtualElementCallWidgetOptions(
elementCallUrl = baseUrl,
@ -53,6 +54,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor(
hideHeader = true,
controlledMediaDevices = true,
header = HeaderStyle.APP_BAR,
sendNotificationType = if (direct) NotificationType.RING else NotificationType.NOTIFICATION,
)
val rustWidgetSettings = newVirtualElementCallWidget(options)
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)

View file

@ -1,30 +0,0 @@
/*
* Copyright 2024 New Vector 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.matrix.impl.fixtures.fakes
import org.matrix.rustcomponents.sdk.InsertData
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.SetData
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
class FakeFfiTimelineDiff(
private val change: TimelineChange,
private val item: TimelineItem? = FakeFfiTimelineItem()
) : TimelineDiff(NoPointer) {
override fun change() = change
override fun append(): List<TimelineItem>? = item?.let { listOf(it) }
override fun insert(): InsertData? = item?.let { InsertData(1u, it) }
override fun pushBack(): TimelineItem? = item
override fun pushFront(): TimelineItem? = item
override fun remove(): UInt? = 1u
override fun reset(): List<TimelineItem>? = item?.let { listOf(it) }
override fun set(): SetData? = item?.let { SetData(1u, it) }
override fun truncate(): UInt? = 1u
}

View file

@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.impl.timeline
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineDiff
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineItem
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
class MatrixTimelineDiffProcessorTest {
private val timelineItems = MutableStateFlow<List<MatrixTimelineItem>>(emptyList())
@ -33,7 +33,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Append adds new entries at the end of the list`() = runTest {
timelineItems.value = listOf(anEvent)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.APPEND)))
processor.postDiffs(listOf(TimelineDiff.Append(listOf(FakeFfiTimelineItem()))))
assertThat(timelineItems.value.count()).isEqualTo(2)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -45,7 +45,7 @@ class MatrixTimelineDiffProcessorTest {
fun `PushBack adds a new entry at the end of the list`() = runTest {
timelineItems.value = listOf(anEvent)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.PUSH_BACK)))
processor.postDiffs(listOf(TimelineDiff.PushBack(FakeFfiTimelineItem())))
assertThat(timelineItems.value.count()).isEqualTo(2)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -57,7 +57,7 @@ class MatrixTimelineDiffProcessorTest {
fun `PushFront inserts a new entry at the start of the list`() = runTest {
timelineItems.value = listOf(anEvent)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.PUSH_FRONT)))
processor.postDiffs(listOf(TimelineDiff.PushFront(FakeFfiTimelineItem())))
assertThat(timelineItems.value.count()).isEqualTo(2)
assertThat(timelineItems.value).containsExactly(
MatrixTimelineItem.Other,
@ -69,7 +69,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Set replaces an entry at some index`() = runTest {
timelineItems.value = listOf(anEvent, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.SET)))
processor.postDiffs(listOf(TimelineDiff.Set(1u, FakeFfiTimelineItem())))
assertThat(timelineItems.value.count()).isEqualTo(2)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -81,7 +81,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Insert inserts a new entry at the provided index`() = runTest {
timelineItems.value = listOf(anEvent, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.INSERT)))
processor.postDiffs(listOf(TimelineDiff.Insert(1u, FakeFfiTimelineItem())))
assertThat(timelineItems.value.count()).isEqualTo(3)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -94,7 +94,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Remove removes an entry at some index`() = runTest {
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.REMOVE)))
processor.postDiffs(listOf(TimelineDiff.Remove(1u)))
assertThat(timelineItems.value.count()).isEqualTo(2)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -106,7 +106,7 @@ class MatrixTimelineDiffProcessorTest {
fun `PopBack removes an entry at the end of the list`() = runTest {
timelineItems.value = listOf(anEvent, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.POP_BACK)))
processor.postDiffs(listOf(TimelineDiff.PopBack))
assertThat(timelineItems.value.count()).isEqualTo(1)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -117,7 +117,7 @@ class MatrixTimelineDiffProcessorTest {
fun `PopFront removes an entry at the start of the list`() = runTest {
timelineItems.value = listOf(anEvent, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.POP_FRONT)))
processor.postDiffs(listOf(TimelineDiff.PopFront))
assertThat(timelineItems.value.count()).isEqualTo(1)
assertThat(timelineItems.value).containsExactly(
anEvent2,
@ -128,7 +128,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Clear removes all the entries`() = runTest {
timelineItems.value = listOf(anEvent, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.CLEAR)))
processor.postDiffs(listOf(TimelineDiff.Clear))
assertThat(timelineItems.value).isEmpty()
}
@ -136,7 +136,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Truncate removes all entries after the provided length`() = runTest {
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.TRUNCATE)))
processor.postDiffs(listOf(TimelineDiff.Truncate(1u)))
assertThat(timelineItems.value.count()).isEqualTo(1)
assertThat(timelineItems.value).containsExactly(
anEvent,
@ -147,7 +147,7 @@ class MatrixTimelineDiffProcessorTest {
fun `Reset removes all entries and add the provided ones`() = runTest {
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
val processor = createMatrixTimelineDiffProcessor(timelineItems)
processor.postDiffs(listOf(FakeFfiTimelineDiff(change = TimelineChange.RESET)))
processor.postDiffs(listOf(TimelineDiff.Reset(listOf(FakeFfiTimelineItem()))))
assertThat(timelineItems.value.count()).isEqualTo(1)
assertThat(timelineItems.value).containsExactly(
MatrixTimelineItem.Other,

View file

@ -18,7 +18,6 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiRoomListService
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimeline
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineDiff
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
@ -33,7 +32,7 @@ import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import uniffi.matrix_sdk.RoomPaginationStatus
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
@ -51,10 +50,7 @@ class RustTimelineTest {
runCurrent()
inner.emitDiff(
listOf(
FakeFfiTimelineDiff(
item = null,
change = TimelineChange.RESET,
)
TimelineDiff.Reset(emptyList())
)
)
with(awaitItem()) {

View file

@ -12,7 +12,6 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustEventTimelineItem
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimeline
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineDiff
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineItem
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -24,7 +23,7 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.matrix.rustcomponents.sdk.Timeline
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import uniffi.matrix_sdk_ui.EventItemOrigin
@OptIn(ExperimentalCoroutinesApi::class)
@ -42,7 +41,7 @@ class TimelineItemsSubscriberTest {
timelineItemsSubscriber.subscribeIfNeeded()
// Wait for the listener to be set.
runCurrent()
timeline.emitDiff(listOf(FakeFfiTimelineDiff(item = null, change = TimelineChange.RESET)))
timeline.emitDiff(listOf(TimelineDiff.Reset(emptyList())))
val final = awaitItem()
assertThat(final).isEmpty()
timelineItemsSubscriber.unsubscribeIfNeeded()
@ -62,7 +61,7 @@ class TimelineItemsSubscriberTest {
timelineItemsSubscriber.subscribeIfNeeded()
// Wait for the listener to be set.
runCurrent()
timeline.emitDiff(listOf(FakeFfiTimelineDiff(item = FakeFfiTimelineItem(), change = TimelineChange.RESET)))
timeline.emitDiff(listOf(TimelineDiff.Reset(listOf(FakeFfiTimelineItem()))))
val final = awaitItem()
assertThat(final).isNotEmpty()
timelineItemsSubscriber.unsubscribeIfNeeded()
@ -86,11 +85,10 @@ class TimelineItemsSubscriberTest {
runCurrent()
timeline.emitDiff(
listOf(
FakeFfiTimelineDiff(
item = FakeFfiTimelineItem(
TimelineDiff.Reset(
listOf(FakeFfiTimelineItem(
asEventResult = aRustEventTimelineItem(origin = EventItemOrigin.SYNC),
),
change = TimelineChange.RESET,
))
)
)
)