Skip to content

Commit dff295e

Browse files
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 <[email protected]>
1 parent bfdcc97 commit dff295e

File tree

17 files changed

+64
-174
lines changed

17 files changed

+64
-174
lines changed

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,11 @@ import io.element.android.libraries.architecture.Presenter
3333
import io.element.android.libraries.architecture.runCatchingUpdatingState
3434
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
3535
import io.element.android.libraries.di.annotations.AppCoroutineScope
36-
import io.element.android.libraries.matrix.api.MatrixClient
3736
import io.element.android.libraries.matrix.api.MatrixClientProvider
38-
import io.element.android.libraries.matrix.api.core.RoomId
3937
import io.element.android.libraries.matrix.api.sync.SyncState
4038
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
4139
import io.element.android.libraries.network.useragent.UserAgentProvider
4240
import io.element.android.services.analytics.api.ScreenTracker
43-
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
4441
import io.element.android.services.appnavstate.api.AppForegroundStateService
4542
import io.element.android.services.toolbox.api.systemclock.SystemClock
4643
import kotlinx.coroutines.CoroutineScope
@@ -64,7 +61,6 @@ class CallScreenPresenter @AssistedInject constructor(
6461
private val activeCallManager: ActiveCallManager,
6562
private val languageTagProvider: LanguageTagProvider,
6663
private val appForegroundStateService: AppForegroundStateService,
67-
private val activeRoomsHolder: ActiveRoomsHolder,
6864
@AppCoroutineScope
6965
private val appCoroutineScope: CoroutineScope,
7066
) : Presenter<CallScreenState> {
@@ -75,7 +71,6 @@ class CallScreenPresenter @AssistedInject constructor(
7571

7672
private val isInWidgetMode = callType is CallType.RoomCall
7773
private val userAgent = userAgentProvider.provide()
78-
private var notifiedCallStart = false
7974

8075
@Composable
8176
override fun present(): CallScreenState {
@@ -248,9 +243,7 @@ class CallScreenPresenter @AssistedInject constructor(
248243
Timber.d("Observing sync state in-call for sessionId: ${roomCallType.sessionId}")
249244
client.syncService().syncState
250245
.collect { state ->
251-
if (state == SyncState.Running) {
252-
client.notifyCallStartIfNeeded(callType.roomId)
253-
} else {
246+
if (state != SyncState.Running) {
254247
appForegroundStateService.updateIsInCallState(true)
255248
}
256249
}
@@ -263,32 +256,6 @@ class CallScreenPresenter @AssistedInject constructor(
263256
}
264257
}
265258

266-
private suspend fun MatrixClient.notifyCallStartIfNeeded(roomId: RoomId) {
267-
if (notifiedCallStart) return
268-
269-
val activeRoomForSession = activeRoomsHolder.getActiveRoomMatching(sessionId, roomId)
270-
val sendCallNotificationResult = if (activeRoomForSession != null) {
271-
Timber.d("Notifying call start for room $roomId. Has room call: ${activeRoomForSession.info().hasRoomCall}")
272-
activeRoomForSession.sendCallNotificationIfNeeded()
273-
} else {
274-
// Instantiate the room from the session and roomId and send the notification
275-
getJoinedRoom(roomId)?.use { room ->
276-
Timber.d("Notifying call start for room $roomId. Has room call: ${room.info().hasRoomCall}")
277-
room.sendCallNotificationIfNeeded()
278-
} ?: run {
279-
Timber.w("No room found for session $sessionId and room $roomId, skipping call notification.")
280-
return
281-
}
282-
}
283-
284-
sendCallNotificationResult.fold(
285-
onSuccess = { notifiedCallStart = true },
286-
onFailure = { error ->
287-
Timber.e(error, "Failed to send call notification for room $roomId.")
288-
}
289-
)
290-
}
291-
292259
private fun parseMessage(message: String): WidgetMessage? {
293260
return WidgetMessageSerializer.deserialize(message).getOrNull()
294261
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.element.android.libraries.di.AppScope
1313
import io.element.android.libraries.matrix.api.MatrixClientProvider
1414
import io.element.android.libraries.matrix.api.core.RoomId
1515
import io.element.android.libraries.matrix.api.core.SessionId
16+
import io.element.android.libraries.matrix.api.room.isDm
1617
import io.element.android.libraries.matrix.api.widget.CallWidgetSettingsProvider
1718
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
1819
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
@@ -44,7 +45,7 @@ class DefaultCallWidgetProvider @Inject constructor(
4445
val baseUrl = customBaseUrl ?: EMBEDDED_CALL_WIDGET_BASE_URL
4546

4647
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
47-
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = isEncrypted)
48+
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = isEncrypted, direct = room.isDm())
4849
val callUrl = room.generateWidgetWebViewUrl(
4950
widgetSettings = widgetSettings,
5051
clientId = clientId,

features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,11 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
2626
import io.element.android.libraries.matrix.test.A_SESSION_ID
2727
import io.element.android.libraries.matrix.test.FakeMatrixClient
2828
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
29-
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
3029
import io.element.android.libraries.matrix.test.sync.FakeSyncService
3130
import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
3231
import io.element.android.libraries.network.useragent.UserAgentProvider
3332
import io.element.android.services.analytics.api.ScreenTracker
3433
import io.element.android.services.analytics.test.FakeScreenTracker
35-
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
3634
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
3735
import io.element.android.services.toolbox.api.systemclock.SystemClock
3836
import io.element.android.tests.testutils.WarmUpRule
@@ -82,19 +80,12 @@ import kotlin.time.Duration.Companion.seconds
8280
}
8381

8482
@Test
85-
fun `present - with CallType RoomCall sets call as active, loads URL, runs WidgetDriver and notifies the other clients a call started`() = runTest {
86-
val sendCallNotificationIfNeededLambda = lambdaRecorder<Result<Boolean>> { Result.success(true) }
87-
val syncService = FakeSyncService(SyncState.Running)
88-
val fakeRoom = FakeJoinedRoom(sendCallNotificationIfNeededResult = sendCallNotificationIfNeededLambda)
89-
val client = FakeMatrixClient(syncService = syncService).apply {
90-
givenGetRoomResult(A_ROOM_ID, fakeRoom)
91-
}
83+
fun `present - with CallType RoomCall sets call as active, loads URL and runs WidgetDriver`() = runTest {
9284
val widgetDriver = FakeMatrixWidgetDriver()
9385
val widgetProvider = FakeCallWidgetProvider(widgetDriver)
9486
val analyticsLambda = lambdaRecorder<MobileScreen.ScreenName, Unit> {}
9587
val joinedCallLambda = lambdaRecorder<CallType, Unit> {}
9688
val presenter = createCallScreenPresenter(
97-
matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(client) }),
9889
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
9990
widgetDriver = widgetDriver,
10091
widgetProvider = widgetProvider,
@@ -116,7 +107,6 @@ import kotlin.time.Duration.Companion.seconds
116107
assertThat(widgetProvider.getWidgetCalled).isTrue()
117108
assertThat(widgetDriver.runCalledCount).isEqualTo(1)
118109
analyticsLambda.assertions().isCalledOnce().with(value(MobileScreen.ScreenName.RoomCall))
119-
sendCallNotificationIfNeededLambda.assertions().isCalledOnce()
120110

121111
// Wait until the WidgetDriver is loaded
122112
skipItems(1)
@@ -399,7 +389,6 @@ import kotlin.time.Duration.Companion.seconds
399389
activeCallManager: FakeActiveCallManager = FakeActiveCallManager(),
400390
screenTracker: ScreenTracker = FakeScreenTracker(),
401391
appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(),
402-
activeRoomsHolder: ActiveRoomsHolder = ActiveRoomsHolder(),
403392
): CallScreenPresenter {
404393
val userAgentProvider = object : UserAgentProvider {
405394
override fun provide(): String {
@@ -420,7 +409,6 @@ import kotlin.time.Duration.Companion.seconds
420409
languageTagProvider = FakeLanguageTagProvider("en-US"),
421410
appForegroundStateService = appForegroundStateService,
422411
appCoroutineScope = backgroundScope,
423-
activeRoomsHolder = activeRoomsHolder,
424412
)
425413
}
426414
}

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ jsoup = "org.jsoup:jsoup:1.21.1"
176176
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
177177
molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0"
178178
timber = "com.jakewharton.timber:timber:5.0.1"
179-
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.7.28"
179+
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.8.5"
180180
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
181181
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
182182
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
@@ -215,7 +215,7 @@ anvil_compiler_api = { module = "dev.zacsweers.anvil:compiler-api", version.ref
215215
anvil_compiler_utils = { module = "dev.zacsweers.anvil:compiler-utils", version.ref = "anvil" }
216216

217217
# Element Call
218-
element_call_embedded = "io.element.android:element-call-embedded:0.13.1"
218+
element_call_embedded = "io.element.android:element-call-embedded:0.14.1"
219219

220220
# Auto services
221221
google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" }

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,6 @@ interface JoinedRoom : BaseRoom {
156156
*/
157157
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
158158

159-
/**
160-
* Send an Element Call started notification if needed.
161-
*/
162-
suspend fun sendCallNotificationIfNeeded(): Result<Boolean>
163-
164159
suspend fun setSendQueueEnabled(enabled: Boolean)
165160

166161
/**

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ interface CallWidgetSettingsProvider {
1414
baseUrl: String,
1515
widgetId: String = UUID.randomUUID().toString(),
1616
encrypted: Boolean,
17+
direct: Boolean,
1718
): MatrixWidgetSettings
1819
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -428,12 +428,6 @@ class JoinedRustRoom(
428428
}
429429
}
430430

431-
override suspend fun sendCallNotificationIfNeeded(): Result<Boolean> = withContext(roomDispatcher) {
432-
runCatchingExceptions {
433-
innerRoom.sendCallNotificationIfNeeded()
434-
}
435-
}
436-
437431
override suspend fun setSendQueueEnabled(enabled: Boolean) {
438432
withContext(roomDispatcher) {
439433
Timber.d("setSendQueuesEnabled: $enabled")

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
2929

3030
private val ROOM_LIST_RUST_FILTERS = listOf(
3131
RoomListEntriesDynamicFilterKind.NonLeft,
32+
RoomListEntriesDynamicFilterKind.NonSpace,
3233
RoomListEntriesDynamicFilterKind.DeduplicateVersions
3334
)
3435

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
1414
import kotlinx.coroutines.flow.first
1515
import kotlinx.coroutines.sync.Mutex
1616
import kotlinx.coroutines.sync.withLock
17-
import org.matrix.rustcomponents.sdk.TimelineChange
1817
import org.matrix.rustcomponents.sdk.TimelineDiff
1918
import org.matrix.rustcomponents.sdk.TimelineItem
2019
import timber.log.Timber
@@ -49,55 +48,51 @@ internal class MatrixTimelineDiffProcessor(
4948
}
5049

5150
private fun MutableList<MatrixTimelineItem>.applyDiff(diff: TimelineDiff) {
52-
when (diff.change()) {
53-
TimelineChange.APPEND -> {
54-
val items = diff.append()?.map { it.asMatrixTimelineItem() } ?: return
51+
when (diff) {
52+
is TimelineDiff.Append -> {
53+
val items = diff.values.map { it.asMatrixTimelineItem() }
5554
addAll(items)
5655
}
57-
TimelineChange.PUSH_BACK -> {
58-
val item = diff.pushBack()?.asMatrixTimelineItem() ?: return
56+
is TimelineDiff.PushBack -> {
57+
val item = diff.value.asMatrixTimelineItem()
5958
if (item is MatrixTimelineItem.Event && item.event.content is RoomMembershipContent) {
6059
// TODO - This is a temporary solution to notify the room screen about membership changes
6160
// Ideally, this should be implemented by the Rust SDK
6261
_membershipChangeEventReceived.tryEmit(Unit)
6362
}
6463
add(item)
6564
}
66-
TimelineChange.PUSH_FRONT -> {
67-
val item = diff.pushFront()?.asMatrixTimelineItem() ?: return
65+
is TimelineDiff.PushFront -> {
66+
val item = diff.value.asMatrixTimelineItem()
6867
add(0, item)
6968
}
70-
TimelineChange.SET -> {
71-
val updateAtData = diff.set() ?: return
72-
val item = updateAtData.item.asMatrixTimelineItem()
73-
set(updateAtData.index.toInt(), item)
69+
is TimelineDiff.Set -> {
70+
val item = diff.value.asMatrixTimelineItem()
71+
set(diff.index.toInt(), item)
7472
}
75-
TimelineChange.INSERT -> {
76-
val insertAtData = diff.insert() ?: return
77-
val item = insertAtData.item.asMatrixTimelineItem()
78-
add(insertAtData.index.toInt(), item)
73+
is TimelineDiff.Insert -> {
74+
val item = diff.value.asMatrixTimelineItem()
75+
add(diff.index.toInt(), item)
7976
}
80-
TimelineChange.REMOVE -> {
81-
val removeAtData = diff.remove() ?: return
82-
removeAt(removeAtData.toInt())
77+
is TimelineDiff.Remove -> {
78+
removeAt(diff.index.toInt())
8379
}
84-
TimelineChange.RESET -> {
80+
is TimelineDiff.Reset -> {
8581
clear()
86-
val items = diff.reset()?.map { it.asMatrixTimelineItem() } ?: return
82+
val items = diff.values.map { it.asMatrixTimelineItem() }
8783
addAll(items)
8884
}
89-
TimelineChange.POP_FRONT -> {
85+
TimelineDiff.PopFront -> {
9086
removeFirstOrNull()
9187
}
92-
TimelineChange.POP_BACK -> {
88+
TimelineDiff.PopBack -> {
9389
removeLastOrNull()
9490
}
95-
TimelineChange.CLEAR -> {
91+
TimelineDiff.Clear -> {
9692
clear()
9793
}
98-
TimelineChange.TRUNCATE -> {
99-
val index = diff.truncate() ?: return
100-
subList(index.toInt(), size).clear()
94+
is TimelineDiff.Truncate -> {
95+
subList(diff.length.toInt(), size).clear()
10196
}
10297
}
10398
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineDiffExt.kt

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
package io.element.android.libraries.matrix.impl.timeline
99

10-
import org.matrix.rustcomponents.sdk.TimelineChange
1110
import org.matrix.rustcomponents.sdk.TimelineDiff
1211
import org.matrix.rustcomponents.sdk.TimelineItem
1312
import uniffi.matrix_sdk_ui.EventItemOrigin
@@ -17,25 +16,13 @@ import uniffi.matrix_sdk_ui.EventItemOrigin
1716
* If there is multiple events in the diff, uses the first one as it should be a good indicator.
1817
*/
1918
internal fun TimelineDiff.eventOrigin(): EventItemOrigin? {
20-
return when (change()) {
21-
TimelineChange.APPEND -> {
22-
append()?.firstOrNull()?.eventOrigin()
23-
}
24-
TimelineChange.PUSH_BACK -> {
25-
pushBack()?.eventOrigin()
26-
}
27-
TimelineChange.PUSH_FRONT -> {
28-
pushFront()?.eventOrigin()
29-
}
30-
TimelineChange.SET -> {
31-
set()?.item?.eventOrigin()
32-
}
33-
TimelineChange.INSERT -> {
34-
insert()?.item?.eventOrigin()
35-
}
36-
TimelineChange.RESET -> {
37-
reset()?.firstOrNull()?.eventOrigin()
38-
}
19+
return when (this) {
20+
is TimelineDiff.Append -> values.firstOrNull()?.eventOrigin()
21+
is TimelineDiff.PushBack -> value.eventOrigin()
22+
is TimelineDiff.PushFront -> value.eventOrigin()
23+
is TimelineDiff.Set -> value.eventOrigin()
24+
is TimelineDiff.Insert -> value.eventOrigin()
25+
is TimelineDiff.Reset -> values.firstOrNull()?.eventOrigin()
3926
else -> null
4027
}
4128
}

0 commit comments

Comments
 (0)