mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-01 19:56:47 +01:00
Merge pull request #6644 from vector-im/feature/mna/notification-tap-lls
[Location Share] Open maximized map on tapping on live sharing notification (PSG-616)
This commit is contained in:
commit
c7d5ceca5d
1
changelog.d/6642.misc
Normal file
1
changelog.d/6642.misc
Normal file
@ -0,0 +1 @@
|
||||
[Location Share] Open maximized map on tapping on live sharing notification
|
@ -377,7 +377,7 @@
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".features.location.LocationSharingAndroidService"
|
||||
android:name=".features.location.live.tracking.LocationSharingAndroidService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location" />
|
||||
|
||||
|
@ -42,6 +42,7 @@ import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.location.live.LiveLocationLabsFlagPromotionBottomSheet
|
||||
import im.vector.app.features.location.live.duration.ChooseLiveDurationBottomSheet
|
||||
import im.vector.app.features.location.live.tracking.LocationSharingAndroidService
|
||||
import im.vector.app.features.location.option.LocationSharingOption
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
@ -22,6 +22,7 @@ import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.features.location.live.tracking.LocationSharingAndroidService
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -24,6 +24,7 @@ import im.vector.app.R
|
||||
import im.vector.app.core.extensions.addFragment
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityLocationSharingBinding
|
||||
import im.vector.app.features.MainActivity
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@ -59,10 +60,15 @@ class LocationLiveMapViewActivity : VectorBaseActivity<ActivityLocationSharingBi
|
||||
|
||||
private const val EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS = "EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS"
|
||||
|
||||
fun getIntent(context: Context, locationLiveMapViewArgs: LocationLiveMapViewArgs): Intent {
|
||||
return Intent(context, LocationLiveMapViewActivity::class.java).apply {
|
||||
fun getIntent(context: Context, locationLiveMapViewArgs: LocationLiveMapViewArgs, firstStartMainActivity: Boolean = false): Intent {
|
||||
val intent = Intent(context, LocationLiveMapViewActivity::class.java).apply {
|
||||
putExtra(EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS, locationLiveMapViewArgs)
|
||||
}
|
||||
return if (firstStartMainActivity) {
|
||||
MainActivity.getIntentWithNextIntent(context, intent)
|
||||
} else {
|
||||
intent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.tracking
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.createIgnoredUri
|
||||
import im.vector.app.core.platform.PendingIntentCompat
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.home.HomeActivity
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
|
||||
import im.vector.app.features.location.live.map.LocationLiveMapViewActivity
|
||||
import im.vector.app.features.location.live.map.LocationLiveMapViewArgs
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class LiveLocationNotificationBuilder @Inject constructor(
|
||||
private val context: Context,
|
||||
private val stringProvider: StringProvider,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Creates a notification that indicates the application is retrieving location even if it is in background or killed.
|
||||
* @param roomId the id of the room where a live location is shared
|
||||
*/
|
||||
fun buildLiveLocationSharingNotification(roomId: String): Notification {
|
||||
return NotificationCompat.Builder(context, NotificationUtils.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_attachment_location_live_white)
|
||||
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
||||
.setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING)
|
||||
.setContentIntent(buildOpenLiveLocationMapIntent(roomId))
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildOpenLiveLocationMapIntent(roomId: String): PendingIntent? {
|
||||
val homeIntent = HomeActivity.newIntent(context, firstStartMainActivity = false)
|
||||
val roomIntent = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true), firstStartMainActivity = false)
|
||||
val mapIntent = LocationLiveMapViewActivity.getIntent(
|
||||
context = context,
|
||||
locationLiveMapViewArgs = LocationLiveMapViewArgs(roomId = roomId),
|
||||
firstStartMainActivity = true
|
||||
)
|
||||
mapIntent.action = NotificationUtils.TAP_TO_VIEW_ACTION
|
||||
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
||||
mapIntent.data = createIgnoredUri("openLiveLocationMap?$roomId")
|
||||
|
||||
// Recreate the back stack
|
||||
return TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(homeIntent)
|
||||
.addNextIntent(roomIntent)
|
||||
.addNextIntent(mapIntent)
|
||||
.getPendingIntent(
|
||||
clock.epochMillis().toInt(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
}
|
@ -14,18 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.location
|
||||
package im.vector.app.features.location.live.tracking
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.services.VectorAndroidService
|
||||
import im.vector.app.features.location.LocationData
|
||||
import im.vector.app.features.location.LocationTracker
|
||||
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -54,7 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
val durationMillis: Long
|
||||
) : Parcelable
|
||||
|
||||
@Inject lateinit var notificationUtils: NotificationUtils
|
||||
@Inject lateinit var liveLocationNotificationBuilder: LiveLocationNotificationBuilder
|
||||
@Inject lateinit var locationTracker: LocationTracker
|
||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
||||
@ -62,13 +64,11 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
|
||||
private val binder = LocalBinder()
|
||||
|
||||
/**
|
||||
* Keep track of a map between beacon event Id starting the live and RoomArgs.
|
||||
*/
|
||||
private val roomArgsMap = mutableMapOf<String, RoomArgs>()
|
||||
private val liveInfoSet = linkedSetOf<LiveInfo>()
|
||||
var callback: Callback? = null
|
||||
private val jobs = mutableListOf<Job>()
|
||||
private var startInProgress = false
|
||||
private var foregroundModeStarted = false
|
||||
|
||||
private val _roomIdsOfActiveLives = MutableSharedFlow<Set<String>>(replay = 1)
|
||||
val roomIdsOfActiveLives = _roomIdsOfActiveLives.asSharedFlow()
|
||||
@ -76,7 +76,6 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Timber.i("onCreate")
|
||||
|
||||
initLocationTracking()
|
||||
}
|
||||
|
||||
@ -102,8 +101,13 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
|
||||
if (roomArgs != null) {
|
||||
// Show a sticky notification
|
||||
val notification = notificationUtils.buildLiveLocationSharingNotification()
|
||||
startForeground(roomArgs.roomId.hashCode(), notification)
|
||||
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomArgs.roomId)
|
||||
if (foregroundModeStarted) {
|
||||
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||
} else {
|
||||
startForeground(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||
foregroundModeStarted = true
|
||||
}
|
||||
|
||||
// Send beacon info state event
|
||||
launchWithActiveSession { session ->
|
||||
@ -148,15 +152,24 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
private fun stopSharingLocation(beaconEventId: String) {
|
||||
Timber.i("stopSharingLocation for beacon $beaconEventId")
|
||||
removeRoomArgs(beaconEventId)
|
||||
updateNotification()
|
||||
tryToDestroyMe()
|
||||
}
|
||||
|
||||
private fun updateNotification() {
|
||||
if (liveInfoSet.isNotEmpty()) {
|
||||
val roomId = liveInfoSet.last().roomArgs.roomId
|
||||
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomId)
|
||||
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLocationUpdate(locationData: LocationData) {
|
||||
Timber.i("onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
|
||||
|
||||
// Emit location update to all rooms in which live location sharing is active
|
||||
roomArgsMap.toMap().forEach { item ->
|
||||
sendLiveLocation(item.value.roomId, item.key, locationData)
|
||||
liveInfoSet.toSet().forEach { liveInfo ->
|
||||
sendLiveLocation(liveInfo.roomArgs.roomId, liveInfo.beaconEventId, locationData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +196,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
}
|
||||
|
||||
private fun tryToDestroyMe() {
|
||||
if (startInProgress.not() && roomArgsMap.isEmpty()) {
|
||||
if (startInProgress.not() && liveInfoSet.isEmpty()) {
|
||||
Timber.i("Destroying self, time is up for all rooms")
|
||||
stopSelf()
|
||||
}
|
||||
@ -199,13 +212,14 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
|
||||
private fun addRoomArgs(beaconEventId: String, roomArgs: RoomArgs) {
|
||||
Timber.i("adding roomArgs for beaconEventId: $beaconEventId")
|
||||
roomArgsMap[beaconEventId] = roomArgs
|
||||
liveInfoSet.removeAll { it.beaconEventId == beaconEventId }
|
||||
liveInfoSet.add(LiveInfo(beaconEventId, roomArgs))
|
||||
launchWithActiveSession { _roomIdsOfActiveLives.emit(getRoomIdsOfActiveLives()) }
|
||||
}
|
||||
|
||||
private fun removeRoomArgs(beaconEventId: String) {
|
||||
Timber.i("removing roomArgs for beaconEventId: $beaconEventId")
|
||||
roomArgsMap.remove(beaconEventId)
|
||||
liveInfoSet.removeAll { it.beaconEventId == beaconEventId }
|
||||
launchWithActiveSession { _roomIdsOfActiveLives.emit(getRoomIdsOfActiveLives()) }
|
||||
}
|
||||
|
||||
@ -234,7 +248,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
}
|
||||
|
||||
fun getRoomIdsOfActiveLives(): Set<String> {
|
||||
return roomArgsMap.map { it.value.roomId }.toSet()
|
||||
return liveInfoSet.map { it.roomArgs.roomId }.toSet()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
@ -251,5 +265,11 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
||||
|
||||
companion object {
|
||||
const val EXTRA_ROOM_ARGS = "EXTRA_ROOM_ARGS"
|
||||
private const val FOREGROUND_SERVICE_NOTIFICATION_ID = 300
|
||||
}
|
||||
|
||||
private data class LiveInfo(
|
||||
val beaconEventId: String,
|
||||
val roomArgs: RoomArgs
|
||||
)
|
||||
}
|
@ -105,7 +105,7 @@ class NotificationUtils @Inject constructor(
|
||||
const val SMART_REPLY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.SMART_REPLY_ACTION"
|
||||
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
|
||||
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
|
||||
private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
||||
const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
||||
const val DIAGNOSTIC_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DIAGNOSTIC"
|
||||
const val PUSH_ACTION = "${BuildConfig.APPLICATION_ID}.PUSH"
|
||||
|
||||
@ -118,7 +118,7 @@ class NotificationUtils @Inject constructor(
|
||||
|
||||
private const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID"
|
||||
|
||||
private const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2"
|
||||
const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2"
|
||||
private const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V2"
|
||||
|
||||
fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
@ -540,20 +540,6 @@ class NotificationUtils @Inject constructor(
|
||||
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_attachment_location_live_white)
|
||||
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
||||
.setCategory(NotificationCompat.CATEGORY_LOCATION_SHARING)
|
||||
.setContentIntent(buildOpenHomePendingIntentForSummary())
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a notification that indicates the application is capturing the screen.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user