Observing live status in DB from location sharing Android service
This commit is contained in:
parent
3cffedd353
commit
81e14c7c3b
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.location
|
package org.matrix.android.sdk.api.session.room.location
|
||||||
|
|
||||||
|
import androidx.annotation.MainThread
|
||||||
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
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
@ -60,11 +61,13 @@ interface LocationSharingService {
|
|||||||
/**
|
/**
|
||||||
* Returns a LiveData on the list of current running live location shares.
|
* Returns a LiveData on the list of current running live location shares.
|
||||||
*/
|
*/
|
||||||
|
@MainThread
|
||||||
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a LiveData on the live location share summary with the given eventId.
|
* Returns a LiveData on the live location share summary with the given eventId.
|
||||||
* @param beaconInfoEventId event id of the initial beacon info state event
|
* @param beaconInfoEventId event id of the initial beacon info state event
|
||||||
*/
|
*/
|
||||||
|
@MainThread
|
||||||
fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>>
|
fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>>
|
||||||
}
|
}
|
||||||
|
@ -18,22 +18,27 @@ package im.vector.app.features.location
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Parcelable
|
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.features.location.live.GetLiveLocationShareSummaryUseCase
|
||||||
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.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
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.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Timer
|
|
||||||
import java.util.TimerTask
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -49,6 +54,7 @@ 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 getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
||||||
|
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
|
|
||||||
@ -56,8 +62,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
* Keep track of a map between beacon event Id starting the live and RoomArgs.
|
* Keep track of a map between beacon event Id starting the live and RoomArgs.
|
||||||
*/
|
*/
|
||||||
private val roomArgsMap = mutableMapOf<String, RoomArgs>()
|
private val roomArgsMap = mutableMapOf<String, RoomArgs>()
|
||||||
private val timers = mutableListOf<Timer>()
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
private val jobs = mutableListOf<Job>()
|
||||||
|
private val mainHandler by lazy { Handler(mainLooper) }
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@ -78,9 +85,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
val notification = notificationUtils.buildLiveLocationSharingNotification()
|
val notification = notificationUtils.buildLiveLocationSharingNotification()
|
||||||
startForeground(roomArgs.roomId.hashCode(), notification)
|
startForeground(roomArgs.roomId.hashCode(), notification)
|
||||||
|
|
||||||
// Schedule a timer to stop sharing
|
|
||||||
scheduleTimer(roomArgs.roomId, roomArgs.durationMillis)
|
|
||||||
|
|
||||||
// Send beacon info state event
|
// Send beacon info state event
|
||||||
launchInIO { session ->
|
launchInIO { session ->
|
||||||
sendStartingLiveBeaconInfo(session, roomArgs)
|
sendStartingLiveBeaconInfo(session, roomArgs)
|
||||||
@ -101,6 +105,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
when (result) {
|
when (result) {
|
||||||
is UpdateLiveLocationShareResult.Success -> {
|
is UpdateLiveLocationShareResult.Success -> {
|
||||||
addRoomArgs(result.beaconEventId, roomArgs)
|
addRoomArgs(result.beaconEventId, roomArgs)
|
||||||
|
listenForLiveSummaryChanges(roomArgs.roomId, result.beaconEventId)
|
||||||
locationTracker.requestLastKnownLocation()
|
locationTracker.requestLastKnownLocation()
|
||||||
}
|
}
|
||||||
is UpdateLiveLocationShareResult.Failure -> {
|
is UpdateLiveLocationShareResult.Failure -> {
|
||||||
@ -115,22 +120,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleTimer(roomId: String, durationMillis: Long) {
|
private fun stopSharingLocation(roomId: String) {
|
||||||
Timer()
|
|
||||||
.apply {
|
|
||||||
schedule(object : TimerTask() {
|
|
||||||
override fun run() {
|
|
||||||
stopSharingLocation(roomId)
|
|
||||||
timers.remove(this@apply)
|
|
||||||
}
|
|
||||||
}, durationMillis)
|
|
||||||
}
|
|
||||||
.also {
|
|
||||||
timers.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stopSharingLocation(roomId: String) {
|
|
||||||
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
Timber.i("### LocationSharingService.stopSharingLocation for $roomId")
|
||||||
removeRoomArgs(roomId)
|
removeRoomArgs(roomId)
|
||||||
tryToDestroyMe()
|
tryToDestroyMe()
|
||||||
@ -177,9 +167,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun destroyMe() {
|
private fun destroyMe() {
|
||||||
|
jobs.forEach { it.cancel() }
|
||||||
|
jobs.clear()
|
||||||
locationTracker.removeCallback(this)
|
locationTracker.removeCallback(this)
|
||||||
timers.forEach { it.cancel() }
|
|
||||||
timers.clear()
|
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +192,21 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||||||
beaconIds.forEach { roomArgsMap.remove(it) }
|
beaconIds.forEach { roomArgsMap.remove(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun listenForLiveSummaryChanges(roomId: String, eventId: String) {
|
||||||
|
activeSessionHolder
|
||||||
|
.getSafeActiveSession()
|
||||||
|
?.let { session ->
|
||||||
|
mainHandler.post {
|
||||||
|
val job = getLiveLocationShareSummaryUseCase.execute(roomId, eventId)
|
||||||
|
.distinctUntilChangedBy { it.isActive }
|
||||||
|
.filter { it.isActive == false }
|
||||||
|
.onEach { stopSharingLocation(roomId) }
|
||||||
|
.launchIn(session.coroutineScope)
|
||||||
|
jobs.add(job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) =
|
private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) =
|
||||||
activeSessionHolder
|
activeSessionHolder
|
||||||
.getSafeActiveSession()
|
.getSafeActiveSession()
|
||||||
|
@ -55,10 +55,6 @@ class LocationSharingServiceConnection @Inject constructor(
|
|||||||
removeCallback(callback)
|
removeCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopLiveLocationSharing(roomId: String) {
|
|
||||||
locationSharingService?.stopSharingLocation(roomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
||||||
locationSharingService = (binder as LocationSharingService.LocalBinder).getService().also {
|
locationSharingService = (binder as LocationSharingService.LocalBinder).getService().also {
|
||||||
it.callback = this
|
it.callback = this
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.live
|
package im.vector.app.features.location.live
|
||||||
|
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
@ -23,13 +24,16 @@ import kotlinx.coroutines.flow.mapNotNull
|
|||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
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.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetLiveLocationShareSummaryUseCase @Inject constructor(
|
class GetLiveLocationShareSummaryUseCase @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@MainThread
|
||||||
fun execute(roomId: String, eventId: String): Flow<LiveLocationShareAggregatedSummary> {
|
fun execute(roomId: String, eventId: String): Flow<LiveLocationShareAggregatedSummary> {
|
||||||
|
Timber.d("getting flow for roomId=$roomId and eventId=$eventId")
|
||||||
return session.getRoom(roomId)
|
return session.getRoom(roomId)
|
||||||
?.locationSharingService()
|
?.locationSharingService()
|
||||||
?.getLiveLocationShareSummary(eventId)
|
?.getLiveLocationShareSummary(eventId)
|
||||||
|
@ -16,24 +16,17 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.live
|
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.Session
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class StopLiveLocationShareUseCase @Inject constructor(
|
class StopLiveLocationShareUseCase @Inject constructor(
|
||||||
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
|
||||||
private val session: Session
|
private val session: Session
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun execute(roomId: String): UpdateLiveLocationShareResult? {
|
suspend fun execute(roomId: String): UpdateLiveLocationShareResult? {
|
||||||
val result = sendStoppedBeaconInfo(session, roomId)
|
return sendStoppedBeaconInfo(session, roomId)
|
||||||
when (result) {
|
|
||||||
is UpdateLiveLocationShareResult.Success -> locationSharingServiceConnection.stopLiveLocationSharing(roomId)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? {
|
private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.app.features.location.live
|
package im.vector.app.features.location.live
|
||||||
|
|
||||||
import im.vector.app.test.fakes.FakeLocationSharingServiceConnection
|
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
import io.mockk.unmockkAll
|
import io.mockk.unmockkAll
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
@ -30,11 +29,9 @@ private const val AN_EVENT_ID = "event_id"
|
|||||||
|
|
||||||
class StopLiveLocationShareUseCaseTest {
|
class StopLiveLocationShareUseCaseTest {
|
||||||
|
|
||||||
private val fakeLocationSharingServiceConnection = FakeLocationSharingServiceConnection()
|
|
||||||
private val fakeSession = FakeSession()
|
private val fakeSession = FakeSession()
|
||||||
|
|
||||||
private val stopLiveLocationShareUseCase = StopLiveLocationShareUseCase(
|
private val stopLiveLocationShareUseCase = StopLiveLocationShareUseCase(
|
||||||
locationSharingServiceConnection = fakeLocationSharingServiceConnection.instance,
|
|
||||||
session = fakeSession
|
session = fakeSession
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +42,6 @@ class StopLiveLocationShareUseCaseTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a room id when calling use case then the current live is stopped with success`() = runTest {
|
fun `given a room id when calling use case then the current live is stopped with success`() = runTest {
|
||||||
fakeLocationSharingServiceConnection.givenStopLiveLocationSharing()
|
|
||||||
val updateLiveResult = UpdateLiveLocationShareResult.Success(AN_EVENT_ID)
|
val updateLiveResult = UpdateLiveLocationShareResult.Success(AN_EVENT_ID)
|
||||||
fakeSession.roomService()
|
fakeSession.roomService()
|
||||||
.getRoom(A_ROOM_ID)
|
.getRoom(A_ROOM_ID)
|
||||||
@ -55,7 +51,6 @@ class StopLiveLocationShareUseCaseTest {
|
|||||||
val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID)
|
val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID)
|
||||||
|
|
||||||
result shouldBeEqualTo updateLiveResult
|
result shouldBeEqualTo updateLiveResult
|
||||||
fakeLocationSharingServiceConnection.verifyStopLiveLocationSharing(A_ROOM_ID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -70,6 +65,5 @@ class StopLiveLocationShareUseCaseTest {
|
|||||||
val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID)
|
val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID)
|
||||||
|
|
||||||
result shouldBeEqualTo updateLiveResult
|
result shouldBeEqualTo updateLiveResult
|
||||||
fakeLocationSharingServiceConnection.verifyStopLiveLocationSharingNotCalled(A_ROOM_ID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,16 +34,4 @@ class FakeLocationSharingServiceConnection {
|
|||||||
fun verifyBind(callback: LocationSharingServiceConnection.Callback) {
|
fun verifyBind(callback: LocationSharingServiceConnection.Callback) {
|
||||||
verify { instance.bind(callback) }
|
verify { instance.bind(callback) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenStopLiveLocationSharing() {
|
|
||||||
every { instance.stopLiveLocationSharing(any()) } just runs
|
|
||||||
}
|
|
||||||
|
|
||||||
fun verifyStopLiveLocationSharing(roomId: String) {
|
|
||||||
verify { instance.stopLiveLocationSharing(roomId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun verifyStopLiveLocationSharingNotCalled(roomId: String) {
|
|
||||||
verify(inverse = true) { instance.stopLiveLocationSharing(roomId) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user