mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-21 05:38:49 +03:00
Merge pull request #5758 from vector-im/feature/ons/live_location_stop_sharing
Live Location Sharing - Update beacon info state event when sharing is ended
This commit is contained in:
commit
aa55b1f6b3
8 changed files with 110 additions and 8 deletions
changelog.d
matrix-sdk-android/src/main/java/org/matrix/android/sdk
vector/src/main/java/im/vector/app/features
1
changelog.d/5758.feature
Normal file
1
changelog.d/5758.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Live Location Sharing - Update beacon info state event when sharing is ended
|
|
@ -66,6 +66,19 @@ interface StateService {
|
||||||
*/
|
*/
|
||||||
suspend fun deleteAvatar()
|
suspend fun deleteAvatar()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops sharing live location in the room
|
||||||
|
* @param userId user id
|
||||||
|
*/
|
||||||
|
suspend fun stopLiveLocation(userId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns beacon info state event of a user
|
||||||
|
* @param userId user id who is sharing location
|
||||||
|
* @param filterOnlyLive filters only ongoing live location sharing beacons if true else ended event is included
|
||||||
|
*/
|
||||||
|
suspend fun getLiveLocationBeaconInfo(userId: String, filterOnlyLive: Boolean): Event?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a state event to the room
|
* Send a state event to the room
|
||||||
* @param eventType The type of event to send.
|
* @param eventType The type of event to send.
|
||||||
|
|
|
@ -21,16 +21,20 @@ import androidx.lifecycle.LiveData
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.livelocation.BeaconInfo
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.MimeTypes
|
import org.matrix.android.sdk.api.util.MimeTypes
|
||||||
|
@ -186,4 +190,42 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
||||||
}
|
}
|
||||||
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun stopLiveLocation(userId: String) {
|
||||||
|
getLiveLocationBeaconInfo(userId, true)?.let { beaconInfoStateEvent ->
|
||||||
|
beaconInfoStateEvent.getClearContent()?.toModel<LiveLocationBeaconContent>()?.let { content ->
|
||||||
|
val beaconContent = LiveLocationBeaconContent(
|
||||||
|
unstableBeaconInfo = BeaconInfo(
|
||||||
|
description = content.getBestBeaconInfo()?.description,
|
||||||
|
timeout = content.getBestBeaconInfo()?.timeout,
|
||||||
|
isLive = false,
|
||||||
|
),
|
||||||
|
unstableTimestampAsMilliseconds = System.currentTimeMillis()
|
||||||
|
).toContent()
|
||||||
|
|
||||||
|
beaconInfoStateEvent.stateKey?.let {
|
||||||
|
sendStateEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
||||||
|
body = beaconContent,
|
||||||
|
stateKey = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getLiveLocationBeaconInfo(userId: String, filterOnlyLive: Boolean): Event? {
|
||||||
|
return EventType.STATE_ROOM_BEACON_INFO
|
||||||
|
.mapNotNull {
|
||||||
|
stateEventDataSource.getStateEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
eventType = it,
|
||||||
|
stateKey = QueryStringValue.Equals(userId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.firstOrNull { beaconInfoEvent ->
|
||||||
|
!filterOnlyLive ||
|
||||||
|
beaconInfoEvent.getClearContent()?.toModel<LiveLocationBeaconContent>()?.getBestBeaconInfo()?.isLive.orFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,4 +111,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||||
|
|
||||||
// Poll
|
// Poll
|
||||||
data class EndPoll(val eventId: String) : RoomDetailAction()
|
data class EndPoll(val eventId: String) : RoomDetailAction()
|
||||||
|
|
||||||
|
// Live Location
|
||||||
|
object StopLiveLocationSharing : RoomDetailAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,6 +385,7 @@ class TimelineFragment @Inject constructor(
|
||||||
setupEmojiButton()
|
setupEmojiButton()
|
||||||
setupRemoveJitsiWidgetView()
|
setupRemoveJitsiWidgetView()
|
||||||
setupVoiceMessageView()
|
setupVoiceMessageView()
|
||||||
|
setupLiveLocationIndicator()
|
||||||
|
|
||||||
views.includeRoomToolbar.roomToolbarContentView.debouncedClicks {
|
views.includeRoomToolbar.roomToolbarContentView.debouncedClicks {
|
||||||
navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
|
navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
|
||||||
|
@ -810,6 +811,12 @@ class TimelineFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupLiveLocationIndicator() {
|
||||||
|
views.locationLiveStatusIndicator.stopButton.debouncedClicks {
|
||||||
|
timelineViewModel.handle(RoomDetailAction.StopLiveLocationSharing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
|
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
|
||||||
navigator.openRoomWidget(requireContext(), timelineArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
|
navigator.openRoomWidget(requireContext(), timelineArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,6 +444,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true))
|
_viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true))
|
||||||
}
|
}
|
||||||
is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId)
|
is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId)
|
||||||
|
RoomDetailAction.StopLiveLocationSharing -> handleStopLiveLocationSharing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1093,6 +1094,10 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleStopLiveLocationSharing() {
|
||||||
|
locationSharingServiceConnection.stopLiveLocationSharing(room.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.flow().liveRoomSummary()
|
room.flow().liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -87,7 +87,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
||||||
.getSafeActiveSession()
|
.getSafeActiveSession()
|
||||||
?.let { session ->
|
?.let { session ->
|
||||||
session.coroutineScope.launch(session.coroutineDispatchers.io) {
|
session.coroutineScope.launch(session.coroutineDispatchers.io) {
|
||||||
sendBeaconInfo(session, roomArgs)
|
sendLiveBeaconInfo(session, roomArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendBeaconInfo(session: Session, roomArgs: RoomArgs) {
|
private suspend fun sendLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
|
||||||
val beaconContent = LiveLocationBeaconContent(
|
val beaconContent = LiveLocationBeaconContent(
|
||||||
unstableBeaconInfo = BeaconInfo(
|
unstableBeaconInfo = BeaconInfo(
|
||||||
timeout = roomArgs.durationMillis,
|
timeout = roomArgs.durationMillis,
|
||||||
|
@ -129,8 +129,12 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopSharingLocation(roomId: String) {
|
fun stopSharingLocation(roomId: String) {
|
||||||
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
||||||
|
|
||||||
|
// Send a new beacon info state by setting live field as false
|
||||||
|
sendStoppedBeaconInfo(roomId)
|
||||||
|
|
||||||
synchronized(roomArgsList) {
|
synchronized(roomArgsList) {
|
||||||
roomArgsList.removeAll { it.roomId == roomId }
|
roomArgsList.removeAll { it.roomId == roomId }
|
||||||
if (roomArgsList.isEmpty()) {
|
if (roomArgsList.isEmpty()) {
|
||||||
|
@ -140,19 +144,39 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendStoppedBeaconInfo(roomId: String) {
|
||||||
|
activeSessionHolder
|
||||||
|
.getSafeActiveSession()
|
||||||
|
?.let { session ->
|
||||||
|
session.coroutineScope.launch(session.coroutineDispatchers.io) {
|
||||||
|
session.getRoom(roomId)?.stopLiveLocation(session.myUserId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLocationUpdate(locationData: LocationData) {
|
override fun onLocationUpdate(locationData: LocationData) {
|
||||||
Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
|
Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
|
||||||
|
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
// Emit location update to all rooms in which live location sharing is active
|
// Emit location update to all rooms in which live location sharing is active
|
||||||
roomArgsList.toList().forEach { roomArg ->
|
session?.coroutineScope?.launch(session.coroutineDispatchers.io) {
|
||||||
sendLiveLocation(roomArg.roomId, locationData)
|
roomArgsList.toList().forEach { roomArg ->
|
||||||
|
sendLiveLocation(roomArg.roomId, locationData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendLiveLocation(roomId: String, locationData: LocationData) {
|
private suspend fun sendLiveLocation(roomId: String, locationData: LocationData) {
|
||||||
val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomId)
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
val room = session?.getRoom(roomId)
|
||||||
|
val userId = session?.myUserId
|
||||||
|
|
||||||
|
if (room == null || userId == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
room
|
room
|
||||||
?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first())
|
.getLiveLocationBeaconInfo(userId, true)
|
||||||
?.eventId
|
?.eventId
|
||||||
?.let {
|
?.let {
|
||||||
room.sendLiveLocation(
|
room.sendLiveLocation(
|
||||||
|
|
|
@ -34,6 +34,7 @@ class LocationSharingServiceConnection @Inject constructor(
|
||||||
|
|
||||||
private var callback: Callback? = null
|
private var callback: Callback? = null
|
||||||
private var isBound = false
|
private var isBound = false
|
||||||
|
private var locationSharingService: LocationSharingService? = null
|
||||||
|
|
||||||
fun bind(callback: Callback) {
|
fun bind(callback: Callback) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
|
@ -51,13 +52,19 @@ class LocationSharingServiceConnection @Inject constructor(
|
||||||
callback = null
|
callback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun stopLiveLocationSharing(roomId: String) {
|
||||||
|
locationSharingService?.stopSharingLocation(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
||||||
|
locationSharingService = (binder as LocationSharingService.LocalBinder).getService()
|
||||||
isBound = true
|
isBound = true
|
||||||
callback?.onLocationServiceRunning()
|
callback?.onLocationServiceRunning()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(className: ComponentName) {
|
override fun onServiceDisconnected(className: ComponentName) {
|
||||||
isBound = false
|
isBound = false
|
||||||
|
locationSharingService = null
|
||||||
callback?.onLocationServiceStopped()
|
callback?.onLocationServiceStopped()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue