From 5b6866435048817dc108d11bb1e5f26e6b715f49 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Feb 2026 14:59:49 +0100 Subject: [PATCH] Expose liveLocationSharing methods from sdk --- .../libraries/matrix/api/room/JoinedRoom.kt | 27 ++++++++++++++++ .../api/room/location/LiveLocationShare.kt | 24 ++++++++++++++ .../matrix/impl/room/JoinedRustRoom.kt | 31 +++++++++++++++++++ .../room/location/LiveLocationShareMapper.kt | 21 +++++++++++++ .../matrix/test/room/FakeJoinedRoom.kt | 21 +++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationShareMapper.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt index b804bdf46d..808f37c7c9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.knock.KnockRequest +import io.element.android.libraries.matrix.api.room.location.LiveLocationShare import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility @@ -182,4 +183,30 @@ interface JoinedRoom : BaseRoom { * Subscribe to a [Flow] of [SendQueueUpdate] related to this room. */ fun subscribeToSendQueueUpdates(): Flow + + /** + * Subscribe to live location shares in this room. + * @return Flow of list of active live location shares. + */ + fun subscribeToLiveLocationShares(): Flow> + + /** + * Start sharing live location in this room. + * @param durationMillis How long to share location (in milliseconds). + * @return Result indicating success or failure. + */ + suspend fun startLiveLocationShare(durationMillis: Long): Result + + /** + * Stop sharing live location in this room. + * @return Result indicating success or failure. + */ + suspend fun stopLiveLocationShare(): Result + + /** + * Send a live location update while a live location share is active. + * @param geoUri The geo URI (e.g., "geo:51.5074,-0.1278"). + * @return Result indicating success or failure. + */ + suspend fun sendLiveLocation(geoUri: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt new file mode 100644 index 0000000000..7e841639bd --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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.matrix.api.room.location + +import io.element.android.libraries.matrix.api.core.UserId + +/** + * Represents a live location share from a user in a room. + */ +data class LiveLocationShare( + /** The user who is sharing their location. */ + val userId: UserId, + /** The last known geo URI (e.g., "geo:51.5074,-0.1278"). */ + val lastGeoUri: String, + /** The timestamp of the last location update. */ + val lastTimestamp: Long, + /** Whether the live location share is still active. */ + val isLive: Boolean, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index dded213d4c..4f9568df7c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.room.SendQueueUpdate import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.knock.KnockRequest +import io.element.android.libraries.matrix.api.room.location.LiveLocationShare import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.roomNotificationSettings @@ -42,6 +43,7 @@ import io.element.android.libraries.matrix.impl.mapper.map import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest +import io.element.android.libraries.matrix.impl.room.location.map import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.roomdirectory.map import io.element.android.libraries.matrix.impl.timeline.RustTimeline @@ -66,6 +68,7 @@ import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.DateDividerMode import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener import org.matrix.rustcomponents.sdk.KnockRequestsListener +import org.matrix.rustcomponents.sdk.LiveLocationShareListener import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType import org.matrix.rustcomponents.sdk.RoomSendQueueUpdate import org.matrix.rustcomponents.sdk.SendQueueListener @@ -500,6 +503,34 @@ class JoinedRustRoom( } } + override fun subscribeToLiveLocationShares(): Flow> { + return mxCallbackFlow { + innerRoom.subscribeToLiveLocationShares(object : LiveLocationShareListener { + override fun call(liveLocationShares: List) { + trySend(liveLocationShares.map { it.map() }) + } + }) + } + } + + override suspend fun startLiveLocationShare(durationMillis: Long): Result = withContext(roomDispatcher) { + runCatchingExceptions { + innerRoom.startLiveLocationShare(durationMillis.toULong()) + } + } + + override suspend fun stopLiveLocationShare(): Result = withContext(roomDispatcher) { + runCatchingExceptions { + innerRoom.stopLiveLocationShare() + } + } + + override suspend fun sendLiveLocation(geoUri: String): Result = withContext(roomDispatcher) { + runCatchingExceptions { + innerRoom.sendLiveLocation(geoUri) + } + } + override fun close() = destroy() override fun destroy() { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationShareMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationShareMapper.kt new file mode 100644 index 0000000000..3b80c1c61f --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationShareMapper.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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.matrix.impl.room.location + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import org.matrix.rustcomponents.sdk.LiveLocationShare as RustLiveLocationShare + +fun RustLiveLocationShare.map(): LiveLocationShare { + return LiveLocationShare( + userId = UserId(userId), + lastGeoUri = lastLocation.location.geoUri, + lastTimestamp = lastLocation.ts.toLong(), + isLive = isLive, + ) +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt index 9c97e7787b..a4580334e4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.room.SendQueueUpdate import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.knock.KnockRequest +import io.element.android.libraries.matrix.api.room.location.LiveLocationShare import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility @@ -84,6 +85,10 @@ class FakeJoinedRoom( private val enableEncryptionResult: () -> Result = { lambdaError() }, private val updateJoinRuleResult: (JoinRule) -> Result = { lambdaError() }, private val setSendQueueEnabledResult: (Boolean) -> Unit = { _: Boolean -> }, + private val liveLocationSharesFlow: Flow> = MutableStateFlow(emptyList()), + private val startLiveLocationShareResult: (Long) -> Result = { lambdaError() }, + private val stopLiveLocationShareResult: () -> Result = { lambdaError() }, + private val sendLiveLocationResult: (String) -> Result = { lambdaError() }, ) : JoinedRoom, BaseRoom by baseRoom { private val sendQueueUpdates = MutableSharedFlow(extraBufferCapacity = 10) @@ -227,6 +232,22 @@ class FakeJoinedRoom( return sendQueueUpdates } + override fun subscribeToLiveLocationShares(): Flow> { + return liveLocationSharesFlow + } + + override suspend fun startLiveLocationShare(durationMillis: Long): Result = simulateLongTask { + startLiveLocationShareResult(durationMillis) + } + + override suspend fun stopLiveLocationShare(): Result = simulateLongTask { + stopLiveLocationShareResult() + } + + override suspend fun sendLiveLocation(geoUri: String): Result = simulateLongTask { + sendLiveLocationResult(geoUri) + } + private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) { progressCallbackValues.forEach { (current, total) -> progressCallback?.onProgress(current, total)