From a1d27940cdceae5b9a0806b8d7c1db262d6201c4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 18 Mar 2022 15:26:11 +0300 Subject: [PATCH 01/13] Create a foreground service. --- vector/src/main/AndroidManifest.xml | 8 +++++- .../location/LocationSharingService.kt | 27 +++++++++++++++++++ .../app/features/location/LocationTracker.kt | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 1d99fba91a..71ea6aaf8f 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -46,6 +46,7 @@ + @@ -84,8 +85,8 @@ android:resizeableActivity="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Vector.Light" android:taskAffinity="${applicationId}.${appTaskAffinitySuffix}" + android:theme="@style/Theme.Vector.Light" tools:replace="android:allowBackup"> @@ -369,6 +370,11 @@ + + Date: Mon, 21 Mar 2022 14:27:15 +0300 Subject: [PATCH 02/13] Start the foreground service when users start live location sharing. --- .../location/LocationSharingFragment.kt | 20 ++++++++-- .../location/LocationSharingService.kt | 37 ++++++++++++++++++- .../location/LocationSharingViewEvents.kt | 1 + .../location/LocationSharingViewModel.kt | 6 ++- .../notifications/NotificationUtils.kt | 12 ++++++ vector/src/main/res/values/strings.xml | 2 + 6 files changed, 71 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index c4dccc1b73..15f91d1f47 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -16,11 +16,13 @@ package im.vector.app.features.location +import android.content.Intent import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.fragmentViewModel @@ -83,9 +85,10 @@ class LocationSharingFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - LocationSharingViewEvents.Close -> locationSharingNavigator.quit() - LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() - is LocationSharingViewEvents.ZoomToUserLocation -> handleZoomToUserLocationEvent(it) + LocationSharingViewEvents.Close -> locationSharingNavigator.quit() + LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() + is LocationSharingViewEvents.ZoomToUserLocation -> handleZoomToUserLocationEvent(it) + is LocationSharingViewEvents.StartLiveLocationService -> handleStartLiveLocationService(it) }.exhaustive } } @@ -177,6 +180,17 @@ class LocationSharingFragment @Inject constructor( views.mapView.zoomToLocation(event.userLocation.latitude, event.userLocation.longitude) } + private fun handleStartLiveLocationService(event: LocationSharingViewEvents.StartLiveLocationService) { + Intent(requireContext(), LocationSharingService::class.java) + .apply { + putExtra(LocationSharingService.EXTRA_SESSION_ID, event.sessionId) + putExtra(LocationSharingService.EXTRA_ROOM_ID, event.roomId) + } + .also { + ContextCompat.startForegroundService(requireContext(), it) + } + } + private fun initOptionsPicker() { // set no option at start views.shareLocationOptionsPicker.render() diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 4c565ac2bb..e0faebe528 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -16,12 +16,45 @@ package im.vector.app.features.location -import android.app.Service import android.content.Intent import android.os.IBinder +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.services.VectorService +import im.vector.app.features.notifications.NotificationUtils +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class LocationSharingService : VectorService() { + + @Inject lateinit var notificationUtils: NotificationUtils + + private var sessionId: String? = null + private var roomId: String? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + sessionId = intent?.getStringExtra(EXTRA_SESSION_ID) + roomId = intent?.getStringExtra(EXTRA_ROOM_ID) + + Timber.d("LocationSharingService $sessionId - $roomId") + + if (sessionId == null || roomId == null) { + stopForeground(true) + stopSelf() + } + + val notification = notificationUtils.buildLiveLocationSharingNotification() + startForeground(roomId!!.hashCode(), notification) + + return START_STICKY + } -class LocationSharingService : Service() { override fun onBind(intent: Intent?): IBinder? { return null } + + companion object { + const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID" + const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" + } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt index 8d31db1119..a7204f02c1 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt @@ -22,4 +22,5 @@ sealed class LocationSharingViewEvents : VectorViewEvents { object Close : LocationSharingViewEvents() object LocationNotAvailableError : LocationSharingViewEvents() data class ZoomToUserLocation(val userLocation: LocationData) : LocationSharingViewEvents() + data class StartLiveLocationService(val sessionId: String, val roomId: String) : LocationSharingViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 639666e63f..893eee6f70 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -160,8 +160,10 @@ class LocationSharingViewModel @AssistedInject constructor( } private fun handleStartLiveLocationSharingAction() { - // TODO start sharing live location and update view state - Timber.d("live location sharing started") + _viewEvents.post(LocationSharingViewEvents.StartLiveLocationService( + sessionId = session.sessionId, + roomId = room.roomId + )) } override fun onLocationUpdate(locationData: LocationData) { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index d39926f620..0366b160ee 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -521,6 +521,18 @@ class NotificationUtils @Inject constructor(private val context: Context, return builder.build() } + /** + * Creates a notification that indicates the application is retrieving location even if it is in background or killed. + */ + fun buildLiveLocationSharingNotification(): Notification { + return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID) + .setContentTitle(stringProvider.getString(R.string.live_location_sharing_notification_title)) + .setContentText(stringProvider.getString(R.string.live_location_sharing_notification_description)) + .setSmallIcon(R.drawable.ic_location_pin) + .setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING) + .build() + } + fun buildDownloadFileNotification(uri: Uri, fileName: String, mimeType: String): Notification { return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID) .setGroup(stringProvider.getString(R.string.app_name)) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 428be3209f..9a30e89948 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2946,6 +2946,8 @@ Once enabled you will be able to send your location to any room Render user locations in the timeline Failed to load map + ${app_name} Live Location + Location sharing is in progress Show Message bubbles From 334368083e2fc2189c22ce9476f79f00f27b9e88 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 21 Mar 2022 16:46:40 +0300 Subject: [PATCH 03/13] Track location in foreground service. --- .../location/LocationSharingService.kt | 24 ++++++++++++-- .../location/LocationSharingViewModel.kt | 5 +-- .../app/features/location/LocationTracker.kt | 32 ++++++++++++++----- .../notifications/NotificationUtils.kt | 2 +- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index e0faebe528..1253891421 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -25,9 +25,10 @@ import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint -class LocationSharingService : VectorService() { +class LocationSharingService : VectorService(), LocationTracker.Callback { @Inject lateinit var notificationUtils: NotificationUtils + @Inject lateinit var locationTracker: LocationTracker private var sessionId: String? = null private var roomId: String? = null @@ -36,19 +37,27 @@ class LocationSharingService : VectorService() { sessionId = intent?.getStringExtra(EXTRA_SESSION_ID) roomId = intent?.getStringExtra(EXTRA_ROOM_ID) - Timber.d("LocationSharingService $sessionId - $roomId") - if (sessionId == null || roomId == null) { stopForeground(true) stopSelf() } + // Show a sticky notification val notification = notificationUtils.buildLiveLocationSharingNotification() startForeground(roomId!!.hashCode(), notification) + // Start tracking location + locationTracker.addCallback(this) + locationTracker.start() + return START_STICKY } + override fun onDestroy() { + super.onDestroy() + locationTracker.removeCallback(this) + } + override fun onBind(intent: Intent?): IBinder? { return null } @@ -57,4 +66,13 @@ class LocationSharingService : VectorService() { const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID" const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" } + + override fun onLocationUpdate(locationData: LocationData) { + Timber.d("### LocationSharingService.onLocationUpdate: ${locationData.latitude} - ${locationData.longitude}") + } + + override fun onLocationProviderIsNotAvailable() { + stopForeground(true) + stopSelf() + } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 893eee6f70..b2bf6d1762 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -65,7 +65,8 @@ class LocationSharingViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { - locationTracker.start(this) + locationTracker.addCallback(this) + locationTracker.start() setUserItem() updatePin() compareTargetAndUserLocation() @@ -112,7 +113,7 @@ class LocationSharingViewModel @AssistedInject constructor( override fun onCleared() { super.onCleared() - locationTracker.stop() + locationTracker.removeCallback(this) } override fun handle(action: LocationSharingAction) { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 0ee6395871..d5e298a14b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -40,18 +40,17 @@ class LocationTracker @Inject constructor( fun onLocationProviderIsNotAvailable() } - private var callback: Callback? = null + private var callbacks = mutableListOf() private var hasGpsProviderLiveLocation = false @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) - fun start(callback: Callback?) { + fun start() { Timber.d("## LocationTracker. start()") hasGpsProviderLiveLocation = false - this.callback = callback if (locationManager == null) { - callback?.onLocationProviderIsNotAvailable() + callbacks.forEach { it.onLocationProviderIsNotAvailable() } Timber.v("## LocationTracker. LocationManager is not available") return } @@ -81,7 +80,7 @@ class LocationTracker @Inject constructor( ) } ?: run { - callback?.onLocationProviderIsNotAvailable() + callbacks.forEach { it.onLocationProviderIsNotAvailable() } Timber.v("## LocationTracker. There is no location provider available") } } @@ -90,7 +89,24 @@ class LocationTracker @Inject constructor( fun stop() { Timber.d("## LocationTracker. stop()") locationManager?.removeUpdates(this) - callback = null + callbacks.clear() + } + + fun addCallback(callback: Callback) { + synchronized(callbacks) { + if (!callbacks.contains(callback)) { + callbacks.add(callback) + } + } + } + + fun removeCallback(callback: Callback) { + synchronized(callbacks) { + callbacks.remove(callback) + if (callbacks.size == 0) { + stop() + } + } } override fun onLocationChanged(location: Location) { @@ -115,12 +131,12 @@ class LocationTracker @Inject constructor( } } } - callback?.onLocationUpdate(location.toLocationData()) + callbacks.forEach { it.onLocationUpdate(location.toLocationData()) } } override fun onProviderDisabled(provider: String) { Timber.d("## LocationTracker. onProviderDisabled: $provider") - callback?.onLocationProviderIsNotAvailable() + callbacks.forEach { it.onLocationProviderIsNotAvailable() } } private fun Location.toLocationData(): LocationData { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index 0366b160ee..9fa094075a 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -528,7 +528,7 @@ class NotificationUtils @Inject constructor(private val context: Context, return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID) .setContentTitle(stringProvider.getString(R.string.live_location_sharing_notification_title)) .setContentText(stringProvider.getString(R.string.live_location_sharing_notification_description)) - .setSmallIcon(R.drawable.ic_location_pin) + .setSmallIcon(R.drawable.ic_attachment_location_live_white) .setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING) .build() } From 7e5c293ebc547585c3df07bb1612e1b32a867a1f Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 21 Mar 2022 17:27:14 +0300 Subject: [PATCH 04/13] Use primary color for location notification item. --- .../im/vector/app/features/notifications/NotificationUtils.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index 9fa094075a..8120134526 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -529,6 +529,7 @@ class NotificationUtils @Inject constructor(private val context: Context, .setContentTitle(stringProvider.getString(R.string.live_location_sharing_notification_title)) .setContentText(stringProvider.getString(R.string.live_location_sharing_notification_description)) .setSmallIcon(R.drawable.ic_attachment_location_live_white) + .setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary)) .setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING) .build() } From 70c8a8bf347c630097abb199d44a80ad280e12ca Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 22 Mar 2022 16:01:51 +0300 Subject: [PATCH 05/13] Support sharing live location in multiple rooms. --- .../location/LocationSharingAction.kt | 2 +- .../location/LocationSharingFragment.kt | 8 +- .../location/LocationSharingService.kt | 78 ++++++++++++++----- .../location/LocationSharingViewEvents.kt | 2 +- .../location/LocationSharingViewModel.kt | 15 ++-- 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt index d7d686ee60..4025fbefa8 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -23,5 +23,5 @@ sealed class LocationSharingAction : VectorViewModelAction { data class PinnedLocationSharing(val locationData: LocationData?) : LocationSharingAction() data class LocationTargetChange(val locationData: LocationData) : LocationSharingAction() object ZoomToUserLocation : LocationSharingAction() - object StartLiveLocationSharing : LocationSharingAction() + data class StartLiveLocationSharing(val duration: Long) : LocationSharingAction() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 15f91d1f47..84efff0e0d 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -183,8 +183,8 @@ class LocationSharingFragment @Inject constructor( private fun handleStartLiveLocationService(event: LocationSharingViewEvents.StartLiveLocationService) { Intent(requireContext(), LocationSharingService::class.java) .apply { - putExtra(LocationSharingService.EXTRA_SESSION_ID, event.sessionId) - putExtra(LocationSharingService.EXTRA_ROOM_ID, event.roomId) + putExtra(LocationSharingService.EXTRA_ROOM_ARGS, + LocationSharingService.RoomArgs(event.sessionId, event.roomId, event.duration)) } .also { ContextCompat.startForegroundService(requireContext(), it) @@ -236,7 +236,9 @@ class LocationSharingFragment @Inject constructor( } private fun startLiveLocationSharing() { - viewModel.handle(LocationSharingAction.StartLiveLocationSharing) + // TODO. Get duration from user + val duration = 30 * 1000L + viewModel.handle(LocationSharingAction.StartLiveLocationSharing(duration)) } private fun updateMap(state: LocationSharingViewState) { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 1253891421..183d3ee2ab 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -18,44 +18,87 @@ package im.vector.app.features.location import android.content.Intent import android.os.IBinder +import android.os.Parcelable import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.services.VectorService import im.vector.app.features.notifications.NotificationUtils +import kotlinx.parcelize.Parcelize import timber.log.Timber +import java.util.Timer +import java.util.TimerTask import javax.inject.Inject @AndroidEntryPoint class LocationSharingService : VectorService(), LocationTracker.Callback { + @Parcelize + data class RoomArgs( + val sessionId: String, + val roomId: String, + val durationMillis: Long + ) : Parcelable + @Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var locationTracker: LocationTracker - private var sessionId: String? = null - private var roomId: String? = null + private var roomArgsList = mutableListOf() - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - sessionId = intent?.getStringExtra(EXTRA_SESSION_ID) - roomId = intent?.getStringExtra(EXTRA_ROOM_ID) - - if (sessionId == null || roomId == null) { - stopForeground(true) - stopSelf() - } - - // Show a sticky notification - val notification = notificationUtils.buildLiveLocationSharingNotification() - startForeground(roomId!!.hashCode(), notification) + override fun onCreate() { + super.onCreate() + Timber.d("### LocationSharingService.onCreate") // Start tracking location locationTracker.addCallback(this) locationTracker.start() + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val roomArgs = intent?.getParcelableExtra(EXTRA_ROOM_ARGS) as? RoomArgs + + Timber.d("### LocationSharingService.onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}") + + if (roomArgs != null) { + roomArgsList.add(roomArgs) + + // Show a sticky notification + val notification = notificationUtils.buildLiveLocationSharingNotification() + startForeground(roomArgs.roomId.hashCode(), notification) + + // Schedule a timer to stop sharing + scheduleTimer(roomArgs.roomId, roomArgs.durationMillis) + } return START_STICKY } + private fun scheduleTimer(roomId: String, durationMillis: Long) { + Timer().schedule(object : TimerTask() { + override fun run() { + stopSharingLocation(roomId) + } + }, durationMillis) + } + + private fun stopSharingLocation(roomId: String) { + Timber.d("### LocationSharingService.stopSharingLocation for $roomId") + synchronized(roomArgsList) { + roomArgsList.removeAll { it.roomId == roomId } + if (roomArgsList.isEmpty()) { + Timber.d("### LocationSharingService. Destroying self, time is up for all rooms") + destroyMe() + } + } + } + + private fun destroyMe() { + locationTracker.removeCallback(this) + stopSelf() + } + override fun onDestroy() { super.onDestroy() - locationTracker.removeCallback(this) + Timber.d("### LocationSharingService.onDestroy") + destroyMe() } override fun onBind(intent: Intent?): IBinder? { @@ -63,12 +106,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } companion object { - const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID" - const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" + const val EXTRA_ROOM_ARGS = "EXTRA_ROOM_ARGS" } override fun onLocationUpdate(locationData: LocationData) { - Timber.d("### LocationSharingService.onLocationUpdate: ${locationData.latitude} - ${locationData.longitude}") + Timber.d("### LocationSharingService.onLocationUpdate. Lat - lon: ${locationData.latitude} - ${locationData.longitude}") } override fun onLocationProviderIsNotAvailable() { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt index a7204f02c1..b25a4988b0 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewEvents.kt @@ -22,5 +22,5 @@ sealed class LocationSharingViewEvents : VectorViewEvents { object Close : LocationSharingViewEvents() object LocationNotAvailableError : LocationSharingViewEvents() data class ZoomToUserLocation(val userLocation: LocationData) : LocationSharingViewEvents() - data class StartLiveLocationService(val sessionId: String, val roomId: String) : LocationSharingViewEvents() + data class StartLiveLocationService(val sessionId: String, val roomId: String, val duration: Long) : LocationSharingViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index b2bf6d1762..8293ac6143 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -118,11 +118,11 @@ class LocationSharingViewModel @AssistedInject constructor( override fun handle(action: LocationSharingAction) { when (action) { - LocationSharingAction.CurrentUserLocationSharing -> handleCurrentUserLocationSharingAction() - is LocationSharingAction.PinnedLocationSharing -> handlePinnedLocationSharingAction(action) - is LocationSharingAction.LocationTargetChange -> handleLocationTargetChangeAction(action) - LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction() - LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction() + LocationSharingAction.CurrentUserLocationSharing -> handleCurrentUserLocationSharingAction() + is LocationSharingAction.PinnedLocationSharing -> handlePinnedLocationSharingAction(action) + is LocationSharingAction.LocationTargetChange -> handleLocationTargetChangeAction(action) + LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction() + is LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction(action.duration) }.exhaustive } @@ -160,10 +160,11 @@ class LocationSharingViewModel @AssistedInject constructor( } } - private fun handleStartLiveLocationSharingAction() { + private fun handleStartLiveLocationSharingAction(duration: Long) { _viewEvents.post(LocationSharingViewEvents.StartLiveLocationService( sessionId = session.sessionId, - roomId = room.roomId + roomId = room.roomId, + duration = duration )) } From 3fa4aea506899f09a177b4a0dc4547f3b20136d7 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 23 Mar 2022 15:37:44 +0300 Subject: [PATCH 06/13] Navigate to room list when user clicks to the sticky notification. --- .../app/features/location/LocationSharingService.kt | 12 ++++++------ .../app/features/notifications/NotificationUtils.kt | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 183d3ee2ab..f62ad6465f 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -45,7 +45,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onCreate() { super.onCreate() - Timber.d("### LocationSharingService.onCreate") + Timber.i("### LocationSharingService.onCreate") // Start tracking location locationTracker.addCallback(this) @@ -55,7 +55,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val roomArgs = intent?.getParcelableExtra(EXTRA_ROOM_ARGS) as? RoomArgs - Timber.d("### LocationSharingService.onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}") + Timber.i("### LocationSharingService.onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}") if (roomArgs != null) { roomArgsList.add(roomArgs) @@ -80,11 +80,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private fun stopSharingLocation(roomId: String) { - Timber.d("### LocationSharingService.stopSharingLocation for $roomId") + Timber.i("### LocationSharingService.stopSharingLocation for $roomId") synchronized(roomArgsList) { roomArgsList.removeAll { it.roomId == roomId } if (roomArgsList.isEmpty()) { - Timber.d("### LocationSharingService. Destroying self, time is up for all rooms") + Timber.i("### LocationSharingService. Destroying self, time is up for all rooms") destroyMe() } } @@ -97,7 +97,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onDestroy() { super.onDestroy() - Timber.d("### LocationSharingService.onDestroy") + Timber.i("### LocationSharingService.onDestroy") destroyMe() } @@ -110,7 +110,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } override fun onLocationUpdate(locationData: LocationData) { - Timber.d("### LocationSharingService.onLocationUpdate. Lat - lon: ${locationData.latitude} - ${locationData.longitude}") + Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") } override fun onLocationProviderIsNotAvailable() { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index 8120134526..161b58d53d 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -531,6 +531,7 @@ class NotificationUtils @Inject constructor(private val context: Context, .setSmallIcon(R.drawable.ic_attachment_location_live_white) .setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary)) .setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING) + .setContentIntent(buildOpenHomePendingIntentForSummary()) .build() } From 96a2bc9ce47fc807e5f5f60174ced5bfa13246bd Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 23 Mar 2022 15:47:40 +0300 Subject: [PATCH 07/13] Changelog added. --- changelog.d/5595.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5595.feature diff --git a/changelog.d/5595.feature b/changelog.d/5595.feature new file mode 100644 index 0000000000..02ae703bdd --- /dev/null +++ b/changelog.d/5595.feature @@ -0,0 +1 @@ +Live Location Sharing - Foreground Service \ No newline at end of file From 5f74442f5746f17347dd89882dba24ddbd2e387c Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 23 Mar 2022 16:34:45 +0300 Subject: [PATCH 08/13] Reorder functions. --- .../location/LocationSharingService.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index f62ad6465f..b11ddf93e2 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -90,6 +90,15 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } } + override fun onLocationUpdate(locationData: LocationData) { + Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") + } + + override fun onLocationProviderIsNotAvailable() { + stopForeground(true) + stopSelf() + } + private fun destroyMe() { locationTracker.removeCallback(this) stopSelf() @@ -108,13 +117,4 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { companion object { const val EXTRA_ROOM_ARGS = "EXTRA_ROOM_ARGS" } - - override fun onLocationUpdate(locationData: LocationData) { - Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") - } - - override fun onLocationProviderIsNotAvailable() { - stopForeground(true) - stopSelf() - } } From 7a575ed062f0d7576d11837738fd06310a10cfd1 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 24 Mar 2022 13:10:06 +0300 Subject: [PATCH 09/13] Lint fixes. --- .../im/vector/app/features/location/LocationSharingViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 347aad7db9..dfa936dcaa 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem -import timber.log.Timber /** * Sampling period to compare target location and user location. From 79afdf724f23725fd82eb73046f76e9aa070353b Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 25 Mar 2022 13:48:20 +0300 Subject: [PATCH 10/13] Code review fixes. --- vector/src/main/AndroidManifest.xml | 1 - .../app/features/location/LocationSharingFragment.kt | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 71ea6aaf8f..d52e95b382 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -46,7 +46,6 @@ - diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index d4582d98b9..b779b50c8b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -180,11 +180,10 @@ class LocationSharingFragment @Inject constructor( } private fun handleStartLiveLocationService(event: LocationSharingViewEvents.StartLiveLocationService) { + val args = LocationSharingService.RoomArgs(event.sessionId, event.roomId, event.duration) + Intent(requireContext(), LocationSharingService::class.java) - .apply { - putExtra(LocationSharingService.EXTRA_ROOM_ARGS, - LocationSharingService.RoomArgs(event.sessionId, event.roomId, event.duration)) - } + .putExtra(LocationSharingService.EXTRA_ROOM_ARGS, args) .also { ContextCompat.startForegroundService(requireContext(), it) } From 7285bc6889aacafe09ccf6640a58d438fd58f54f Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 25 Mar 2022 13:58:48 +0300 Subject: [PATCH 11/13] Code review fixes. --- .../app/features/location/LocationTracker.kt | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index d5e298a14b..b7006370a6 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -93,19 +93,15 @@ class LocationTracker @Inject constructor( } fun addCallback(callback: Callback) { - synchronized(callbacks) { - if (!callbacks.contains(callback)) { - callbacks.add(callback) - } + if (!callbacks.contains(callback)) { + callbacks.add(callback) } } fun removeCallback(callback: Callback) { - synchronized(callbacks) { - callbacks.remove(callback) - if (callbacks.size == 0) { - stop() - } + callbacks.remove(callback) + if (callbacks.size == 0) { + stop() } } From bdbdfe52cdbd3f2a1e8560b1ce5ee7d54ae41b29 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 25 Mar 2022 14:24:42 +0300 Subject: [PATCH 12/13] Cancel timers when service is destroyed. --- .../location/LocationSharingService.kt | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index b11ddf93e2..a2a68e4188 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -42,6 +42,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { @Inject lateinit var locationTracker: LocationTracker private var roomArgsList = mutableListOf() + private var timers = mutableListOf() override fun onCreate() { super.onCreate() @@ -72,11 +73,18 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private fun scheduleTimer(roomId: String, durationMillis: Long) { - Timer().schedule(object : TimerTask() { - override fun run() { - stopSharingLocation(roomId) - } - }, durationMillis) + Timer() + .apply { + schedule(object : TimerTask() { + override fun run() { + stopSharingLocation(roomId) + timers.remove(this@apply) + } + }, durationMillis) + } + .also { + timers.add(it) + } } private fun stopSharingLocation(roomId: String) { @@ -101,6 +109,8 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { private fun destroyMe() { locationTracker.removeCallback(this) + timers.forEach { it.cancel() } + timers.clear() stopSelf() } From 9b271e4ffde152b872922ec5fa0c08a92779d591 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 28 Mar 2022 12:11:22 +0300 Subject: [PATCH 13/13] Trigger github actions. --- changelog.d/5595.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/5595.feature b/changelog.d/5595.feature index 02ae703bdd..8fd4d4b144 100644 --- a/changelog.d/5595.feature +++ b/changelog.d/5595.feature @@ -1 +1 @@ -Live Location Sharing - Foreground Service \ No newline at end of file +Live Location Sharing - Foreground Service and Notification \ No newline at end of file