From c26c9ff1cc45feefd2b74bda82f32a67e2a234e4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 12:49:12 +0300 Subject: [PATCH 1/5] Send live location data. --- .../sdk/api/session/events/model/EventType.kt | 11 +--- .../livelocation/LiveLocationBeaconContent.kt | 2 +- .../message/MessageLiveLocationContent.kt | 52 +++++++++++++++++++ .../session/room/model/message/MessageType.kt | 5 +- .../sdk/api/session/room/send/SendService.kt | 9 ++++ .../session/room/send/DefaultSendService.kt | 6 +++ .../room/send/LocalEchoEventFactory.kt | 27 ++++++++++ .../location/LocationSharingService.kt | 17 ++++-- 8 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 855801e79e..fa3a9f6acd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -49,7 +49,8 @@ object EventType { const val STATE_ROOM_JOIN_RULES = "m.room.join_rules" const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access" const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels" - private const val STATE_ROOM_BEACON_INFO_PREFIX = "org.matrix.msc3489.beacon_info." + val STATE_ROOM_BEACON_INFO = listOf("org.matrix.msc3672.beacon_info", "m.beacon_info") + val BEACON_LOCATION_DATA = listOf("org.matrix.msc3672.beacon", "m.beacon") const val STATE_SPACE_CHILD = "m.space.child" @@ -121,12 +122,4 @@ object EventType { type == CALL_REJECT || type == CALL_REPLACES } - - /** - * Returns an event type like org.matrix.msc3489.beacon_info.@userid:matrix.org.1648814272273 - */ - fun generateBeaconInfoStateEventType(userId: String): String { - val uniqueId = System.currentTimeMillis() - return "$STATE_ROOM_BEACON_INFO_PREFIX$userId.$uniqueId" - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt index e08d5b629b..a4551d462e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt @@ -26,7 +26,7 @@ data class LiveLocationBeaconContent( /** * Indicates user's intent to share ephemeral location. */ - @Json(name = "org.matrix.msc3489.beacon_info") val unstableBeaconInfo: BeaconInfo? = null, + @Json(name = "org.matrix.msc3672.beacon_info") val unstableBeaconInfo: BeaconInfo? = null, @Json(name = "m.beacon_info") val beaconInfo: BeaconInfo? = null, /** * Beacon creation timestamp. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt new file mode 100644 index 0000000000..e5de46b3ef --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room.model.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Content +import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent + +@JsonClass(generateAdapter = true) +data class MessageLiveLocationContent( + /** + * Local message type, not from server + */ + @Transient + override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION, + + @Json(name = "body") override val body: String = "", + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "m.new_content") override val newContent: Content? = null, + + /** + * See [MSC3488](https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md) + */ + @Json(name = "org.matrix.msc3488.location") val unstableLocationInfo: LocationInfo? = null, + @Json(name = "m.location") val locationInfo: LocationInfo? = null, + + /** + * Exact time that the data in the event refers to (milliseconds since the UNIX epoch) + */ + @Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null, + @Json(name = "m.ts") val ts: Long? = null +) : MessageContent { + + fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo + + fun getBestTs() = ts ?: unstableTs +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt index 2a6138ae60..280699cde8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt @@ -33,10 +33,13 @@ object MessageType { const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker" // Fake message types for poll events to be able to inherit them from MessageContent - // Because poll events are not message events and they don't hanve msgtype field + // Because poll events are not message events and they don't have msgtype field const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start" const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response" const val MSGTYPE_CONFETTI = "nic.custom.confetti" const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall" + + // Fake message types for live location events to be able to inherit them from MessageContent + const val MSGTYPE_LIVE_LOCATION = "org.matrix.android.sdk.livelocation" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 9f8b1d93d7..af7ab11df1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -146,6 +146,15 @@ interface SendService { */ fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable + /** + * Send a live location event to the room. beacon_info state event has to be sent before sending live location updates. + * @param beaconInfoEventId event id of the initial beacon info state event + * @param latitude required latitude of the location + * @param longitude required longitude of the location + * @param uncertainty Accuracy of the location in meters + */ + fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable + /** * Remove this failed message from the timeline * @param localEcho the unsent local echo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index 31c7254ed5..c315bf9f7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -134,6 +134,12 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } + override fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { + return localEchoEventFactory.createLiveLocationEvent(beaconInfoEventId, roomId, latitude, longitude, uncertainty) + .also { createLocalEcho(it) } + .let { sendEvent(it) } + } + override fun redactEvent(event: Event, reason: String?): Cancelable { // TODO manage media/attachements? val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 0ba95cc1fb..d7d74ca601 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -43,6 +43,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollConte import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageFormat import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent @@ -242,6 +243,32 @@ internal class LocalEchoEventFactory @Inject constructor( return createMessageEvent(roomId, content) } + fun createLiveLocationEvent(beaconInfoEventId: String, + roomId: String, + latitude: Double, + longitude: Double, + uncertainty: Double?): Event { + val geoUri = buildGeoUri(latitude, longitude, uncertainty) + val content = MessageLiveLocationContent( + body = geoUri, + relatesTo = RelationDefaultContent( + type = RelationType.REFERENCE, + eventId = beaconInfoEventId + ), + unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri), + unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + ) + val localId = LocalEcho.createLocalEchoId() + return Event( + roomId = roomId, + originServerTs = dummyOriginServerTs(), + senderId = userId, + eventId = localId, + type = EventType.BEACON_LOCATION_DATA.first(), + content = content.toContent(), + unsignedData = UnsignedData(age = null, transactionId = localId)) + } + fun createReplaceTextOfReply(roomId: String, eventReplaced: TimelineEvent, originalEvent: TimelineEvent, diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 063cc3caa6..f9a32ad549 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -29,8 +29,9 @@ import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.EventType.generateBeaconInfoStateEventType +import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.livelocation.BeaconInfo import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import timber.log.Timber @@ -104,12 +105,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { unstableTimestampAsMilliseconds = clock.epochMillis() ).toContent() - val eventType = generateBeaconInfoStateEventType(session.myUserId) val stateKey = session.myUserId session .getRoom(roomArgs.roomId) ?.sendStateEvent( - eventType = eventType, + eventType = EventType.STATE_ROOM_BEACON_INFO.first(), stateKey = stateKey, body = beaconContent ) @@ -143,6 +143,17 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onLocationUpdate(locationData: LocationData) { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") + roomArgsList.forEach { roomArg -> + val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomArg.roomId) + room?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first())?.let { beaconInfoEvent -> + room.sendLiveLocation( + beaconInfoEventId = beaconInfoEvent.eventId!!, + latitude = locationData.latitude, + longitude = locationData.longitude, + uncertainty = locationData.uncertainty + ) + } + } } override fun onLocationProviderIsNotAvailable() { From f00fc1e5ab04a09b604e27c55f94f7128c2c68b5 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 12:55:50 +0300 Subject: [PATCH 2/5] Changelog added. --- changelog.d/5697.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5697.feature diff --git a/changelog.d/5697.feature b/changelog.d/5697.feature new file mode 100644 index 0000000000..47504084f4 --- /dev/null +++ b/changelog.d/5697.feature @@ -0,0 +1 @@ +Live Location Sharing - Send location data \ No newline at end of file From db45ebd012ea9038ddfd2c2d5267483711e4644e Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 13:05:09 +0300 Subject: [PATCH 3/5] Lint fixes. --- .../im/vector/app/features/location/LocationSharingService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index f9a32ad549..38e2b3e6a6 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -31,7 +31,6 @@ import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent -import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.livelocation.BeaconInfo import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import timber.log.Timber From dbb43fe046f7105660a0ca8e4a5cfcba7a3fe187 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 15:42:01 +0300 Subject: [PATCH 4/5] Refactor code to avoid force unwrapping. --- .../location/LocationSharingService.kt | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 38e2b3e6a6..81aeeee602 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -142,19 +142,28 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onLocationUpdate(locationData: LocationData) { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") + + // Emit location update to all rooms in which live location sharing is active roomArgsList.forEach { roomArg -> - val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomArg.roomId) - room?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first())?.let { beaconInfoEvent -> - room.sendLiveLocation( - beaconInfoEventId = beaconInfoEvent.eventId!!, - latitude = locationData.latitude, - longitude = locationData.longitude, - uncertainty = locationData.uncertainty - ) - } + sendLiveLocation(roomArg.roomId, locationData) } } + private fun sendLiveLocation(roomId: String, locationData: LocationData) { + val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomId) + room + ?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first()) + ?.eventId + ?.let { + room.sendLiveLocation( + beaconInfoEventId = it, + latitude = locationData.latitude, + longitude = locationData.longitude, + uncertainty = locationData.uncertainty + ) + } + } + override fun onLocationProviderIsNotAvailable() { stopForeground(true) stopSelf() From e0d59efd860552fb05e409db961cd51123872ab2 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 19:08:11 +0300 Subject: [PATCH 5/5] Secure list while sending the location. --- .../im/vector/app/features/location/LocationSharingService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 81aeeee602..85679e34a7 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -144,7 +144,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") // Emit location update to all rooms in which live location sharing is active - roomArgsList.forEach { roomArg -> + roomArgsList.toList().forEach { roomArg -> sendLiveLocation(roomArg.roomId, locationData) } }