diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index f4a937967e..deaf91a9d1 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -14,6 +14,7 @@ mod error; mod event; mod helpers; mod identity_status_change; +mod live_location_share; mod notification; mod notification_settings; mod platform; diff --git a/bindings/matrix-sdk-ffi/src/live_location_share.rs b/bindings/matrix-sdk-ffi/src/live_location_share.rs new file mode 100644 index 0000000000..940fb48ca3 --- /dev/null +++ b/bindings/matrix-sdk-ffi/src/live_location_share.rs @@ -0,0 +1,32 @@ +// Copyright 2024 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. +use crate::ruma::LocationContent; +#[derive(uniffi::Record)] +pub struct LastLocation { + /// The most recent location content of the user. + pub location: LocationContent, + /// A timestamp in milliseconds since Unix Epoch on that day in local + /// time. + pub ts: u64, +} +/// Details of a users live location share. +#[derive(uniffi::Record)] +pub struct LiveLocationShare { + /// The user's last known location. + pub last_location: LastLocation, + /// The live status of the live location share. + pub(crate) is_live: bool, + /// The user ID of the person sharing their live location. + pub user_id: String, +} diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index 08e9fdbee0..818ac3ff33 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -28,7 +28,7 @@ use ruma::{ EventId, Int, OwnedDeviceId, OwnedUserId, RoomAliasId, UserId, }; use tokio::sync::RwLock; -use tracing::error; +use tracing::{error, warn}; use super::RUNTIME; use crate::{ @@ -37,9 +37,10 @@ use crate::{ error::{ClientError, MediaInfoError, NotYetImplemented, RoomError}, event::{MessageLikeEventType, StateEventType}, identity_status_change::IdentityStatusChange, + live_location_share::{LastLocation, LiveLocationShare}, room_info::RoomInfo, room_member::RoomMember, - ruma::{ImageInfo, Mentions, NotifyType}, + ruma::{ImageInfo, LocationContent, Mentions, NotifyType}, timeline::{ configuration::{AllowedMessageTypes, TimelineConfiguration}, ReceiptType, SendHandle, Timeline, @@ -969,6 +970,75 @@ impl Room { let visibility = self.inner.privacy_settings().get_room_visibility().await?; Ok(visibility.into()) } + + /// Start the current users live location share in the room. + pub async fn start_live_location_share(&self, duration_millis: u64) -> Result<(), ClientError> { + self.inner.start_live_location_share(duration_millis, None).await?; + Ok(()) + } + + /// Stop the current users live location share in the room. + pub async fn stop_live_location_share(&self) -> Result<(), ClientError> { + self.inner.stop_live_location_share().await.expect("Unable to stop live location share"); + Ok(()) + } + + /// Send the current users live location beacon in the room. + pub async fn send_live_location(&self, geo_uri: String) -> Result<(), ClientError> { + self.inner + .send_location_beacon(geo_uri) + .await + .expect("Unable to send live location beacon"); + Ok(()) + } + + /// Subscribes to live location shares in this room, using a `listener` to + /// be notified of the changes. + /// + /// The current live location shares will be emitted immediately when + /// subscribing, along with a [`TaskHandle`] to cancel the subscription. + pub fn subscribe_to_live_location_shares( + self: Arc, + listener: Box, + ) -> Arc { + let room = self.inner.clone(); + + Arc::new(TaskHandle::new(RUNTIME.spawn(async move { + let subscription = room.observe_live_location_shares(); + let mut stream = subscription.subscribe(); + let mut pinned_stream = pin!(stream); + + while let Some(event) = pinned_stream.next().await { + let last_location = LocationContent { + body: "".to_owned(), + geo_uri: event.last_location.location.uri.clone().to_string(), + description: None, + zoom_level: None, + asset: None, + }; + + let Some(beacon_info) = event.beacon_info else { + warn!("Live location share is missing the associated beacon_info state, skipping event."); + continue; + }; + + listener.call(vec![LiveLocationShare { + last_location: LastLocation { + location: last_location, + ts: event.last_location.ts.0.into(), + }, + is_live: beacon_info.is_live(), + user_id: event.user_id.to_string(), + }]) + } + }))) + } +} + +/// A listener for receiving new live location shares in a room. +#[matrix_sdk_ffi_macros::export(callback_interface)] +pub trait LiveLocationShareListener: Sync + Send { + fn call(&self, live_location_shares: Vec); } impl From for KnockRequest {