Merge pull request #6304 from vector-im/feature/mna/location-sharing-service-api
[SDK] Improve location sharing service api (PSF-1004)
This commit is contained in:
commit
0948cab31f
1
changelog.d/5864.sdk
Normal file
1
changelog.d/5864.sdk
Normal file
@ -0,0 +1 @@
|
|||||||
|
Group all location sharing related API into LocationSharingService
|
@ -18,10 +18,45 @@ package org.matrix.android.sdk.api.session.room.location
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage all location sharing related features.
|
* Manage all location sharing related features.
|
||||||
*/
|
*/
|
||||||
interface LocationSharingService {
|
interface LocationSharingService {
|
||||||
|
/**
|
||||||
|
* Send a static location event to the room.
|
||||||
|
* @param latitude required latitude of the location
|
||||||
|
* @param longitude required longitude of the location
|
||||||
|
* @param uncertainty Accuracy of the location in meters
|
||||||
|
* @param isUserLocation indicates whether the location data corresponds to the user location or not (pinned location)
|
||||||
|
*/
|
||||||
|
suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a live location event to the room.
|
||||||
|
* To get the beacon info event id, [startLiveLocationShare] must be called 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
|
||||||
|
*/
|
||||||
|
suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts sharing live location in the room.
|
||||||
|
* @param timeoutMillis timeout of the live in milliseconds
|
||||||
|
* @return the id of the created beacon info event
|
||||||
|
*/
|
||||||
|
suspend fun startLiveLocationShare(timeoutMillis: Long): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops sharing live location in the room.
|
||||||
|
*/
|
||||||
|
suspend fun stopLiveLocationShare()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a LiveData on the list of current running live location shares.
|
||||||
|
*/
|
||||||
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
||||||
}
|
}
|
||||||
|
@ -142,24 +142,6 @@ interface SendService {
|
|||||||
*/
|
*/
|
||||||
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable
|
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a location event to the room.
|
|
||||||
* @param latitude required latitude of the location
|
|
||||||
* @param longitude required longitude of the location
|
|
||||||
* @param uncertainty Accuracy of the location in meters
|
|
||||||
* @param isUserLocation indicates whether the location data corresponds to the user location or not
|
|
||||||
*/
|
|
||||||
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.
|
* Remove this failed message from the timeline.
|
||||||
* @param localEcho the unsent local echo
|
* @param localEcho the unsent local echo
|
||||||
|
@ -66,19 +66,6 @@ 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.
|
||||||
|
@ -16,15 +16,17 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.database.mapper
|
package org.matrix.android.sdk.internal.database.mapper
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() {
|
internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() :
|
||||||
|
Monarchy.Mapper<LiveLocationShareAggregatedSummary, LiveLocationShareAggregatedSummaryEntity> {
|
||||||
|
|
||||||
fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary {
|
override fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary {
|
||||||
return LiveLocationShareAggregatedSummary(
|
return LiveLocationShareAggregatedSummary(
|
||||||
userId = entity.userId,
|
userId = entity.userId,
|
||||||
isActive = entity.isActive,
|
isActive = entity.isActive,
|
||||||
|
@ -51,6 +51,14 @@ import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDire
|
|||||||
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
|
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
|
||||||
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
|
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
|
||||||
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
|
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.DefaultSendLiveLocationTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.SendLiveLocationTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.location.StopLiveLocationShareTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask
|
import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask
|
||||||
@ -299,4 +307,16 @@ internal abstract class RoomModule {
|
|||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindFetchThreadSummariesTask(task: DefaultFetchThreadSummariesTask): FetchThreadSummariesTask
|
abstract fun bindFetchThreadSummariesTask(task: DefaultFetchThreadSummariesTask): FetchThreadSummariesTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindStartLiveLocationShareTask(task: DefaultStartLiveLocationShareTask): StartLiveLocationShareTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindStopLiveLocationShareTask(task: DefaultStopLiveLocationShareTask): StopLiveLocationShareTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindSendStaticLocationTask(task: DefaultSendStaticLocationTask): SendStaticLocationTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindSendLiveLocationTask(task: DefaultSendLiveLocationTask): SendLiveLocationTask
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,19 @@ import dagger.assisted.AssistedFactory
|
|||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import org.matrix.android.sdk.api.session.room.location.LocationSharingService
|
import org.matrix.android.sdk.api.session.room.location.LocationSharingService
|
||||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper
|
import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom
|
import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
|
||||||
// TODO add unit tests
|
|
||||||
internal class DefaultLocationSharingService @AssistedInject constructor(
|
internal class DefaultLocationSharingService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
private val sendStaticLocationTask: SendStaticLocationTask,
|
||||||
|
private val sendLiveLocationTask: SendLiveLocationTask,
|
||||||
|
private val startLiveLocationShareTask: StartLiveLocationShareTask,
|
||||||
|
private val stopLiveLocationShareTask: StopLiveLocationShareTask,
|
||||||
private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
|
private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
|
||||||
) : LocationSharingService {
|
) : LocationSharingService {
|
||||||
|
|
||||||
@ -40,10 +44,47 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
|
|||||||
fun create(roomId: String): DefaultLocationSharingService
|
fun create(roomId: String): DefaultLocationSharingService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable {
|
||||||
|
val params = SendStaticLocationTask.Params(
|
||||||
|
roomId = roomId,
|
||||||
|
latitude = latitude,
|
||||||
|
longitude = longitude,
|
||||||
|
uncertainty = uncertainty,
|
||||||
|
isUserLocation = isUserLocation,
|
||||||
|
)
|
||||||
|
return sendStaticLocationTask.execute(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable {
|
||||||
|
val params = SendLiveLocationTask.Params(
|
||||||
|
beaconInfoEventId = beaconInfoEventId,
|
||||||
|
roomId = roomId,
|
||||||
|
latitude = latitude,
|
||||||
|
longitude = longitude,
|
||||||
|
uncertainty = uncertainty,
|
||||||
|
)
|
||||||
|
return sendLiveLocationTask.execute(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun startLiveLocationShare(timeoutMillis: Long): String {
|
||||||
|
val params = StartLiveLocationShareTask.Params(
|
||||||
|
roomId = roomId,
|
||||||
|
timeoutMillis = timeoutMillis
|
||||||
|
)
|
||||||
|
return startLiveLocationShareTask.execute(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun stopLiveLocationShare() {
|
||||||
|
val params = StopLiveLocationShareTask.Params(
|
||||||
|
roomId = roomId,
|
||||||
|
)
|
||||||
|
return stopLiveLocationShareTask.execute(params)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> {
|
override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
return monarchy.findAllMappedWithChanges(
|
||||||
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) },
|
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) },
|
||||||
{ liveLocationShareAggregatedSummaryMapper.map(it) }
|
liveLocationShareAggregatedSummaryMapper
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface SendLiveLocationTask : Task<SendLiveLocationTask.Params, Cancelable> {
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
val beaconInfoEventId: String,
|
||||||
|
val latitude: Double,
|
||||||
|
val longitude: Double,
|
||||||
|
val uncertainty: Double?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultSendLiveLocationTask @Inject constructor(
|
||||||
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
|
) : SendLiveLocationTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: SendLiveLocationTask.Params): Cancelable {
|
||||||
|
val event = localEchoEventFactory.createLiveLocationEvent(
|
||||||
|
beaconInfoEventId = params.beaconInfoEventId,
|
||||||
|
roomId = params.roomId,
|
||||||
|
latitude = params.latitude,
|
||||||
|
longitude = params.longitude,
|
||||||
|
uncertainty = params.uncertainty,
|
||||||
|
)
|
||||||
|
localEchoEventFactory.createLocalEcho(event)
|
||||||
|
return eventSenderProcessor.postEvent(event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface SendStaticLocationTask : Task<SendStaticLocationTask.Params, Cancelable> {
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
val latitude: Double,
|
||||||
|
val longitude: Double,
|
||||||
|
val uncertainty: Double?,
|
||||||
|
val isUserLocation: Boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultSendStaticLocationTask @Inject constructor(
|
||||||
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
|
) : SendStaticLocationTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: SendStaticLocationTask.Params): Cancelable {
|
||||||
|
val event = localEchoEventFactory.createStaticLocationEvent(
|
||||||
|
roomId = params.roomId,
|
||||||
|
latitude = params.latitude,
|
||||||
|
longitude = params.longitude,
|
||||||
|
uncertainty = params.uncertainty,
|
||||||
|
isUserLocation = params.isUserLocation
|
||||||
|
)
|
||||||
|
localEchoEventFactory.createLocalEcho(event)
|
||||||
|
return eventSenderProcessor.postEvent(event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
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.model.message.MessageBeaconInfoContent
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface StartLiveLocationShareTask : Task<StartLiveLocationShareTask.Params, String> {
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
val timeoutMillis: Long,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultStartLiveLocationShareTask @Inject constructor(
|
||||||
|
@UserId private val userId: String,
|
||||||
|
private val clock: Clock,
|
||||||
|
private val sendStateTask: SendStateTask,
|
||||||
|
) : StartLiveLocationShareTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: StartLiveLocationShareTask.Params): String {
|
||||||
|
val beaconContent = MessageBeaconInfoContent(
|
||||||
|
timeout = params.timeoutMillis,
|
||||||
|
isLive = true,
|
||||||
|
unstableTimestampMillis = clock.epochMillis()
|
||||||
|
).toContent()
|
||||||
|
val eventType = EventType.STATE_ROOM_BEACON_INFO.first()
|
||||||
|
val sendStateTaskParams = SendStateTask.Params(
|
||||||
|
roomId = params.roomId,
|
||||||
|
stateKey = userId,
|
||||||
|
eventType = eventType,
|
||||||
|
body = beaconContent
|
||||||
|
)
|
||||||
|
return sendStateTask.executeRetry(sendStateTaskParams, 3)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
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.EventType
|
||||||
|
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.message.MessageBeaconInfoContent
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface StopLiveLocationShareTask : Task<StopLiveLocationShareTask.Params, Unit> {
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultStopLiveLocationShareTask @Inject constructor(
|
||||||
|
@UserId private val userId: String,
|
||||||
|
private val sendStateTask: SendStateTask,
|
||||||
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
|
) : StopLiveLocationShareTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: StopLiveLocationShareTask.Params) {
|
||||||
|
val beaconInfoStateEvent = getLiveLocationBeaconInfoForUser(userId, params.roomId) ?: return
|
||||||
|
val stateKey = beaconInfoStateEvent.stateKey ?: return
|
||||||
|
val content = beaconInfoStateEvent.getClearContent()?.toModel<MessageBeaconInfoContent>() ?: return
|
||||||
|
val updatedContent = content.copy(isLive = false).toContent()
|
||||||
|
val sendStateTaskParams = SendStateTask.Params(
|
||||||
|
roomId = params.roomId,
|
||||||
|
stateKey = stateKey,
|
||||||
|
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
||||||
|
body = updatedContent
|
||||||
|
)
|
||||||
|
sendStateTask.executeRetry(sendStateTaskParams, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLiveLocationBeaconInfoForUser(userId: String, roomId: String): Event? {
|
||||||
|
return EventType.STATE_ROOM_BEACON_INFO
|
||||||
|
.mapNotNull {
|
||||||
|
stateEventDataSource.getStateEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
eventType = it,
|
||||||
|
stateKey = QueryStringValue.Equals(userId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.firstOrNull { beaconInfoEvent ->
|
||||||
|
beaconInfoEvent.getClearContent()?.toModel<MessageBeaconInfoContent>()?.isLive.orFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -129,18 +129,6 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable {
|
|
||||||
return localEchoEventFactory.createLocationEvent(roomId, latitude, longitude, uncertainty, isUserLocation)
|
|
||||||
.also { createLocalEcho(it) }
|
|
||||||
.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 {
|
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
||||||
// TODO manage media/attachements?
|
// TODO manage media/attachements?
|
||||||
val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason)
|
val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason)
|
||||||
|
@ -244,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createLocationEvent(
|
fun createStaticLocationEvent(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
latitude: Double,
|
latitude: Double,
|
||||||
longitude: Double,
|
longitude: Double,
|
||||||
|
@ -21,33 +21,27 @@ 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.QueryStateEventValue
|
import org.matrix.android.sdk.api.query.QueryStateEventValue
|
||||||
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.message.MessageBeaconInfoContent
|
|
||||||
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
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.session.content.FileUploader
|
import org.matrix.android.sdk.internal.session.content.FileUploader
|
||||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
|
||||||
|
|
||||||
internal class DefaultStateService @AssistedInject constructor(
|
internal class DefaultStateService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
private val stateEventDataSource: StateEventDataSource,
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
private val sendStateTask: SendStateTask,
|
private val sendStateTask: SendStateTask,
|
||||||
private val fileUploader: FileUploader,
|
private val fileUploader: FileUploader,
|
||||||
private val viaParameterFinder: ViaParameterFinder
|
|
||||||
) : StateService {
|
) : StateService {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -191,35 +185,4 @@ internal class DefaultStateService @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun stopLiveLocation(userId: String) {
|
|
||||||
getLiveLocationBeaconInfo(userId, true)?.let { beaconInfoStateEvent ->
|
|
||||||
beaconInfoStateEvent.getClearContent()?.toModel<MessageBeaconInfoContent>()?.let { content ->
|
|
||||||
val updatedContent = content.copy(isLive = false).toContent()
|
|
||||||
|
|
||||||
beaconInfoStateEvent.stateKey?.let {
|
|
||||||
sendStateEvent(
|
|
||||||
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
|
||||||
body = updatedContent,
|
|
||||||
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<MessageBeaconInfoContent>()?.isLive.orFalse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,12 @@ import org.amshove.kluent.shouldBeEqualTo
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.pushers.PusherState
|
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||||
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.PusherEntityFields
|
||||||
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
||||||
import org.matrix.android.sdk.test.fakes.FakePushersAPI
|
import org.matrix.android.sdk.test.fakes.FakePushersAPI
|
||||||
import org.matrix.android.sdk.test.fakes.FakeRequestExecutor
|
import org.matrix.android.sdk.test.fakes.FakeRequestExecutor
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||||
import java.net.SocketException
|
import java.net.SocketException
|
||||||
|
|
||||||
private val A_JSON_PUSHER = JsonPusher(
|
private val A_JSON_PUSHER = JsonPusher(
|
||||||
@ -56,6 +58,7 @@ class DefaultAddPusherTaskTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() {
|
fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() {
|
||||||
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
||||||
|
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
|
||||||
|
|
||||||
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||||
|
|
||||||
@ -71,6 +74,7 @@ class DefaultAddPusherTaskTest {
|
|||||||
fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() {
|
fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() {
|
||||||
val realmResult = PusherEntity(appDisplayName = null)
|
val realmResult = PusherEntity(appDisplayName = null)
|
||||||
monarchy.givenWhereReturns(result = realmResult)
|
monarchy.givenWhereReturns(result = realmResult)
|
||||||
|
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
|
||||||
|
|
||||||
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||||
|
|
||||||
@ -84,6 +88,7 @@ class DefaultAddPusherTaskTest {
|
|||||||
fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows`() {
|
fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows`() {
|
||||||
val realmResult = PusherEntity()
|
val realmResult = PusherEntity()
|
||||||
monarchy.givenWhereReturns(result = realmResult)
|
monarchy.givenWhereReturns(result = realmResult)
|
||||||
|
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
|
||||||
pushersAPI.givenSetPusherErrors(SocketException())
|
pushersAPI.givenSetPusherErrors(SocketException())
|
||||||
|
|
||||||
assertFailsWith<SocketException> {
|
assertFailsWith<SocketException> {
|
||||||
@ -96,6 +101,7 @@ class DefaultAddPusherTaskTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() {
|
fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() {
|
||||||
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
||||||
|
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
|
||||||
pushersAPI.givenSetPusherErrors(SocketException())
|
pushersAPI.givenSetPusherErrors(SocketException())
|
||||||
|
|
||||||
assertFailsWith<SocketException> {
|
assertFailsWith<SocketException> {
|
||||||
|
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper
|
||||||
|
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenIsNotEmpty
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenIsNotNull
|
||||||
|
|
||||||
|
private const val A_ROOM_ID = "room_id"
|
||||||
|
private const val AN_EVENT_ID = "event_id"
|
||||||
|
private const val A_LATITUDE = 1.4
|
||||||
|
private const val A_LONGITUDE = 40.0
|
||||||
|
private const val AN_UNCERTAINTY = 5.0
|
||||||
|
private const val A_TIMEOUT = 15_000L
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
internal class DefaultLocationSharingServiceTest {
|
||||||
|
|
||||||
|
private val fakeRoomId = A_ROOM_ID
|
||||||
|
private val fakeMonarchy = FakeMonarchy()
|
||||||
|
private val sendStaticLocationTask = mockk<SendStaticLocationTask>()
|
||||||
|
private val sendLiveLocationTask = mockk<SendLiveLocationTask>()
|
||||||
|
private val startLiveLocationShareTask = mockk<StartLiveLocationShareTask>()
|
||||||
|
private val stopLiveLocationShareTask = mockk<StopLiveLocationShareTask>()
|
||||||
|
private val fakeLiveLocationShareAggregatedSummaryMapper = mockk<LiveLocationShareAggregatedSummaryMapper>()
|
||||||
|
|
||||||
|
private val defaultLocationSharingService = DefaultLocationSharingService(
|
||||||
|
roomId = fakeRoomId,
|
||||||
|
monarchy = fakeMonarchy.instance,
|
||||||
|
sendStaticLocationTask = sendStaticLocationTask,
|
||||||
|
sendLiveLocationTask = sendLiveLocationTask,
|
||||||
|
startLiveLocationShareTask = startLiveLocationShareTask,
|
||||||
|
stopLiveLocationShareTask = stopLiveLocationShareTask,
|
||||||
|
liveLocationShareAggregatedSummaryMapper = fakeLiveLocationShareAggregatedSummaryMapper
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `static location can be sent`() = runTest {
|
||||||
|
val isUserLocation = true
|
||||||
|
val cancelable = mockk<Cancelable>()
|
||||||
|
coEvery { sendStaticLocationTask.execute(any()) } returns cancelable
|
||||||
|
|
||||||
|
val result = defaultLocationSharingService.sendStaticLocation(
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY,
|
||||||
|
isUserLocation = isUserLocation
|
||||||
|
)
|
||||||
|
|
||||||
|
result shouldBeEqualTo cancelable
|
||||||
|
val expectedParams = SendStaticLocationTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY,
|
||||||
|
isUserLocation = isUserLocation,
|
||||||
|
)
|
||||||
|
coVerify { sendStaticLocationTask.execute(expectedParams) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `live location can be sent`() = runTest {
|
||||||
|
val cancelable = mockk<Cancelable>()
|
||||||
|
coEvery { sendLiveLocationTask.execute(any()) } returns cancelable
|
||||||
|
|
||||||
|
val result = defaultLocationSharingService.sendLiveLocation(
|
||||||
|
beaconInfoEventId = AN_EVENT_ID,
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY
|
||||||
|
)
|
||||||
|
|
||||||
|
result shouldBeEqualTo cancelable
|
||||||
|
val expectedParams = SendLiveLocationTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
beaconInfoEventId = AN_EVENT_ID,
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY
|
||||||
|
)
|
||||||
|
coVerify { sendLiveLocationTask.execute(expectedParams) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `live location share can be started with a given timeout`() = runTest {
|
||||||
|
coEvery { startLiveLocationShareTask.execute(any()) } returns AN_EVENT_ID
|
||||||
|
|
||||||
|
val eventId = defaultLocationSharingService.startLiveLocationShare(A_TIMEOUT)
|
||||||
|
|
||||||
|
eventId shouldBeEqualTo AN_EVENT_ID
|
||||||
|
val expectedParams = StartLiveLocationShareTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
timeoutMillis = A_TIMEOUT
|
||||||
|
)
|
||||||
|
coVerify { startLiveLocationShareTask.execute(expectedParams) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `live location share can be stopped`() = runTest {
|
||||||
|
coEvery { stopLiveLocationShareTask.execute(any()) } just runs
|
||||||
|
|
||||||
|
defaultLocationSharingService.stopLiveLocationShare()
|
||||||
|
|
||||||
|
val expectedParams = StopLiveLocationShareTask.Params(
|
||||||
|
roomId = A_ROOM_ID
|
||||||
|
)
|
||||||
|
coVerify { stopLiveLocationShareTask.execute(expectedParams) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `livedata of live summaries is correctly computed`() {
|
||||||
|
val entity = LiveLocationShareAggregatedSummaryEntity()
|
||||||
|
val summary = LiveLocationShareAggregatedSummary(
|
||||||
|
userId = "",
|
||||||
|
isActive = true,
|
||||||
|
endOfLiveTimestampMillis = 123,
|
||||||
|
lastLocationDataContent = null
|
||||||
|
)
|
||||||
|
|
||||||
|
fakeMonarchy.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
|
||||||
|
.givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, fakeRoomId)
|
||||||
|
.givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true)
|
||||||
|
.givenIsNotEmpty(LiveLocationShareAggregatedSummaryEntityFields.USER_ID)
|
||||||
|
.givenIsNotNull(LiveLocationShareAggregatedSummaryEntityFields.LAST_LOCATION_CONTENT)
|
||||||
|
fakeMonarchy.givenFindAllMappedWithChangesReturns(
|
||||||
|
realmEntities = listOf(entity),
|
||||||
|
mappedResult = listOf(summary),
|
||||||
|
fakeLiveLocationShareAggregatedSummaryMapper
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = defaultLocationSharingService.getRunningLiveLocationShareSummaries().value
|
||||||
|
|
||||||
|
result shouldBeEqualTo listOf(summary)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeLocalEchoEventFactory
|
||||||
|
|
||||||
|
private const val A_ROOM_ID = "room_id"
|
||||||
|
private const val AN_EVENT_ID = "event_id"
|
||||||
|
private const val A_LATITUDE = 1.4
|
||||||
|
private const val A_LONGITUDE = 44.0
|
||||||
|
private const val AN_UNCERTAINTY = 5.0
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
internal class DefaultSendLiveLocationTaskTest {
|
||||||
|
|
||||||
|
private val fakeLocalEchoEventFactory = FakeLocalEchoEventFactory()
|
||||||
|
private val fakeEventSenderProcessor = FakeEventSenderProcessor()
|
||||||
|
|
||||||
|
private val defaultSendLiveLocationTask = DefaultSendLiveLocationTask(
|
||||||
|
localEchoEventFactory = fakeLocalEchoEventFactory.instance,
|
||||||
|
eventSenderProcessor = fakeEventSenderProcessor
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given parameters when calling the task then it is correctly executed`() = runTest {
|
||||||
|
val params = SendLiveLocationTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
beaconInfoEventId = AN_EVENT_ID,
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY
|
||||||
|
)
|
||||||
|
val event = fakeLocalEchoEventFactory.givenCreateLiveLocationEvent(
|
||||||
|
withLocalEcho = true
|
||||||
|
)
|
||||||
|
val cancelable = mockk<Cancelable>()
|
||||||
|
fakeEventSenderProcessor.givenPostEventReturns(event, cancelable)
|
||||||
|
|
||||||
|
val result = defaultSendLiveLocationTask.execute(params)
|
||||||
|
|
||||||
|
result shouldBeEqualTo cancelable
|
||||||
|
fakeLocalEchoEventFactory.verifyCreateLiveLocationEvent(
|
||||||
|
roomId = params.roomId,
|
||||||
|
beaconInfoEventId = params.beaconInfoEventId,
|
||||||
|
latitude = params.latitude,
|
||||||
|
longitude = params.longitude,
|
||||||
|
uncertainty = params.uncertainty
|
||||||
|
)
|
||||||
|
fakeLocalEchoEventFactory.verifyCreateLocalEcho(event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeLocalEchoEventFactory
|
||||||
|
|
||||||
|
private const val A_ROOM_ID = "room_id"
|
||||||
|
private const val A_LATITUDE = 1.4
|
||||||
|
private const val A_LONGITUDE = 44.0
|
||||||
|
private const val AN_UNCERTAINTY = 5.0
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
internal class DefaultSendStaticLocationTaskTest {
|
||||||
|
|
||||||
|
private val fakeLocalEchoEventFactory = FakeLocalEchoEventFactory()
|
||||||
|
private val fakeEventSenderProcessor = FakeEventSenderProcessor()
|
||||||
|
|
||||||
|
private val defaultSendStaticLocationTask = DefaultSendStaticLocationTask(
|
||||||
|
localEchoEventFactory = fakeLocalEchoEventFactory.instance,
|
||||||
|
eventSenderProcessor = fakeEventSenderProcessor
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given parameters when calling the task then it is correctly executed`() = runTest {
|
||||||
|
val params = SendStaticLocationTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
latitude = A_LATITUDE,
|
||||||
|
longitude = A_LONGITUDE,
|
||||||
|
uncertainty = AN_UNCERTAINTY,
|
||||||
|
isUserLocation = true
|
||||||
|
)
|
||||||
|
val event = fakeLocalEchoEventFactory.givenCreateStaticLocationEvent(
|
||||||
|
withLocalEcho = true
|
||||||
|
)
|
||||||
|
val cancelable = mockk<Cancelable>()
|
||||||
|
fakeEventSenderProcessor.givenPostEventReturns(event, cancelable)
|
||||||
|
|
||||||
|
val result = defaultSendStaticLocationTask.execute(params)
|
||||||
|
|
||||||
|
result shouldBeEqualTo cancelable
|
||||||
|
fakeLocalEchoEventFactory.verifyCreateStaticLocationEvent(
|
||||||
|
roomId = params.roomId,
|
||||||
|
latitude = params.latitude,
|
||||||
|
longitude = params.longitude,
|
||||||
|
uncertainty = params.uncertainty,
|
||||||
|
isUserLocation = params.isUserLocation
|
||||||
|
)
|
||||||
|
fakeLocalEchoEventFactory.verifyCreateLocalEcho(event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
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.model.message.MessageBeaconInfoContent
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeClock
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeSendStateTask
|
||||||
|
|
||||||
|
private const val A_USER_ID = "user-id"
|
||||||
|
private const val A_ROOM_ID = "room-id"
|
||||||
|
private const val AN_EVENT_ID = "event-id"
|
||||||
|
private const val A_TIMEOUT = 15_000L
|
||||||
|
private const val AN_EPOCH = 1655210176L
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
internal class DefaultStartLiveLocationShareTaskTest {
|
||||||
|
|
||||||
|
private val fakeClock = FakeClock()
|
||||||
|
private val fakeSendStateTask = FakeSendStateTask()
|
||||||
|
|
||||||
|
private val defaultStartLiveLocationShareTask = DefaultStartLiveLocationShareTask(
|
||||||
|
userId = A_USER_ID,
|
||||||
|
clock = fakeClock,
|
||||||
|
sendStateTask = fakeSendStateTask
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given parameters when calling the task then it is correctly executed`() = runTest {
|
||||||
|
val params = StartLiveLocationShareTask.Params(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
timeoutMillis = A_TIMEOUT
|
||||||
|
)
|
||||||
|
fakeClock.givenEpoch(AN_EPOCH)
|
||||||
|
fakeSendStateTask.givenExecuteRetryReturns(AN_EVENT_ID)
|
||||||
|
|
||||||
|
val result = defaultStartLiveLocationShareTask.execute(params)
|
||||||
|
|
||||||
|
result shouldBeEqualTo AN_EVENT_ID
|
||||||
|
val expectedBeaconContent = MessageBeaconInfoContent(
|
||||||
|
timeout = params.timeoutMillis,
|
||||||
|
isLive = true,
|
||||||
|
unstableTimestampMillis = AN_EPOCH
|
||||||
|
).toContent()
|
||||||
|
val expectedParams = SendStateTask.Params(
|
||||||
|
roomId = params.roomId,
|
||||||
|
stateKey = A_USER_ID,
|
||||||
|
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
||||||
|
body = expectedBeaconContent
|
||||||
|
)
|
||||||
|
fakeSendStateTask.verifyExecuteRetry(
|
||||||
|
params = expectedParams,
|
||||||
|
remainingRetry = 3
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.internal.session.room.location
|
||||||
|
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
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.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeSendStateTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeStateEventDataSource
|
||||||
|
|
||||||
|
private const val A_USER_ID = "user-id"
|
||||||
|
private const val A_ROOM_ID = "room-id"
|
||||||
|
private const val AN_EVENT_ID = "event-id"
|
||||||
|
private const val A_TIMEOUT = 15_000L
|
||||||
|
private const val AN_EPOCH = 1655210176L
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class DefaultStopLiveLocationShareTaskTest {
|
||||||
|
|
||||||
|
private val fakeSendStateTask = FakeSendStateTask()
|
||||||
|
private val fakeStateEventDataSource = FakeStateEventDataSource()
|
||||||
|
|
||||||
|
private val defaultStopLiveLocationShareTask = DefaultStopLiveLocationShareTask(
|
||||||
|
userId = A_USER_ID,
|
||||||
|
sendStateTask = fakeSendStateTask,
|
||||||
|
stateEventDataSource = fakeStateEventDataSource.instance
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given parameters when calling the task then it is correctly executed`() = runTest {
|
||||||
|
val params = StopLiveLocationShareTask.Params(roomId = A_ROOM_ID)
|
||||||
|
val currentStateEvent = Event(
|
||||||
|
stateKey = A_USER_ID,
|
||||||
|
content = MessageBeaconInfoContent(
|
||||||
|
timeout = A_TIMEOUT,
|
||||||
|
isLive = true,
|
||||||
|
unstableTimestampMillis = AN_EPOCH
|
||||||
|
).toContent()
|
||||||
|
)
|
||||||
|
fakeStateEventDataSource.givenGetStateEventReturns(currentStateEvent)
|
||||||
|
fakeSendStateTask.givenExecuteRetryReturns(AN_EVENT_ID)
|
||||||
|
|
||||||
|
defaultStopLiveLocationShareTask.execute(params)
|
||||||
|
|
||||||
|
val expectedBeaconContent = MessageBeaconInfoContent(
|
||||||
|
timeout = A_TIMEOUT,
|
||||||
|
isLive = false,
|
||||||
|
unstableTimestampMillis = AN_EPOCH
|
||||||
|
).toContent()
|
||||||
|
val expectedParams = SendStateTask.Params(
|
||||||
|
roomId = params.roomId,
|
||||||
|
stateKey = A_USER_ID,
|
||||||
|
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
||||||
|
body = expectedBeaconContent
|
||||||
|
)
|
||||||
|
fakeSendStateTask.verifyExecuteRetry(
|
||||||
|
params = expectedParams,
|
||||||
|
remainingRetry = 3
|
||||||
|
)
|
||||||
|
fakeStateEventDataSource.verifyGetStateEvent(
|
||||||
|
roomId = params.roomId,
|
||||||
|
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
||||||
|
stateKey = A_USER_ID
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
|
||||||
|
internal class FakeEventSenderProcessor : EventSenderProcessor by mockk() {
|
||||||
|
|
||||||
|
fun givenPostEventReturns(event: Event, cancelable: Cancelable) {
|
||||||
|
every { postEvent(event) } returns cancelable
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
|
||||||
|
internal class FakeLocalEchoEventFactory {
|
||||||
|
|
||||||
|
val instance = mockk<LocalEchoEventFactory>()
|
||||||
|
|
||||||
|
fun givenCreateStaticLocationEvent(withLocalEcho: Boolean): Event {
|
||||||
|
val event = Event()
|
||||||
|
every {
|
||||||
|
instance.createStaticLocationEvent(
|
||||||
|
roomId = any(),
|
||||||
|
latitude = any(),
|
||||||
|
longitude = any(),
|
||||||
|
uncertainty = any(),
|
||||||
|
isUserLocation = any()
|
||||||
|
)
|
||||||
|
} returns event
|
||||||
|
|
||||||
|
if (withLocalEcho) {
|
||||||
|
every { instance.createLocalEcho(event) } just runs
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenCreateLiveLocationEvent(withLocalEcho: Boolean): Event {
|
||||||
|
val event = Event()
|
||||||
|
every {
|
||||||
|
instance.createLiveLocationEvent(
|
||||||
|
beaconInfoEventId = any(),
|
||||||
|
roomId = any(),
|
||||||
|
latitude = any(),
|
||||||
|
longitude = any(),
|
||||||
|
uncertainty = any()
|
||||||
|
)
|
||||||
|
} returns event
|
||||||
|
|
||||||
|
if (withLocalEcho) {
|
||||||
|
every { instance.createLocalEcho(event) } just runs
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyCreateStaticLocationEvent(
|
||||||
|
roomId: String,
|
||||||
|
latitude: Double,
|
||||||
|
longitude: Double,
|
||||||
|
uncertainty: Double?,
|
||||||
|
isUserLocation: Boolean
|
||||||
|
) {
|
||||||
|
verify {
|
||||||
|
instance.createStaticLocationEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
latitude = latitude,
|
||||||
|
longitude = longitude,
|
||||||
|
uncertainty = uncertainty,
|
||||||
|
isUserLocation = isUserLocation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyCreateLiveLocationEvent(
|
||||||
|
roomId: String,
|
||||||
|
beaconInfoEventId: String,
|
||||||
|
latitude: Double,
|
||||||
|
longitude: Double,
|
||||||
|
uncertainty: Double?
|
||||||
|
) {
|
||||||
|
verify {
|
||||||
|
instance.createLiveLocationEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
beaconInfoEventId = beaconInfoEventId,
|
||||||
|
latitude = latitude,
|
||||||
|
longitude = longitude,
|
||||||
|
uncertainty = uncertainty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyCreateLocalEcho(event: Event) {
|
||||||
|
verify { instance.createLocalEcho(event) }
|
||||||
|
}
|
||||||
|
}
|
@ -16,40 +16,62 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.test.fakes
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import io.mockk.MockKVerificationScope
|
import io.mockk.MockKVerificationScope
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.verify
|
import io.mockk.slot
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmModel
|
import io.realm.RealmModel
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
|
||||||
internal class FakeMonarchy {
|
internal class FakeMonarchy {
|
||||||
|
|
||||||
val instance = mockk<Monarchy>()
|
val instance = mockk<Monarchy>()
|
||||||
private val realm = mockk<Realm>(relaxed = true)
|
private val fakeRealm = FakeRealm()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt")
|
mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt")
|
||||||
coEvery {
|
coEvery {
|
||||||
instance.awaitTransaction(any<suspend (Realm) -> Any>())
|
instance.awaitTransaction(any<suspend (Realm) -> Any>())
|
||||||
} coAnswers {
|
} coAnswers {
|
||||||
secondArg<suspend (Realm) -> Any>().invoke(realm)
|
secondArg<suspend (Realm) -> Any>().invoke(fakeRealm.instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : RealmModel> givenWhereReturns(result: T?) {
|
inline fun <reified T : RealmModel> givenWhere(): RealmQuery<T> {
|
||||||
val queryResult = mockk<RealmQuery<T>>(relaxed = true)
|
return fakeRealm.givenWhere()
|
||||||
every { queryResult.findFirst() } returns result
|
}
|
||||||
every { realm.where<T>() } returns queryResult
|
|
||||||
|
inline fun <reified T : RealmModel> givenWhereReturns(result: T?): RealmQuery<T> {
|
||||||
|
return fakeRealm.givenWhere<T>()
|
||||||
|
.givenFindFirst(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : RealmModel> verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) {
|
inline fun <reified T : RealmModel> verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) {
|
||||||
verify { realm.insertOrUpdate(verification()) }
|
fakeRealm.verifyInsertOrUpdate(verification)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified R, reified T : RealmModel> givenFindAllMappedWithChangesReturns(
|
||||||
|
realmEntities: List<T>,
|
||||||
|
mappedResult: List<R>,
|
||||||
|
mapper: Monarchy.Mapper<R, T>
|
||||||
|
) {
|
||||||
|
every { mapper.map(any()) } returns mockk()
|
||||||
|
val monarchyQuery = slot<Monarchy.Query<T>>()
|
||||||
|
val monarchyMapper = slot<Monarchy.Mapper<R, T>>()
|
||||||
|
every {
|
||||||
|
instance.findAllMappedWithChanges(capture(monarchyQuery), capture(monarchyMapper))
|
||||||
|
} answers {
|
||||||
|
monarchyQuery.captured.createQuery(fakeRealm.instance)
|
||||||
|
realmEntities.forEach {
|
||||||
|
monarchyMapper.captured.map(it)
|
||||||
|
}
|
||||||
|
MutableLiveData(mappedResult)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.test.fakes
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.MockKVerificationScope
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmModel
|
import io.realm.RealmModel
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
@ -33,6 +35,10 @@ internal class FakeRealm {
|
|||||||
every { instance.where<T>() } returns query
|
every { instance.where<T>() } returns query
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : RealmModel> verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) {
|
||||||
|
verify { instance.insertOrUpdate(verification()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : RealmModel> RealmQuery<T>.givenFindFirst(
|
inline fun <reified T : RealmModel> RealmQuery<T>.givenFindFirst(
|
||||||
@ -77,3 +83,17 @@ inline fun <reified T : RealmModel> RealmQuery<T>.givenNotEqualTo(
|
|||||||
every { notEqualTo(fieldName, value) } returns this
|
every { notEqualTo(fieldName, value) } returns this
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : RealmModel> RealmQuery<T>.givenIsNotEmpty(
|
||||||
|
fieldName: String
|
||||||
|
): RealmQuery<T> {
|
||||||
|
every { isNotEmpty(fieldName) } returns this
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : RealmModel> RealmQuery<T>.givenIsNotNull(
|
||||||
|
fieldName: String
|
||||||
|
): RealmQuery<T> {
|
||||||
|
every { isNotNull(fieldName) } returns this
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||||
|
|
||||||
|
internal class FakeSendStateTask : SendStateTask by mockk() {
|
||||||
|
|
||||||
|
fun givenExecuteRetryReturns(eventId: String) {
|
||||||
|
coEvery { executeRetry(any(), any()) } returns eventId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyExecuteRetry(params: SendStateTask.Params, remainingRetry: Int) {
|
||||||
|
coVerify { executeRetry(params, remainingRetry) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.test.fakes
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
|
||||||
|
internal class FakeStateEventDataSource {
|
||||||
|
|
||||||
|
val instance: StateEventDataSource = mockk()
|
||||||
|
|
||||||
|
fun givenGetStateEventReturns(event: Event) {
|
||||||
|
every {
|
||||||
|
instance.getStateEvent(
|
||||||
|
roomId = any(),
|
||||||
|
eventType = any(),
|
||||||
|
stateKey = any()
|
||||||
|
)
|
||||||
|
} returns event
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyGetStateEvent(roomId: String, eventType: String, stateKey: String) {
|
||||||
|
verify {
|
||||||
|
instance.getStateEvent(
|
||||||
|
roomId = roomId,
|
||||||
|
eventType = eventType,
|
||||||
|
stateKey = QueryStringValue.Equals(stateKey)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,16 +23,13 @@ import android.os.Parcelable
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.services.VectorService
|
import im.vector.app.core.services.VectorService
|
||||||
import im.vector.app.core.time.Clock
|
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.session.Session
|
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.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
@ -51,7 +48,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
@Inject lateinit var notificationUtils: NotificationUtils
|
@Inject lateinit var notificationUtils: NotificationUtils
|
||||||
@Inject lateinit var locationTracker: LocationTracker
|
@Inject lateinit var locationTracker: LocationTracker
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var clock: Clock
|
|
||||||
|
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
|
|
||||||
@ -84,34 +80,19 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
scheduleTimer(roomArgs.roomId, roomArgs.durationMillis)
|
scheduleTimer(roomArgs.roomId, roomArgs.durationMillis)
|
||||||
|
|
||||||
// Send beacon info state event
|
// Send beacon info state event
|
||||||
activeSessionHolder
|
launchInIO { session ->
|
||||||
.getSafeActiveSession()
|
|
||||||
?.let { session ->
|
|
||||||
session.coroutineScope.launch(session.coroutineDispatchers.io) {
|
|
||||||
sendStartingLiveBeaconInfo(session, roomArgs)
|
sendStartingLiveBeaconInfo(session, roomArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
|
private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
|
||||||
val beaconContent = MessageBeaconInfoContent(
|
|
||||||
timeout = roomArgs.durationMillis,
|
|
||||||
isLive = true,
|
|
||||||
unstableTimestampMillis = clock.epochMillis()
|
|
||||||
).toContent()
|
|
||||||
|
|
||||||
val stateKey = session.myUserId
|
|
||||||
val beaconEventId = session
|
val beaconEventId = session
|
||||||
.getRoom(roomArgs.roomId)
|
.getRoom(roomArgs.roomId)
|
||||||
?.stateService()
|
?.locationSharingService()
|
||||||
?.sendStateEvent(
|
?.startLiveLocationShare(timeoutMillis = roomArgs.durationMillis)
|
||||||
eventType = EventType.STATE_ROOM_BEACON_INFO.first(),
|
|
||||||
stateKey = stateKey,
|
|
||||||
body = beaconContent
|
|
||||||
)
|
|
||||||
|
|
||||||
beaconEventId
|
beaconEventId
|
||||||
?.takeUnless { it.isEmpty() }
|
?.takeUnless { it.isEmpty() }
|
||||||
@ -159,12 +140,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun sendStoppedBeaconInfo(roomId: String) {
|
private fun sendStoppedBeaconInfo(roomId: String) {
|
||||||
activeSessionHolder
|
launchInIO { session ->
|
||||||
.getSafeActiveSession()
|
session.getRoom(roomId)
|
||||||
?.let { session ->
|
?.locationSharingService()
|
||||||
session.coroutineScope.launch(session.coroutineDispatchers.io) {
|
?.stopLiveLocationShare()
|
||||||
session.getRoom(roomId)?.stateService()?.stopLiveLocation(session.myUserId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,21 +161,17 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
beaconInfoEventId: String,
|
beaconInfoEventId: String,
|
||||||
locationData: LocationData
|
locationData: LocationData
|
||||||
) {
|
) {
|
||||||
val session = activeSessionHolder.getSafeActiveSession()
|
launchInIO { session ->
|
||||||
val room = session?.getRoom(roomId)
|
session.getRoom(roomId)
|
||||||
val userId = session?.myUserId
|
?.locationSharingService()
|
||||||
|
?.sendLiveLocation(
|
||||||
if (room == null || userId == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
room.sendService().sendLiveLocation(
|
|
||||||
beaconInfoEventId = beaconInfoEventId,
|
beaconInfoEventId = beaconInfoEventId,
|
||||||
latitude = locationData.latitude,
|
latitude = locationData.latitude,
|
||||||
longitude = locationData.longitude,
|
longitude = locationData.longitude,
|
||||||
uncertainty = locationData.uncertainty
|
uncertainty = locationData.uncertainty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNoLocationProviderAvailable() {
|
override fun onNoLocationProviderAvailable() {
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
@ -216,6 +191,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
destroyMe()
|
destroyMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) =
|
||||||
|
activeSessionHolder
|
||||||
|
.getSafeActiveSession()
|
||||||
|
?.let { session ->
|
||||||
|
session.coroutineScope.launch(
|
||||||
|
context = session.coroutineDispatchers.io,
|
||||||
|
block = { block(session) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder {
|
override fun onBind(intent: Intent?): IBinder {
|
||||||
return binder
|
return binder
|
||||||
}
|
}
|
||||||
|
@ -136,13 +136,15 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun shareLocation(locationData: LocationData?, isUserLocation: Boolean) {
|
private fun shareLocation(locationData: LocationData?, isUserLocation: Boolean) {
|
||||||
locationData?.let { location ->
|
locationData?.let { location ->
|
||||||
room.sendService().sendLocation(
|
viewModelScope.launch {
|
||||||
|
room.locationSharingService().sendStaticLocation(
|
||||||
latitude = location.latitude,
|
latitude = location.latitude,
|
||||||
longitude = location.longitude,
|
longitude = location.longitude,
|
||||||
uncertainty = location.uncertainty,
|
uncertainty = location.uncertainty,
|
||||||
isUserLocation = isUserLocation
|
isUserLocation = isUserLocation
|
||||||
)
|
)
|
||||||
_viewEvents.post(LocationSharingViewEvents.Close)
|
_viewEvents.post(LocationSharingViewEvents.Close)
|
||||||
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
_viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError)
|
_viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user