Keep call notification ringing while a call is present in the room (#4634)
This commit is contained in:
parent
3391e7cc55
commit
c39b480f82
5 changed files with 217 additions and 99 deletions
|
|
@ -9,16 +9,22 @@ package io.element.android.libraries.push.impl.notifications
|
|||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.notification.CallNotifyType
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
||||
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Helper to resolve a valid [NotifiableEvent] from a [NotificationData].
|
||||
|
|
@ -31,7 +37,7 @@ interface CallNotificationEventResolver {
|
|||
* @param forceNotify `true` to force the notification to be non-ringing, `false` to use the default behaviour. Default is `false`.
|
||||
* @return a [NotifiableEvent] if the notification data is a call notification, null otherwise
|
||||
*/
|
||||
fun resolveEvent(
|
||||
suspend fun resolveEvent(
|
||||
sessionId: SessionId,
|
||||
notificationData: NotificationData,
|
||||
forceNotify: Boolean = false,
|
||||
|
|
@ -41,8 +47,10 @@ interface CallNotificationEventResolver {
|
|||
@ContributesBinding(AppScope::class)
|
||||
class DefaultCallNotificationEventResolver @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val appForegroundStateService: AppForegroundStateService,
|
||||
private val clientProvider: MatrixClientProvider,
|
||||
) : CallNotificationEventResolver {
|
||||
override fun resolveEvent(
|
||||
override suspend fun resolveEvent(
|
||||
sessionId: SessionId,
|
||||
notificationData: NotificationData,
|
||||
forceNotify: Boolean
|
||||
|
|
@ -50,8 +58,32 @@ class DefaultCallNotificationEventResolver @Inject constructor(
|
|||
val content = notificationData.content as? NotificationContent.MessageLike.CallNotify
|
||||
?: throw ResolvingException("content is not a call notify")
|
||||
|
||||
val previousRingingCallStatus = appForegroundStateService.hasRingingCall.value
|
||||
// We need the sync service working to get the updated room info
|
||||
val isRoomCallActive = runCatching {
|
||||
if (content.type == CallNotifyType.RING) {
|
||||
appForegroundStateService.updateHasRingingCall(true)
|
||||
|
||||
val client = clientProvider.getOrRestore(sessionId).getOrNull() ?: throw ResolvingException("Session $sessionId not found")
|
||||
val room = client.getRoom(notificationData.roomId) ?: throw ResolvingException("Room ${notificationData.roomId} not found")
|
||||
// Give a few seconds for the room info flow to catch up with the sync, if needed - this is usually instant
|
||||
val isActive = withTimeoutOrNull(3.seconds) { room.roomInfoFlow.firstOrNull { it.hasRoomCall } }?.hasRoomCall ?: false
|
||||
|
||||
// We no longer need the sync service to be active because of a call notification.
|
||||
appForegroundStateService.updateHasRingingCall(previousRingingCallStatus)
|
||||
|
||||
isActive
|
||||
} else {
|
||||
// If the call notification is not of ringing type, we don't need to check if the call is active
|
||||
false
|
||||
}
|
||||
}.onFailure {
|
||||
// Make sure to reset the hasRingingCall state in case of failure
|
||||
appForegroundStateService.updateHasRingingCall(previousRingingCallStatus)
|
||||
}.getOrDefault(false)
|
||||
|
||||
notificationData.run {
|
||||
if (NotifiableRingingCallEvent.shouldRing(content.type, timestamp) && !forceNotify) {
|
||||
if (content.type == CallNotifyType.RING && isRoomCallActive && !forceNotify) {
|
||||
NotifiableRingingCallEvent(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
|
|
@ -70,9 +102,7 @@ class DefaultCallNotificationEventResolver @Inject constructor(
|
|||
senderAvatarUrl = senderAvatarUrl,
|
||||
)
|
||||
} else {
|
||||
val now = System.currentTimeMillis()
|
||||
val elapsed = now - timestamp
|
||||
Timber.d("Event $eventId is call notify but should not ring: $timestamp vs $now ($elapsed ms elapsed), notify: ${content.type}")
|
||||
Timber.d("Event $eventId is call notify but should not ring: $isRoomCallActive, notify: ${content.type}")
|
||||
// Create a simple message notification event
|
||||
buildNotifiableMessageEvent(
|
||||
sessionId = sessionId,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallNotifyType
|
||||
import java.time.Instant
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
data class NotifiableRingingCallEvent(
|
||||
override val sessionId: SessionId,
|
||||
|
|
@ -31,13 +29,4 @@ data class NotifiableRingingCallEvent(
|
|||
val roomAvatarUrl: String? = null,
|
||||
val callNotifyType: CallNotifyType,
|
||||
val timestamp: Long,
|
||||
) : NotifiableEvent {
|
||||
companion object {
|
||||
fun shouldRing(callNotifyType: CallNotifyType, timestamp: Long): Boolean {
|
||||
val timeout = 10.seconds.inWholeMilliseconds
|
||||
val elapsed = Instant.now().toEpochMilli() - timestamp
|
||||
// Only ring if the type is RING and the elapsed time is less than the timeout
|
||||
return callNotifyType == CallNotifyType.RING && elapsed < timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
) : NotifiableEvent
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue