Adding use case to stop live location share
WIP
This commit is contained in:
parent
cd72563d8b
commit
8406b2a4eb
@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
|||||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||||
import im.vector.app.features.home.room.typing.TypingHelper
|
import im.vector.app.features.home.room.typing.TypingHelper
|
||||||
import im.vector.app.features.location.LocationSharingServiceConnection
|
import im.vector.app.features.location.LocationSharingServiceConnection
|
||||||
|
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
||||||
import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault
|
import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault
|
||||||
@ -92,6 +93,7 @@ import org.matrix.android.sdk.api.session.file.FileService
|
|||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
@ -133,8 +135,9 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
private val decryptionFailureTracker: DecryptionFailureTracker,
|
private val decryptionFailureTracker: DecryptionFailureTracker,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
||||||
|
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
||||||
timelineFactory: TimelineFactory,
|
timelineFactory: TimelineFactory,
|
||||||
appStateHandler: AppStateHandler
|
appStateHandler: AppStateHandler,
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {
|
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {
|
||||||
|
|
||||||
@ -1139,7 +1142,12 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleStopLiveLocationSharing() {
|
private fun handleStopLiveLocationSharing() {
|
||||||
locationSharingServiceConnection.stopLiveLocationSharing(room.roomId)
|
viewModelScope.launch {
|
||||||
|
val result = stopLiveLocationShareUseCase.execute(room.roomId)
|
||||||
|
if (result is UpdateLiveLocationShareResult.Failure) {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.Failure(throwable = result.error, showInDialog = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
@ -1310,7 +1318,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
// we should also mark it as read here, for the scenario that the user
|
// we should also mark it as read here, for the scenario that the user
|
||||||
// is already in the thread timeline
|
// is already in the thread timeline
|
||||||
markThreadTimelineAsReadLocal()
|
markThreadTimelineAsReadLocal()
|
||||||
locationSharingServiceConnection.unbind()
|
locationSharingServiceConnection.unbind(this)
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,31 +132,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
|
|
||||||
fun stopSharingLocation(roomId: String) {
|
fun stopSharingLocation(roomId: String) {
|
||||||
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
||||||
|
synchronized(roomArgsMap) {
|
||||||
|
val beaconIds = roomArgsMap
|
||||||
|
.filter { it.value.roomId == roomId }
|
||||||
|
.map { it.key }
|
||||||
|
beaconIds.forEach { roomArgsMap.remove(it) }
|
||||||
|
|
||||||
launchInIO { session ->
|
tryToDestroyMe()
|
||||||
when (val result = sendStoppedBeaconInfo(session, roomId)) {
|
|
||||||
is UpdateLiveLocationShareResult.Success -> {
|
|
||||||
synchronized(roomArgsMap) {
|
|
||||||
val beaconIds = roomArgsMap
|
|
||||||
.filter { it.value.roomId == roomId }
|
|
||||||
.map { it.key }
|
|
||||||
beaconIds.forEach { roomArgsMap.remove(it) }
|
|
||||||
|
|
||||||
tryToDestroyMe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is UpdateLiveLocationShareResult.Failure -> callback?.onServiceError(result.error)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? {
|
|
||||||
return session.getRoom(roomId)
|
|
||||||
?.locationSharingService()
|
|
||||||
?.stopLiveLocationShare()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLocationUpdate(locationData: LocationData) {
|
override fun onLocationUpdate(locationData: LocationData) {
|
||||||
Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
|
Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ import android.content.Intent
|
|||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
class LocationSharingServiceConnection @Inject constructor(
|
class LocationSharingServiceConnection @Inject constructor(
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) : ServiceConnection, LocationSharingService.Callback {
|
) : ServiceConnection, LocationSharingService.Callback {
|
||||||
@ -33,12 +35,12 @@ class LocationSharingServiceConnection @Inject constructor(
|
|||||||
fun onLocationServiceError(error: Throwable)
|
fun onLocationServiceError(error: Throwable)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var callback: Callback? = null
|
private val callbacks = mutableSetOf<Callback>()
|
||||||
private var isBound = false
|
private var isBound = false
|
||||||
private var locationSharingService: LocationSharingService? = null
|
private var locationSharingService: LocationSharingService? = null
|
||||||
|
|
||||||
fun bind(callback: Callback) {
|
fun bind(callback: Callback) {
|
||||||
this.callback = callback
|
addCallback(callback)
|
||||||
|
|
||||||
if (isBound) {
|
if (isBound) {
|
||||||
callback.onLocationServiceRunning()
|
callback.onLocationServiceRunning()
|
||||||
@ -49,8 +51,8 @@ class LocationSharingServiceConnection @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind() {
|
fun unbind(callback: Callback) {
|
||||||
callback = null
|
removeCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopLiveLocationSharing(roomId: String) {
|
fun stopLiveLocationSharing(roomId: String) {
|
||||||
@ -62,17 +64,37 @@ class LocationSharingServiceConnection @Inject constructor(
|
|||||||
it.callback = this
|
it.callback = this
|
||||||
}
|
}
|
||||||
isBound = true
|
isBound = true
|
||||||
callback?.onLocationServiceRunning()
|
onCallbackActionNoArg(Callback::onLocationServiceRunning)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(className: ComponentName) {
|
override fun onServiceDisconnected(className: ComponentName) {
|
||||||
isBound = false
|
isBound = false
|
||||||
locationSharingService?.callback = null
|
locationSharingService?.callback = null
|
||||||
locationSharingService = null
|
locationSharingService = null
|
||||||
callback?.onLocationServiceStopped()
|
onCallbackActionNoArg(Callback::onLocationServiceStopped)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceError(error: Throwable) {
|
override fun onServiceError(error: Throwable) {
|
||||||
callback?.onLocationServiceError(error)
|
forwardErrorToCallbacks(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun addCallback(callback: Callback) {
|
||||||
|
callbacks.add(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun removeCallback(callback: Callback) {
|
||||||
|
callbacks.remove(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun onCallbackActionNoArg(action: Callback.() -> Unit) {
|
||||||
|
callbacks.forEach(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun forwardErrorToCallbacks(error: Throwable) {
|
||||||
|
callbacks.forEach { it.onLocationServiceError(error) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.location.live
|
||||||
|
|
||||||
|
import im.vector.app.features.location.LocationSharingServiceConnection
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class StopLiveLocationShareUseCase @Inject constructor(
|
||||||
|
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
||||||
|
private val session: Session
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun execute(roomId: String): UpdateLiveLocationShareResult? {
|
||||||
|
val result = sendStoppedBeaconInfo(session, roomId)
|
||||||
|
when (result) {
|
||||||
|
is UpdateLiveLocationShareResult.Success -> locationSharingServiceConnection.stopLiveLocationSharing(roomId)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? {
|
||||||
|
return session.getRoom(roomId)
|
||||||
|
?.locationSharingService()
|
||||||
|
?.stopLiveLocationShare()
|
||||||
|
}
|
||||||
|
}
|
@ -24,13 +24,17 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.location.LocationSharingServiceConnection
|
import im.vector.app.features.location.LocationSharingServiceConnection
|
||||||
|
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
|
|
||||||
class LocationLiveMapViewModel @AssistedInject constructor(
|
class LocationLiveMapViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: LocationLiveMapViewState,
|
@Assisted private val initialState: LocationLiveMapViewState,
|
||||||
getListOfUserLiveLocationUseCase: GetListOfUserLiveLocationUseCase,
|
getListOfUserLiveLocationUseCase: GetListOfUserLiveLocationUseCase,
|
||||||
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
||||||
|
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
||||||
) : VectorViewModel<LocationLiveMapViewState, LocationLiveMapAction, LocationLiveMapViewEvents>(initialState), LocationSharingServiceConnection.Callback {
|
) : VectorViewModel<LocationLiveMapViewState, LocationLiveMapAction, LocationLiveMapViewEvents>(initialState), LocationSharingServiceConnection.Callback {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -47,6 +51,11 @@ class LocationLiveMapViewModel @AssistedInject constructor(
|
|||||||
locationSharingServiceConnection.bind(this)
|
locationSharingServiceConnection.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
locationSharingServiceConnection.unbind(this)
|
||||||
|
super.onCleared()
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: LocationLiveMapAction) {
|
override fun handle(action: LocationLiveMapAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is LocationLiveMapAction.AddMapSymbol -> handleAddMapSymbol(action)
|
is LocationLiveMapAction.AddMapSymbol -> handleAddMapSymbol(action)
|
||||||
@ -70,7 +79,12 @@ class LocationLiveMapViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleStopSharing() {
|
private fun handleStopSharing() {
|
||||||
locationSharingServiceConnection.stopLiveLocationSharing(initialState.roomId)
|
viewModelScope.launch {
|
||||||
|
val result = stopLiveLocationShareUseCase.execute(initialState.roomId)
|
||||||
|
if (result is UpdateLiveLocationShareResult.Failure) {
|
||||||
|
_viewEvents.post(LocationLiveMapViewEvents.Error(result.error))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLocationServiceRunning() {
|
override fun onLocationServiceRunning() {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.location.live
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeLocationSharingServiceConnection
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
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.location.UpdateLiveLocationShareResult
|
||||||
|
|
||||||
|
private const val A_ROOM_ID = "room_id"
|
||||||
|
private const val AN_EVENT_ID = "event_id"
|
||||||
|
|
||||||
|
class StopLiveLocationShareUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeLocationSharingServiceConnection = FakeLocationSharingServiceConnection()
|
||||||
|
private val fakeSession = FakeSession()
|
||||||
|
|
||||||
|
private val stopLiveLocationShareUseCase = StopLiveLocationShareUseCase(
|
||||||
|
locationSharingServiceConnection = fakeLocationSharingServiceConnection.instance,
|
||||||
|
session = fakeSession
|
||||||
|
)
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a room id when calling use case then the current live is stopped with success`() = runTest {
|
||||||
|
fakeLocationSharingServiceConnection.givenStopLiveLocationSharing()
|
||||||
|
|
||||||
|
val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID)
|
||||||
|
|
||||||
|
result shouldBeEqualTo UpdateLiveLocationShareResult.Success(AN_EVENT_ID)
|
||||||
|
fakeLocationSharingServiceConnection.verifyStopLiveLocationSharing(A_ROOM_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a room id and error during the process when calling use case then result is failure`() = runTest {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ 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.unmockkAll
|
||||||
import io.mockk.unmockkStatic
|
import io.mockk.unmockkStatic
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
@ -55,7 +56,7 @@ class GetListOfUserLiveLocationUseCaseTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
unmockkStatic("androidx.lifecycle.FlowLiveDataConversions")
|
unmockkAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.location.LocationSharingServiceConnection
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.verify
|
||||||
|
|
||||||
|
class FakeLocationSharingServiceConnection {
|
||||||
|
|
||||||
|
val instance = mockk<LocationSharingServiceConnection>()
|
||||||
|
|
||||||
|
fun givenStopLiveLocationSharing() {
|
||||||
|
every { instance.stopLiveLocationSharing(any()) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyStopLiveLocationSharing(roomId: String) {
|
||||||
|
verify { instance.stopLiveLocationSharing(roomId) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user