From 98882735bf26546dda0d087d4147921d0c16798d Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sat, 13 Jul 2024 14:22:10 +0700 Subject: [PATCH 01/15] #7761 Added ring when starting jitsi call --- .../app/core/services/CallAndroidService.kt | 29 +++++++++++++++++++ .../call/conference/VectorJitsiActivity.kt | 19 ++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index a4e3872e0f2..b066db575a8 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -131,6 +131,13 @@ class CallAndroidService : VectorAndroidService() { ACTION_CALL_TERMINATED -> { handleCallTerminated(intent) } + ACTION_OUTGOING_JITSI_RINGING_CALL -> { + mediaSession?.isActive = true + callRingPlayerOutgoing?.start() + } + ACTION_JITSI_CALL_TERMINATED -> { + callRingPlayerOutgoing?.stop() + } else -> { handleUnexpectedState(null) } @@ -316,8 +323,10 @@ class CallAndroidService : VectorAndroidService() { private const val ACTION_INCOMING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_RINGING_CALL" private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_RINGING_CALL" + private const val ACTION_OUTGOING_JITSI_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_JITSI_RINGING_CALL" private const val ACTION_ONGOING_CALL = "im.vector.app.core.services.CallService.ACTION_ONGOING_CALL" private const val ACTION_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_CALL_TERMINATED" + private const val ACTION_JITSI_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_JITSI_CALL_TERMINATED" private const val EXTRA_CALL_ID = "EXTRA_CALL_ID" private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG" @@ -377,6 +386,26 @@ class CallAndroidService : VectorAndroidService() { } context.startService(intent) } + + fun onOutgoingJitsiCallRinging( + context: Context, + ) { + val intent = Intent(context, CallAndroidService::class.java) + .apply { + action = ACTION_OUTGOING_JITSI_RINGING_CALL + } + ContextCompat.startForegroundService(context, intent) + } + + fun onCancelJitsiCallRinging( + context: Context, + ) { + val intent = Intent(context, CallAndroidService::class.java) + .apply { + action = ACTION_JITSI_CALL_TERMINATED + } + context.startService(intent) + } } inner class CallServiceBinder : Binder() { diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 1f5770a6fb7..5051242ae03 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -39,6 +39,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.services.CallAndroidService import im.vector.app.databinding.ActivityJitsiBinding import im.vector.lib.core.utils.compat.getParcelableExtraCompat import kotlinx.parcelize.Parcelize @@ -87,10 +88,10 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee jitsiViewModel.observeViewEvents { when (it) { - is JitsiCallViewEvents.JoinConference -> configureJitsiView(it) + is JitsiCallViewEvents.JoinConference -> handleJoinConference(it) is JitsiCallViewEvents.ConfirmSwitchingConference -> handleConfirmSwitching(it) JitsiCallViewEvents.FailJoiningConference -> handleFailJoining() - JitsiCallViewEvents.Finish -> finish() + JitsiCallViewEvents.Finish -> handleFinish() JitsiCallViewEvents.LeaveConference -> handleLeaveConference() } } @@ -150,6 +151,17 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee private fun handleLeaveConference() { val leaveBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent() LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(leaveBroadcastIntent) + CallAndroidService.onCancelJitsiCallRinging(applicationContext) + } + + private fun handleJoinConference(joinConference: JitsiCallViewEvents.JoinConference) { + configureJitsiView(joinConference) + CallAndroidService.onOutgoingJitsiCallRinging(applicationContext) + } + + private fun handleFinish() { + CallAndroidService.onCancelJitsiCallRinging(applicationContext) + finish() } private fun handleConfirmSwitching(action: JitsiCallViewEvents.ConfirmSwitchingConference) { @@ -161,6 +173,8 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee } .setNegativeButton(R.string.action_cancel, null) .show() + + CallAndroidService.onCancelJitsiCallRinging(applicationContext) } private val pictureInPictureModeChangedInfoConsumer = Consumer { @@ -192,6 +206,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee private fun handleFailJoining() { Toast.makeText(this, getString(R.string.error_jitsi_join_conf), Toast.LENGTH_LONG).show() + CallAndroidService.onCancelJitsiCallRinging(applicationContext) finish() } From 42fbd155d7f617843fe3f1d81fb2a005eefdc24f Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sun, 18 Aug 2024 12:40:17 +0700 Subject: [PATCH 02/15] #7761 Added ring notification for jitsi calls, and handled decline event --- .../sdk/api/session/events/model/Event.kt | 2 +- .../pushrules/ProcessEventForPushTask.kt | 1 + .../app/core/services/CallAndroidService.kt | 119 +++++++++++++++--- .../call/conference/VectorJitsiActivity.kt | 18 +-- .../call/service/CallHeadsUpActionReceiver.kt | 2 + .../notifications/NotificationUtils.kt | 85 +++++++++++++ .../notifications/PushRuleTriggerListener.kt | 22 +++- 7 files changed, 213 insertions(+), 36 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 196b419598f..d33fd93fc16 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -499,7 +499,7 @@ fun Event.getPollContent(): MessagePollContent? { } fun Event.supportsNotification() = - this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values + this.getClearType() in EventType.STATE_ROOM_WIDGET_LEGACY + EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values fun Event.isContentReportable() = this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushrules/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushrules/ProcessEventForPushTask.kt index d000d709a92..f9f701548f5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushrules/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushrules/ProcessEventForPushTask.kt @@ -64,6 +64,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor( EventType.MESSAGE, EventType.REDACTION, EventType.ENCRYPTED, + EventType.STATE_ROOM_WIDGET_LEGACY, EventType.STATE_ROOM_MEMBER -> true else -> false } diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index b066db575a8..e57764d8b85 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -131,12 +131,14 @@ class CallAndroidService : VectorAndroidService() { ACTION_CALL_TERMINATED -> { handleCallTerminated(intent) } - ACTION_OUTGOING_JITSI_RINGING_CALL -> { - mediaSession?.isActive = true - callRingPlayerOutgoing?.start() - } ACTION_JITSI_CALL_TERMINATED -> { - callRingPlayerOutgoing?.stop() + handleJitsiCallTerminated(intent) + } + ACTION_INCOMING_JITSI_RINGING_CALL -> { + mediaSession?.isActive = true + val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false) + callRingPlayerIncoming?.start(fromBg) + displayIncomingJitsiCallNotification(intent) } else -> { handleUnexpectedState(null) @@ -196,6 +198,55 @@ class CallAndroidService : VectorAndroidService() { knownCalls[callId] = callInformation } + /** + * Display a permanent notification when there is an incoming call. + * + */ + private fun displayIncomingJitsiCallNotification(intent: Intent) { + Timber.tag(loggerTag.value).v("displayIncomingJitsiCallNotification $intent") + val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty() + val signalingRoomId = intent.getStringExtra(EXTRA_SIGNALING_ROOM_ID).orEmpty() + val otherUserId = intent.getStringExtra(EXTRA_OTHER_USER_ID).orEmpty() + + val matrixItem = MatrixItem.RoomItem( + id = signalingRoomId, + displayName = otherUserId, + ) + + val isVideoCall = true + val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false) + Timber.tag(loggerTag.value).v("displayIncomingJitsiCallNotification : display the dedicated notification") + val incomingCallAlert = IncomingCallAlert(callId, + shouldBeDisplayedIn = { activity -> + if (activity is VectorCallActivity) { + activity.intent.getParcelableExtraCompat(Mavericks.KEY_ARG)?.callId != callId + } else true + } + ).apply { + viewBinder = IncomingCallAlert.ViewBinder( + matrixItem = matrixItem, + avatarRenderer = avatarRenderer, + isVideoCall = isVideoCall, + onAccept = { }, + onReject = { onJitsiCallTerminated(applicationContext, callId) } + ) + dismissedAction = Runnable { onJitsiCallTerminated(applicationContext, callId) } + contentAction = Runnable { } + } + alertManager.postVectorAlert(incomingCallAlert) + val notification = notificationUtils.buildIncomingJitsiCallNotification( + callId = callId, + signalingRoomId = signalingRoomId, + isIncomingCall = true, + isVideoCall = isVideoCall, + otherUserId = otherUserId, + title = otherUserId, + fromBg = fromBg, + ) + + startForegroundCompat(callId.hashCode(), notification) + } + private fun handleCallTerminated(intent: Intent) { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val endCallReason = intent.getSerializableExtraCompat(EXTRA_END_CALL_REASON) @@ -223,6 +274,28 @@ class CallAndroidService : VectorAndroidService() { } } + private fun handleJitsiCallTerminated(intent: Intent) { + val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" +// val endCallReason = intent.getSerializableExtraCompat(EXTRA_END_CALL_REASON) +// val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false) + alertManager.cancelAlert(callId) + + val notification = notificationUtils.buildCallEndedNotification(true) + val notificationId = callId.hashCode() + startForegroundCompat(notificationId, notification) + Timber.tag(loggerTag.value).v("No more call, stop the service") + stopForegroundCompat() + mediaSession?.isActive = false + myStopSelf() + +// val wasConnected = connectedCallIds.remove(callId) +// if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) { +// val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall) +// notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification) +// } + } + + private fun showCallScreen(call: WebRtcCall, mode: String) { val intent = VectorCallActivity.newIntent( context = this, @@ -323,12 +396,14 @@ class CallAndroidService : VectorAndroidService() { private const val ACTION_INCOMING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_RINGING_CALL" private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_RINGING_CALL" - private const val ACTION_OUTGOING_JITSI_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_JITSI_RINGING_CALL" + private const val ACTION_INCOMING_JITSI_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_JITSI_RINGING_CALL" private const val ACTION_ONGOING_CALL = "im.vector.app.core.services.CallService.ACTION_ONGOING_CALL" private const val ACTION_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_CALL_TERMINATED" private const val ACTION_JITSI_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_JITSI_CALL_TERMINATED" private const val EXTRA_CALL_ID = "EXTRA_CALL_ID" + private const val EXTRA_SIGNALING_ROOM_ID = "EXTRA_SIGNALING_ROOM_ID" + private const val EXTRA_OTHER_USER_ID = "EXTRA_OTHER_USER_ID" private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG" private const val EXTRA_END_CALL_REJECTED = "EXTRA_END_CALL_REJECTED" private const val EXTRA_END_CALL_REASON = "EXTRA_END_CALL_REASON" @@ -347,6 +422,24 @@ class CallAndroidService : VectorAndroidService() { ContextCompat.startForegroundService(context, intent) } + fun onIncomingJitsiCallRinging( + context: Context, + callId: String, + signalingRoomId: String, + otherUserId: String, + isInBackground: Boolean + ) { + val intent = Intent(context, CallAndroidService::class.java) + .apply { + action = ACTION_INCOMING_JITSI_RINGING_CALL + putExtra(EXTRA_CALL_ID, callId) + putExtra(EXTRA_SIGNALING_ROOM_ID, signalingRoomId) + putExtra(EXTRA_OTHER_USER_ID, otherUserId) + putExtra(EXTRA_IS_IN_BG, isInBackground) + } + ContextCompat.startForegroundService(context, intent) + } + fun onOutgoingCallRinging( context: Context, callId: String @@ -387,22 +480,14 @@ class CallAndroidService : VectorAndroidService() { context.startService(intent) } - fun onOutgoingJitsiCallRinging( - context: Context, - ) { - val intent = Intent(context, CallAndroidService::class.java) - .apply { - action = ACTION_OUTGOING_JITSI_RINGING_CALL - } - ContextCompat.startForegroundService(context, intent) - } - - fun onCancelJitsiCallRinging( + fun onJitsiCallTerminated( context: Context, + callId: String, ) { val intent = Intent(context, CallAndroidService::class.java) .apply { action = ACTION_JITSI_CALL_TERMINATED + putExtra(EXTRA_CALL_ID, callId) } context.startService(intent) } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 5051242ae03..5eb683f77cc 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -88,10 +88,10 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee jitsiViewModel.observeViewEvents { when (it) { - is JitsiCallViewEvents.JoinConference -> handleJoinConference(it) + is JitsiCallViewEvents.JoinConference -> configureJitsiView(it) is JitsiCallViewEvents.ConfirmSwitchingConference -> handleConfirmSwitching(it) JitsiCallViewEvents.FailJoiningConference -> handleFailJoining() - JitsiCallViewEvents.Finish -> handleFinish() + JitsiCallViewEvents.Finish -> finish() JitsiCallViewEvents.LeaveConference -> handleLeaveConference() } } @@ -151,17 +151,6 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee private fun handleLeaveConference() { val leaveBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent() LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(leaveBroadcastIntent) - CallAndroidService.onCancelJitsiCallRinging(applicationContext) - } - - private fun handleJoinConference(joinConference: JitsiCallViewEvents.JoinConference) { - configureJitsiView(joinConference) - CallAndroidService.onOutgoingJitsiCallRinging(applicationContext) - } - - private fun handleFinish() { - CallAndroidService.onCancelJitsiCallRinging(applicationContext) - finish() } private fun handleConfirmSwitching(action: JitsiCallViewEvents.ConfirmSwitchingConference) { @@ -173,8 +162,6 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee } .setNegativeButton(R.string.action_cancel, null) .show() - - CallAndroidService.onCancelJitsiCallRinging(applicationContext) } private val pictureInPictureModeChangedInfoConsumer = Consumer { @@ -206,7 +193,6 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee private fun handleFailJoining() { Toast.makeText(this, getString(R.string.error_jitsi_join_conf), Toast.LENGTH_LONG).show() - CallAndroidService.onCancelJitsiCallRinging(applicationContext) finish() } diff --git a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt index 161aa33d1df..c7b4b99dc88 100644 --- a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import im.vector.app.core.extensions.singletonEntryPoint +import im.vector.app.core.services.CallAndroidService import im.vector.app.features.call.webrtc.WebRtcCallManager import timber.log.Timber @@ -36,6 +37,7 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() { when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) { CALL_ACTION_REJECT -> { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return + CallAndroidService.onJitsiCallTerminated(context, callId) onCallRejectClicked(webRtcCallManager, callId) } } 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 908a1ed3405..af74e4a1c32 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 @@ -363,6 +363,91 @@ class NotificationUtils @Inject constructor( return builder.build() } + /** + * Build an incoming call notification. + * This notification starts the VectorHomeActivity which is in charge of centralizing the incoming call flow. + * + * @param call information about the call + * @param title title of the notification + * @param fromBg true if the app is in background when posting the notification + * @return the call notification. + */ + fun buildIncomingJitsiCallNotification( + callId: String, + signalingRoomId: String, + otherUserId: String, + isIncomingCall: Boolean, + isVideoCall: Boolean, + title: String, + fromBg: Boolean, + ): Notification { + val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color) + val notificationChannel = if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID + val builder = NotificationCompat.Builder(context, notificationChannel) + .setContentTitle(ensureTitleNotEmpty(title)) + .apply { + if (true) { + setContentText(stringProvider.getString(R.string.incoming_video_call)) + setSmallIcon(R.drawable.ic_call_answer_video) + } else { + setContentText(stringProvider.getString(R.string.incoming_voice_call)) + setSmallIcon(R.drawable.ic_call_answer) + } + } + .setCategory(NotificationCompat.CATEGORY_CALL) + .setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary)) + .setLights(accentColor, 500, 500) + .setOngoing(true) + + val contentIntent = VectorCallActivity.newIntent( + context = context, + callId = callId, + signalingRoomId = signalingRoomId, + isIncomingCall = isIncomingCall, + isVideoCall = isVideoCall, + otherUserId = otherUserId, + mode = VectorCallActivity.INCOMING_RINGING + ).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + data = createIgnoredUri(callId) + } + val contentPendingIntent = PendingIntent.getActivity( + context, + clock.epochMillis().toInt(), + contentIntent, + PendingIntentCompat.FLAG_IMMUTABLE + ) + + val answerCallPendingIntent = TaskStackBuilder.create(context) + .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false)) + .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) + + val rejectCallPendingIntent = buildRejectCallPendingIntent(callId) + + builder.addAction( + NotificationCompat.Action( + IconCompat.createWithResource(context, R.drawable.ic_call_hangup) + .setTint(ThemeUtils.getColor(context, R.attr.colorError)), + getActionText(R.string.call_notification_reject, R.attr.colorError), + rejectCallPendingIntent + ) + ) + + builder.addAction( + NotificationCompat.Action( + R.drawable.ic_call_answer, + getActionText(R.string.call_notification_answer, R.attr.colorPrimary), + answerCallPendingIntent + ) + ) + if (fromBg) { + // Compat: Display the incoming call notification on the lock screen + builder.priority = NotificationCompat.PRIORITY_HIGH + builder.setFullScreenIntent(contentPendingIntent, true) + } + return builder.build() + } + fun buildOutgoingRingingCallNotification( call: WebRtcCall, title: String diff --git a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt index 58f895bf0cb..ccab8d8f288 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt @@ -16,6 +16,8 @@ package im.vector.app.features.notifications +import android.content.Context +import im.vector.app.core.services.CallAndroidService import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -32,7 +34,8 @@ import javax.inject.Singleton @Singleton class PushRuleTriggerListener @Inject constructor( private val resolver: NotifiableEventResolver, - private val notificationDrawerManager: NotificationDrawerManager + private val notificationDrawerManager: NotificationDrawerManager, + private val context: Context, ) : PushRuleService.PushRuleListener { private var session: Session? = null @@ -42,9 +45,24 @@ class PushRuleTriggerListener @Inject constructor( scope.launch { session?.let { session -> val notifiableEvents = createNotifiableEvents(pushEvents, session) + val jitsiRingingCallEvent = notifiableEvents.firstOrNull { it is NotifiableMessageEvent } + + jitsiRingingCallEvent?.let { + val event = it as NotifiableMessageEvent + CallAndroidService.onIncomingJitsiCallRinging( + context = context, + callId = event.eventId, + signalingRoomId = event.roomId, + otherUserId = event.matrixID.orEmpty(), + isInBackground = true, + ) + } + notificationDrawerManager.updateEvents { queuedEvents -> notifiableEvents.forEach { notifiableEvent -> - queuedEvents.onNotifiableEventReceived(notifiableEvent) + if (notifiableEvent.eventId != jitsiRingingCallEvent?.eventId) { + queuedEvents.onNotifiableEventReceived(notifiableEvent) + } } queuedEvents.syncRoomEvents(roomsLeft = pushEvents.roomsLeft, roomsJoined = pushEvents.roomsJoined) queuedEvents.markRedacted(pushEvents.redactedEventIds) From 596ca511d2be3329fd4210c7a8dbc05a6b63568b Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Tue, 27 Aug 2024 23:24:08 +0700 Subject: [PATCH 03/15] #7761 Added displaying notification from background --- .../sdk/api/session/events/model/Event.kt | 4 +- .../vector/app/core/di/SingletonEntryPoint.kt | 3 ++ .../app/core/services/CallAndroidService.kt | 33 +++++++------ .../im/vector/app/features/MainActivity.kt | 43 ++++++++++++++++- .../call/service/CallHeadsUpActionReceiver.kt | 3 +- .../notifications/NotifiableEventProcessor.kt | 1 + .../notifications/NotifiableEventResolver.kt | 48 +++++++++++++++++++ .../notifications/NotifiableJitsiEvent.kt | 47 ++++++++++++++++++ .../NotificationDrawerManager.kt | 1 + .../notifications/NotificationEventQueue.kt | 2 + .../notifications/NotificationFactory.kt | 29 +++++++++++ .../notifications/NotificationRenderer.kt | 45 +++++++++++++++-- .../notifications/NotificationUtils.kt | 24 +++------- .../notifications/PushRuleTriggerListener.kt | 16 +------ 14 files changed, 241 insertions(+), 58 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index d33fd93fc16..c691f7839be 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -498,8 +498,10 @@ fun Event.getPollContent(): MessagePollContent? { return getClearContent().toModel() } +fun Event.isJitsiEvent() = this.getClearType() == EventType.STATE_ROOM_WIDGET_LEGACY + fun Event.supportsNotification() = - this.getClearType() in EventType.STATE_ROOM_WIDGET_LEGACY + EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values + this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values fun Event.isContentReportable() = this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values diff --git a/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt index acf22504498..a99523017c3 100644 --- a/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt +++ b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt @@ -25,6 +25,7 @@ import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.navigation.Navigator +import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.pin.PinLocker import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.session.SessionListener @@ -41,6 +42,8 @@ interface SingletonEntryPoint { fun avatarRenderer(): AvatarRenderer + fun notificationUtils(): NotificationUtils + fun activeSessionHolder(): ActiveSessionHolder fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index e57764d8b85..47eb9081e01 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -23,12 +23,14 @@ import android.os.Binder import android.support.v4.media.session.MediaSessionCompat import android.view.KeyEvent import androidx.core.app.NotificationManagerCompat +import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import androidx.media.session.MediaButtonReceiver import com.airbnb.mvrx.Mavericks import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.startForegroundCompat +import im.vector.app.features.MainActivity import im.vector.app.features.call.CallArgs import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.telecom.CallConnection @@ -227,11 +229,11 @@ class CallAndroidService : VectorAndroidService() { matrixItem = matrixItem, avatarRenderer = avatarRenderer, isVideoCall = isVideoCall, - onAccept = { }, + onAccept = { showJitsiCallScreen(callId, signalingRoomId) }, onReject = { onJitsiCallTerminated(applicationContext, callId) } ) dismissedAction = Runnable { onJitsiCallTerminated(applicationContext, callId) } - contentAction = Runnable { } + contentAction = Runnable { showJitsiCallScreen(callId, signalingRoomId) } } alertManager.postVectorAlert(incomingCallAlert) val notification = notificationUtils.buildIncomingJitsiCallNotification( @@ -275,24 +277,10 @@ class CallAndroidService : VectorAndroidService() { } private fun handleJitsiCallTerminated(intent: Intent) { - val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" -// val endCallReason = intent.getSerializableExtraCompat(EXTRA_END_CALL_REASON) -// val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false) + val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty() alertManager.cancelAlert(callId) - - val notification = notificationUtils.buildCallEndedNotification(true) - val notificationId = callId.hashCode() - startForegroundCompat(notificationId, notification) - Timber.tag(loggerTag.value).v("No more call, stop the service") - stopForegroundCompat() mediaSession?.isActive = false myStopSelf() - -// val wasConnected = connectedCallIds.remove(callId) -// if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) { -// val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall) -// notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification) -// } } @@ -305,6 +293,17 @@ class CallAndroidService : VectorAndroidService() { startActivity(intent) } + private fun showJitsiCallScreen(callId: String, roomId: String) { + val intent = MainActivity.jitsiCallIntent( + context = this, + roomId = roomId, + callId = callId, + ) + startActivity(intent) + stopSelf() + stopForegroundCompat() + } + private fun displayOutgoingRingingCallNotification(intent: Intent) { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val call = callManager.getCallById(callId) ?: return Unit.also { diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 0f0ac83903b..8304fd0a2b3 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -43,6 +43,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.threads.ThreadsActivity import im.vector.app.features.location.live.map.LiveLocationMapViewActivity import im.vector.app.features.notifications.NotificationDrawerManager +import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.pin.lockscreen.crypto.LockScreenKeyRepository import im.vector.app.features.pin.lockscreen.pincode.PinCodeHelper @@ -88,7 +89,9 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity private const val EXTRA_NEXT_INTENT = "EXTRA_NEXT_INTENT" private const val EXTRA_INIT_SESSION = "EXTRA_INIT_SESSION" private const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" + private const val EXTRA_CALL_ID = "EXTRA_CALL_ID" private const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT" + private const val ACTION_ROOM_DETAILS_JITSI_CALL = "ROOM_DETAILS_JITSI_CALL" // Special action to clear cache and/or clear credentials fun restartApp(activity: Activity, args: MainActivityArgs) { @@ -112,10 +115,33 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } // Shortcuts can't have intents with parcelables - fun shortcutIntent(context: Context, roomId: String): Intent { + fun shortcutIntent( + context: Context, + roomId: String, + intentFlags: Int? = null, + ): Intent { return Intent(context, MainActivity::class.java).apply { action = ACTION_ROOM_DETAILS_FROM_SHORTCUT + + intentFlags?.let { + flags = it + } + + putExtra(EXTRA_ROOM_ID, roomId) + } + } + + fun jitsiCallIntent( + context: Context, + roomId: String, + callId: String, + ): Intent { + return Intent(context, MainActivity::class.java).apply { + action = ACTION_ROOM_DETAILS_JITSI_CALL + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(EXTRA_ROOM_ID, roomId) + putExtra(EXTRA_CALL_ID, callId) } } @@ -137,6 +163,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity private lateinit var args: MainActivityArgs @Inject lateinit var notificationDrawerManager: NotificationDrawerManager + @Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var uiStateRepository: UiStateRepository @Inject lateinit var shortcutsHandler: ShortcutsHandler @Inject lateinit var pinCodeHelper: PinCodeHelper @@ -210,7 +237,19 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut) } finish() - } else { + } else if (intent.action == ACTION_ROOM_DETAILS_JITSI_CALL) { + val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty() + + notificationUtils.cancelNotificationMessage(callId, NotificationDrawerManager.JITSI_CALL_NOTIFICATION_ID) + + startSyncing() + val roomId = intent.getStringExtra(EXTRA_ROOM_ID) + if (roomId?.isNotEmpty() == true) { + navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut) + } + finish() + } + else { args = parseArgs() if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { clearNotifications() diff --git a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt index c7b4b99dc88..e6a47068eb9 100644 --- a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt @@ -34,10 +34,11 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { val webRtcCallManager = context.singletonEntryPoint().webRtcCallManager() + val notificationUtils = context.singletonEntryPoint().notificationUtils() when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) { CALL_ACTION_REJECT -> { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return - CallAndroidService.onJitsiCallTerminated(context, callId) + notificationUtils.cancelAllNotifications() onCallRejectClicked(webRtcCallManager, callId) } } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt index 81b9844e36e..6e91ecef087 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -41,6 +41,7 @@ class NotifiableEventProcessor @Inject constructor( .also { Timber.d("notification message removed due to being read") } else -> KEEP } + is NotifiableJitsiEvent -> KEEP is SimpleNotifiableEvent -> when (it.type) { EventType.REDACTION -> REMOVE else -> KEEP diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index fa7c3c3f583..e3febe69b07 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId import org.matrix.android.sdk.api.session.events.model.isEdition import org.matrix.android.sdk.api.session.events.model.isImageMessage +import org.matrix.android.sdk.api.session.events.model.isJitsiEvent import org.matrix.android.sdk.api.session.events.model.supportsNotification import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom @@ -77,6 +78,9 @@ class NotifiableEventResolver @Inject constructor( event.supportsNotification() || event.type == EventType.ENCRYPTED -> { resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy) } + event.isJitsiEvent() -> { + resolveJitsiEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy) + } else -> { // If the event can be displayed, display it as is Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule") @@ -130,6 +134,50 @@ class NotifiableEventResolver @Inject constructor( } } + private fun resolveJitsiEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableJitsiEvent?{ + // The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...) + val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/) + + return if (room != null) { + val body = displayableEventFormatter.format(event, isDm = room.roomSummary()?.isDirect.orFalse(), appendAuthor = false).toString() + val roomName = room.roomSummary()?.displayName.orEmpty() + val senderDisplayName = event.senderInfo.disambiguatedDisplayName + + NotifiableJitsiEvent( + eventId = event.root.eventId.orEmpty(), + editedEventId = event.getEditedEventId(), + canBeReplaced = canBeReplaced, + timestamp = event.root.originServerTs ?: 0, + noisy = isNoisy, + senderName = senderDisplayName, + senderId = event.root.senderId, + body = body, + roomId = event.root.roomId!!, + threadId = event.root.getRootThreadEventId(), + roomName = roomName, + roomIsDirect = room.roomSummary()?.isDirect ?: false, + roomAvatarPath = session.contentUrlResolver() + .resolveThumbnail( + room.roomSummary()?.avatarUrl, + 250, + 250, + ContentUrlResolver.ThumbnailMethod.SCALE + ), + senderAvatarPath = session.contentUrlResolver() + .resolveThumbnail( + event.senderInfo.avatarUrl, + 250, + 250, + ContentUrlResolver.ThumbnailMethod.SCALE + ), + matrixID = session.myUserId, + soundName = null + ) + } else { + null + } + } + private suspend fun resolveMessageEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableMessageEvent? { // The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...) val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt new file mode 100644 index 00000000000..90cf6091055 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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.notifications + +import org.matrix.android.sdk.api.session.events.model.EventType + +data class NotifiableJitsiEvent( + override val eventId: String, + override val editedEventId: String?, + override val canBeReplaced: Boolean, + val noisy: Boolean, + val timestamp: Long, + val senderName: String?, + val senderId: String?, + val body: String?, + val roomId: String, + val threadId: String?, + val roomName: String?, + val roomIsDirect: Boolean = false, + val roomAvatarPath: String? = null, + val senderAvatarPath: String? = null, + val matrixID: String? = null, + val soundName: String? = null, + // This is used for >N notification, as the result of a smart reply + val outGoingMessage: Boolean = false, + val outGoingMessageFailed: Boolean = false, + override val isRedacted: Boolean = false, + override val isUpdated: Boolean = false +) : NotifiableEvent { + + val type: String = EventType.MESSAGE + val description: String = body ?: "" + val title: String = senderName ?: "" +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index 61cb14a70ff..5a8b3dc8d13 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -240,5 +240,6 @@ class NotificationDrawerManager @Inject constructor( const val ROOM_MESSAGES_NOTIFICATION_ID = 1 const val ROOM_EVENT_NOTIFICATION_ID = 2 const val ROOM_INVITATION_NOTIFICATION_ID = 3 + const val JITSI_CALL_NOTIFICATION_ID = 4 } } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationEventQueue.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationEventQueue.kt index 8aff9c3bf26..4478a523379 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationEventQueue.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationEventQueue.kt @@ -36,6 +36,7 @@ data class NotificationEventQueue( is InviteNotifiableEvent -> it.copy(isRedacted = true) is NotifiableMessageEvent -> it.copy(isRedacted = true) is SimpleNotifiableEvent -> it.copy(isRedacted = true) + is NotifiableJitsiEvent -> it.copy(isRedacted = true) } } } @@ -117,6 +118,7 @@ data class NotificationEventQueue( is InviteNotifiableEvent -> with.copy(isUpdated = true) is NotifiableMessageEvent -> with.copy(isUpdated = true) is SimpleNotifiableEvent -> with.copy(isUpdated = true) + is NotifiableJitsiEvent -> with.copy(isUpdated = true) } ) } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt index 7292f650091..b8fa99837ee 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -20,6 +20,7 @@ import android.app.Notification import javax.inject.Inject private typealias ProcessedMessageEvents = List> +private typealias ProcessedJitsiEvents = List> class NotificationFactory @Inject constructor( private val notificationUtils: NotificationUtils, @@ -27,6 +28,25 @@ class NotificationFactory @Inject constructor( private val summaryGroupMessageCreator: SummaryGroupMessageCreator ) { + fun Map.toNotifications(): List { + return map { (roomId, events) -> + JitsiNotification.IncomingCall( + roomId = roomId, + eventId = events.firstOrNull()?.event?.eventId.orEmpty(), + roomName = events.firstOrNull()?.event?.roomName.orEmpty(), + notification = notificationUtils.buildIncomingJitsiCallNotification( + callId = events.firstOrNull()?.event?.eventId.orEmpty(), + signalingRoomId = roomId, + otherUserId = events.firstOrNull()?.event?.matrixID.orEmpty(), + isIncomingCall = true, + isVideoCall = true, + title = events.firstOrNull()?.event?.roomName.orEmpty(), + fromBg = true, + ) + ) + } + } + fun Map.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List { return map { (roomId, events) -> when { @@ -117,6 +137,15 @@ sealed interface RoomNotification { } } +sealed interface JitsiNotification { + data class IncomingCall( + val roomId: String, + val eventId: String, + val roomName: String, + val notification: Notification, + ) : JitsiNotification +} + sealed interface OneShotNotification { data class Removed(val key: String) : OneShotNotification data class Append(val notification: Notification, val meta: Meta) : OneShotNotification { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt index ab59b2e6d80..83adfd51762 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -15,19 +15,20 @@ */ package im.vector.app.features.notifications -import android.content.Context import androidx.annotation.WorkerThread +import im.vector.app.features.notifications.NotificationDrawerManager.Companion.JITSI_CALL_NOTIFICATION_ID import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_EVENT_NOTIFICATION_ID import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_INVITATION_NOTIFICATION_ID import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_MESSAGES_NOTIFICATION_ID import im.vector.app.features.notifications.NotificationDrawerManager.Companion.SUMMARY_NOTIFICATION_ID +import im.vector.app.features.settings.VectorPreferences import timber.log.Timber import javax.inject.Inject class NotificationRenderer @Inject constructor( private val notificationDisplayer: NotificationDisplayer, private val notificationFactory: NotificationFactory, - private val appContext: Context + private val vectorPreferences: VectorPreferences, ) { @WorkerThread @@ -38,9 +39,10 @@ class NotificationRenderer @Inject constructor( useCompleteNotificationFormat: Boolean, eventsToProcess: List> ) { - val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType() + val (roomEvents, simpleEvents, invitationEvents, jitsiEvents) = eventsToProcess.groupByType() with(notificationFactory) { val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) + val jitsiNotifications = jitsiEvents.toNotifications() val invitationNotifications = invitationEvents.toNotifications(myUserId) val simpleNotifications = simpleEvents.toNotifications(myUserId) val summaryNotification = createSummaryNotification( @@ -69,6 +71,30 @@ class NotificationRenderer @Inject constructor( } } + Timber.d("Jitsi call notifications count = ${jitsiNotifications.size}") + jitsiNotifications.forEach { wrapper -> + when (wrapper) { + is JitsiNotification.IncomingCall -> { + Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") + if (wrapper.eventId.isNotEmpty()) { + notificationDisplayer.showNotificationMessage(wrapper.eventId, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) + } + } + } + } +// if (vectorPreferences.isJitsiCallNotificationEnabled()) { +// jitsiNotifications.forEach { wrapper -> +// when (wrapper) { +// is JitsiNotification.IncomingCall -> { +// Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") +// if (wrapper.eventId.isNotEmpty()) { +// notificationDisplayer.showNotificationMessage(wrapper.eventId, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) +// } +// } +// } +// } +// } + invitationNotifications.forEach { wrapper -> when (wrapper) { is OneShotNotification.Removed -> { @@ -108,6 +134,7 @@ private fun List>.groupByType(): GroupedNotifica val roomIdToEventMap: MutableMap>> = LinkedHashMap() val simpleEvents: MutableList> = ArrayList() val invitationEvents: MutableList> = ArrayList() + val roomIdToJitsiEventMap: MutableMap>> = LinkedHashMap() forEach { when (val event = it.event) { is InviteNotifiableEvent -> invitationEvents.add(it.castedToEventType()) @@ -115,10 +142,17 @@ private fun List>.groupByType(): GroupedNotifica val roomEvents = roomIdToEventMap.getOrPut(event.roomId) { ArrayList() } roomEvents.add(it.castedToEventType()) } + is NotifiableJitsiEvent -> { + val jitsiEvents = roomIdToJitsiEventMap.getOrPut(event.roomId) { ArrayList() } + val diffInMillis = System.currentTimeMillis() - (it.event as NotifiableJitsiEvent).timestamp + if (diffInMillis < 10000) { + jitsiEvents.add(it.castedToEventType()) + } + } is SimpleNotifiableEvent -> simpleEvents.add(it.castedToEventType()) } } - return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents) + return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents, roomIdToJitsiEventMap) } @Suppress("UNCHECKED_CAST") @@ -127,5 +161,6 @@ private fun ProcessedEvent.castedToEventT data class GroupedNotificationEvents( val roomEvents: Map>>, val simpleEvents: List>, - val invitationEvents: List> + val invitationEvents: List>, + val jitsiEvents: Map>>, ) 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 af74e4a1c32..294cb79d884 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 @@ -386,31 +386,20 @@ class NotificationUtils @Inject constructor( val builder = NotificationCompat.Builder(context, notificationChannel) .setContentTitle(ensureTitleNotEmpty(title)) .apply { - if (true) { - setContentText(stringProvider.getString(R.string.incoming_video_call)) - setSmallIcon(R.drawable.ic_call_answer_video) - } else { - setContentText(stringProvider.getString(R.string.incoming_voice_call)) - setSmallIcon(R.drawable.ic_call_answer) - } + setContentText(stringProvider.getString(R.string.incoming_video_call)) + setSmallIcon(R.drawable.ic_call_answer_video) } .setCategory(NotificationCompat.CATEGORY_CALL) .setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary)) .setLights(accentColor, 500, 500) .setOngoing(true) - val contentIntent = VectorCallActivity.newIntent( + val contentIntent = MainActivity.jitsiCallIntent( context = context, + roomId = signalingRoomId, callId = callId, - signalingRoomId = signalingRoomId, - isIncomingCall = isIncomingCall, - isVideoCall = isVideoCall, - otherUserId = otherUserId, - mode = VectorCallActivity.INCOMING_RINGING - ).apply { - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - data = createIgnoredUri(callId) - } + ) + val contentPendingIntent = PendingIntent.getActivity( context, clock.epochMillis().toInt(), @@ -420,6 +409,7 @@ class NotificationUtils @Inject constructor( val answerCallPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false)) + .addNextIntent(contentIntent) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) val rejectCallPendingIntent = buildRejectCallPendingIntent(callId) diff --git a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt index ccab8d8f288..35eedb33175 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt @@ -45,24 +45,10 @@ class PushRuleTriggerListener @Inject constructor( scope.launch { session?.let { session -> val notifiableEvents = createNotifiableEvents(pushEvents, session) - val jitsiRingingCallEvent = notifiableEvents.firstOrNull { it is NotifiableMessageEvent } - - jitsiRingingCallEvent?.let { - val event = it as NotifiableMessageEvent - CallAndroidService.onIncomingJitsiCallRinging( - context = context, - callId = event.eventId, - signalingRoomId = event.roomId, - otherUserId = event.matrixID.orEmpty(), - isInBackground = true, - ) - } notificationDrawerManager.updateEvents { queuedEvents -> notifiableEvents.forEach { notifiableEvent -> - if (notifiableEvent.eventId != jitsiRingingCallEvent?.eventId) { - queuedEvents.onNotifiableEventReceived(notifiableEvent) - } + queuedEvents.onNotifiableEventReceived(notifiableEvent) } queuedEvents.syncRoomEvents(roomsLeft = pushEvents.roomsLeft, roomsJoined = pushEvents.roomsJoined) queuedEvents.markRedacted(pushEvents.redactedEventIds) From 74a0b180d7877a5a142f504dc3c005f1b72462bb Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sun, 1 Sep 2024 13:25:50 +0700 Subject: [PATCH 04/15] #7761 Fixed case when event info is absent --- .../vector/app/features/notifications/NotificationFactory.kt | 2 +- .../app/features/notifications/NotificationRenderer.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt index b8fa99837ee..47833f057a6 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -35,7 +35,7 @@ class NotificationFactory @Inject constructor( eventId = events.firstOrNull()?.event?.eventId.orEmpty(), roomName = events.firstOrNull()?.event?.roomName.orEmpty(), notification = notificationUtils.buildIncomingJitsiCallNotification( - callId = events.firstOrNull()?.event?.eventId.orEmpty(), + callId = events.firstOrNull()?.event?.eventId.orEmpty().ifEmpty { roomId }, signalingRoomId = roomId, otherUserId = events.firstOrNull()?.event?.matrixID.orEmpty(), isIncomingCall = true, diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt index 83adfd51762..df948cd8b60 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -76,8 +76,9 @@ class NotificationRenderer @Inject constructor( when (wrapper) { is JitsiNotification.IncomingCall -> { Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") - if (wrapper.eventId.isNotEmpty()) { - notificationDisplayer.showNotificationMessage(wrapper.eventId, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) + if (wrapper.eventId.isNotEmpty() || wrapper.roomId.isNotEmpty()) { + val tag = wrapper.eventId.ifEmpty { wrapper.roomId } + notificationDisplayer.showNotificationMessage(tag, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) } } } From 14dfbdb09453c40aa0cd7996065344f8f5914cb2 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sun, 1 Sep 2024 16:11:03 +0700 Subject: [PATCH 05/15] #7761 Added feature flag --- .../src/main/res/values-ru/strings.xml | 2 + .../src/main/res/values/strings.xml | 2 + .../debug/features/DebugVectorFeatures.kt | 4 + .../src/main/res/values/config-settings.xml | 1 + .../app/core/services/CallAndroidService.kt | 74 ------------------- .../im/vector/app/features/VectorFeatures.kt | 2 + .../notifications/NotificationFactory.kt | 3 - .../notifications/NotificationRenderer.kt | 28 +++---- .../notifications/NotificationUtils.kt | 10 +-- .../features/settings/VectorPreferences.kt | 6 ++ .../src/main/res/xml/vector_settings_labs.xml | 6 ++ 11 files changed, 36 insertions(+), 102 deletions(-) diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml index bd1f8b79926..d8a47582eea 100644 --- a/library/ui-strings/src/main/res/values-ru/strings.xml +++ b/library/ui-strings/src/main/res/values-ru/strings.xml @@ -239,6 +239,7 @@ Настройки Принять + Открыть приложение Отклонить Завершить звонок OK @@ -2875,6 +2876,7 @@ Универсальное безопасное приложение для переписок с командами, друзьями и организациями. Создайте переписку или присоеденитесь к уже существующей, чтобы начать. Пространства — новый способ групировать комнаты и людей. Добавьте существующую комнату или создайте новую, используя кнопку слева снизу. Возможность записывать и отправлять голосовые трансляции в ленту комнаты. + Включить уведомление для групповых звонков Получите лучший надзор и контроль над всеми вашими сессиями. Заверенные сеансы есть везде, где вы используете эту учётную запись после ввода своей мнемонической фразы или подтверждения своей личности с помощью другого заверенного сеанса. \n diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 2a98069c2e3..faa8cf26f73 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -373,6 +373,7 @@ Done Accept + Open App Decline Hang Up @@ -3493,6 +3494,7 @@ Record the client name, version, and url to recognise sessions more easily in session manager. Enable voice broadcast Be able to record and send voice broadcast in room timeline. + Ring for group calls. %s\nis looking a little empty. diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index 2134c8cf2c5..2934b3eb32e 100644 --- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -88,6 +88,9 @@ class DebugVectorFeatures( override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled) ?: vectorFeatures.isVoiceBroadcastEnabled() + override fun isJitsiCallNotificationEnabled(): Boolean = read(DebugFeatureKeys.jitsiCallNotificationsEnabled) + ?: vectorFeatures.isJitsiCallNotificationEnabled() + override fun isUnverifiedSessionsAlertEnabled(): Boolean = read(DebugFeatureKeys.unverifiedSessionsAlertEnabled) ?: vectorFeatures.isUnverifiedSessionsAlertEnabled() @@ -155,4 +158,5 @@ object DebugFeatureKeys { val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login") val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled") val unverifiedSessionsAlertEnabled = booleanPreferencesKey("unverified-sessions-alert-enabled") + val jitsiCallNotificationsEnabled = booleanPreferencesKey("jitsi-call-notifications-enabled") } diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml index a8695eed44d..dbced6b6874 100755 --- a/vector-config/src/main/res/values/config-settings.xml +++ b/vector-config/src/main/res/values/config-settings.xml @@ -51,6 +51,7 @@ false true false + true diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index 47eb9081e01..d565e100490 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -136,12 +136,6 @@ class CallAndroidService : VectorAndroidService() { ACTION_JITSI_CALL_TERMINATED -> { handleJitsiCallTerminated(intent) } - ACTION_INCOMING_JITSI_RINGING_CALL -> { - mediaSession?.isActive = true - val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false) - callRingPlayerIncoming?.start(fromBg) - displayIncomingJitsiCallNotification(intent) - } else -> { handleUnexpectedState(null) } @@ -200,55 +194,6 @@ class CallAndroidService : VectorAndroidService() { knownCalls[callId] = callInformation } - /** - * Display a permanent notification when there is an incoming call. - * - */ - private fun displayIncomingJitsiCallNotification(intent: Intent) { - Timber.tag(loggerTag.value).v("displayIncomingJitsiCallNotification $intent") - val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty() - val signalingRoomId = intent.getStringExtra(EXTRA_SIGNALING_ROOM_ID).orEmpty() - val otherUserId = intent.getStringExtra(EXTRA_OTHER_USER_ID).orEmpty() - - val matrixItem = MatrixItem.RoomItem( - id = signalingRoomId, - displayName = otherUserId, - ) - - val isVideoCall = true - val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false) - Timber.tag(loggerTag.value).v("displayIncomingJitsiCallNotification : display the dedicated notification") - val incomingCallAlert = IncomingCallAlert(callId, - shouldBeDisplayedIn = { activity -> - if (activity is VectorCallActivity) { - activity.intent.getParcelableExtraCompat(Mavericks.KEY_ARG)?.callId != callId - } else true - } - ).apply { - viewBinder = IncomingCallAlert.ViewBinder( - matrixItem = matrixItem, - avatarRenderer = avatarRenderer, - isVideoCall = isVideoCall, - onAccept = { showJitsiCallScreen(callId, signalingRoomId) }, - onReject = { onJitsiCallTerminated(applicationContext, callId) } - ) - dismissedAction = Runnable { onJitsiCallTerminated(applicationContext, callId) } - contentAction = Runnable { showJitsiCallScreen(callId, signalingRoomId) } - } - alertManager.postVectorAlert(incomingCallAlert) - val notification = notificationUtils.buildIncomingJitsiCallNotification( - callId = callId, - signalingRoomId = signalingRoomId, - isIncomingCall = true, - isVideoCall = isVideoCall, - otherUserId = otherUserId, - title = otherUserId, - fromBg = fromBg, - ) - - startForegroundCompat(callId.hashCode(), notification) - } - private fun handleCallTerminated(intent: Intent) { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val endCallReason = intent.getSerializableExtraCompat(EXTRA_END_CALL_REASON) @@ -395,7 +340,6 @@ class CallAndroidService : VectorAndroidService() { private const val ACTION_INCOMING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_RINGING_CALL" private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_RINGING_CALL" - private const val ACTION_INCOMING_JITSI_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_JITSI_RINGING_CALL" private const val ACTION_ONGOING_CALL = "im.vector.app.core.services.CallService.ACTION_ONGOING_CALL" private const val ACTION_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_CALL_TERMINATED" private const val ACTION_JITSI_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_JITSI_CALL_TERMINATED" @@ -421,24 +365,6 @@ class CallAndroidService : VectorAndroidService() { ContextCompat.startForegroundService(context, intent) } - fun onIncomingJitsiCallRinging( - context: Context, - callId: String, - signalingRoomId: String, - otherUserId: String, - isInBackground: Boolean - ) { - val intent = Intent(context, CallAndroidService::class.java) - .apply { - action = ACTION_INCOMING_JITSI_RINGING_CALL - putExtra(EXTRA_CALL_ID, callId) - putExtra(EXTRA_SIGNALING_ROOM_ID, signalingRoomId) - putExtra(EXTRA_OTHER_USER_ID, otherUserId) - putExtra(EXTRA_IS_IN_BG, isInBackground) - } - ContextCompat.startForegroundService(context, intent) - } - fun onOutgoingCallRinging( context: Context, callId: String diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index 99abc15f81f..16b4cc718b8 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -44,6 +44,7 @@ interface VectorFeatures { fun isQrCodeLoginForAllServers(): Boolean fun isReciprocateQrCodeLogin(): Boolean fun isVoiceBroadcastEnabled(): Boolean + fun isJitsiCallNotificationEnabled(): Boolean fun isUnverifiedSessionsAlertEnabled(): Boolean } @@ -64,5 +65,6 @@ class DefaultVectorFeatures : VectorFeatures { override fun isQrCodeLoginForAllServers(): Boolean = false override fun isReciprocateQrCodeLogin(): Boolean = false override fun isVoiceBroadcastEnabled(): Boolean = true + override fun isJitsiCallNotificationEnabled(): Boolean = true override fun isUnverifiedSessionsAlertEnabled(): Boolean = true } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt index 47833f057a6..29851f6a625 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -37,9 +37,6 @@ class NotificationFactory @Inject constructor( notification = notificationUtils.buildIncomingJitsiCallNotification( callId = events.firstOrNull()?.event?.eventId.orEmpty().ifEmpty { roomId }, signalingRoomId = roomId, - otherUserId = events.firstOrNull()?.event?.matrixID.orEmpty(), - isIncomingCall = true, - isVideoCall = true, title = events.firstOrNull()?.event?.roomName.orEmpty(), fromBg = true, ) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt index df948cd8b60..0ab5e725edb 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -72,29 +72,19 @@ class NotificationRenderer @Inject constructor( } Timber.d("Jitsi call notifications count = ${jitsiNotifications.size}") - jitsiNotifications.forEach { wrapper -> - when (wrapper) { - is JitsiNotification.IncomingCall -> { - Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") - if (wrapper.eventId.isNotEmpty() || wrapper.roomId.isNotEmpty()) { - val tag = wrapper.eventId.ifEmpty { wrapper.roomId } - notificationDisplayer.showNotificationMessage(tag, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) + if (vectorPreferences.isJitsiCallNotificationEnabled()) { + jitsiNotifications.forEach { wrapper -> + when (wrapper) { + is JitsiNotification.IncomingCall -> { + Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") + if (wrapper.eventId.isNotEmpty() || wrapper.roomId.isNotEmpty()) { + val tag = wrapper.eventId.ifEmpty { wrapper.roomId } + notificationDisplayer.showNotificationMessage(tag, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) + } } } } } -// if (vectorPreferences.isJitsiCallNotificationEnabled()) { -// jitsiNotifications.forEach { wrapper -> -// when (wrapper) { -// is JitsiNotification.IncomingCall -> { -// Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") -// if (wrapper.eventId.isNotEmpty()) { -// notificationDisplayer.showNotificationMessage(wrapper.eventId, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) -// } -// } -// } -// } -// } invitationNotifications.forEach { wrapper -> when (wrapper) { 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 294cb79d884..160bd10124a 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 @@ -364,10 +364,11 @@ class NotificationUtils @Inject constructor( } /** - * Build an incoming call notification. + * Build an incoming jitsi call notification. * This notification starts the VectorHomeActivity which is in charge of centralizing the incoming call flow. * - * @param call information about the call + * @param callId id of the jitsi call + * @param signalingRoomId id of the room * @param title title of the notification * @param fromBg true if the app is in background when posting the notification * @return the call notification. @@ -375,9 +376,6 @@ class NotificationUtils @Inject constructor( fun buildIncomingJitsiCallNotification( callId: String, signalingRoomId: String, - otherUserId: String, - isIncomingCall: Boolean, - isVideoCall: Boolean, title: String, fromBg: Boolean, ): Notification { @@ -426,7 +424,7 @@ class NotificationUtils @Inject constructor( builder.addAction( NotificationCompat.Action( R.drawable.ic_call_answer, - getActionText(R.string.call_notification_answer, R.attr.colorPrimary), + getActionText(R.string.call_notification_open_app, R.attr.colorPrimary), answerCallPendingIntent ) ) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index ed6f993e51c..dd5f3d04f9c 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -75,6 +75,7 @@ class VectorPreferences @Inject constructor( const val SETTINGS_LABS_NEW_SESSION_MANAGER_KEY = "SETTINGS_LABS_NEW_SESSION_MANAGER_KEY" const val SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY = "SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY" const val SETTINGS_LABS_VOICE_BROADCAST_KEY = "SETTINGS_LABS_VOICE_BROADCAST_KEY" + const val SETTINGS_LABS_JITSI_CALL_NOTIFICATION_KEY = "SETTINGS_LABS_JITSI_CALL_NOTIFICATION_KEY" const val SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY" const val SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY" const val SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY" @@ -1251,6 +1252,11 @@ class VectorPreferences @Inject constructor( defaultPrefs.getBoolean(SETTINGS_LABS_VOICE_BROADCAST_KEY, getDefault(R.bool.settings_labs_enable_voice_broadcast_default)) } + fun isJitsiCallNotificationEnabled(): Boolean { + return vectorFeatures.isJitsiCallNotificationEnabled() && + defaultPrefs.getBoolean(SETTINGS_LABS_JITSI_CALL_NOTIFICATION_KEY, getDefault(R.bool.settings_labs_enable_jitsi_call_notifications_default)) + } + fun showIpAddressInSessionManagerScreens(): Boolean { return defaultPrefs.getBoolean(SETTINGS_SESSION_MANAGER_SHOW_IP_ADDRESS, getDefault(R.bool.settings_session_manager_show_ip_address)) } diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml index 15a255753a6..2153ad8bd09 100644 --- a/vector/src/main/res/xml/vector_settings_labs.xml +++ b/vector/src/main/res/xml/vector_settings_labs.xml @@ -124,4 +124,10 @@ android:title="@string/labs_enable_voice_broadcast_title" app:isPreferenceVisible="@bool/settings_labs_enable_voice_broadcast_visible" /> + + From e4a98e652f1f59ac237df15e03090699be08fcd0 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sun, 1 Sep 2024 22:38:04 +0700 Subject: [PATCH 06/15] #7761 Removed unnecessary code --- .../app/core/services/CallAndroidService.kt | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index d565e100490..b918ca32b23 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -133,9 +133,6 @@ class CallAndroidService : VectorAndroidService() { ACTION_CALL_TERMINATED -> { handleCallTerminated(intent) } - ACTION_JITSI_CALL_TERMINATED -> { - handleJitsiCallTerminated(intent) - } else -> { handleUnexpectedState(null) } @@ -238,17 +235,6 @@ class CallAndroidService : VectorAndroidService() { startActivity(intent) } - private fun showJitsiCallScreen(callId: String, roomId: String) { - val intent = MainActivity.jitsiCallIntent( - context = this, - roomId = roomId, - callId = callId, - ) - startActivity(intent) - stopSelf() - stopForegroundCompat() - } - private fun displayOutgoingRingingCallNotification(intent: Intent) { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val call = callManager.getCallById(callId) ?: return Unit.also { @@ -342,11 +328,8 @@ class CallAndroidService : VectorAndroidService() { private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_RINGING_CALL" private const val ACTION_ONGOING_CALL = "im.vector.app.core.services.CallService.ACTION_ONGOING_CALL" private const val ACTION_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_CALL_TERMINATED" - private const val ACTION_JITSI_CALL_TERMINATED = "im.vector.app.core.services.CallService.ACTION_JITSI_CALL_TERMINATED" private const val EXTRA_CALL_ID = "EXTRA_CALL_ID" - private const val EXTRA_SIGNALING_ROOM_ID = "EXTRA_SIGNALING_ROOM_ID" - private const val EXTRA_OTHER_USER_ID = "EXTRA_OTHER_USER_ID" private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG" private const val EXTRA_END_CALL_REJECTED = "EXTRA_END_CALL_REJECTED" private const val EXTRA_END_CALL_REASON = "EXTRA_END_CALL_REASON" @@ -404,18 +387,6 @@ class CallAndroidService : VectorAndroidService() { } context.startService(intent) } - - fun onJitsiCallTerminated( - context: Context, - callId: String, - ) { - val intent = Intent(context, CallAndroidService::class.java) - .apply { - action = ACTION_JITSI_CALL_TERMINATED - putExtra(EXTRA_CALL_ID, callId) - } - context.startService(intent) - } } inner class CallServiceBinder : Binder() { From 9743c72fad78b6485b40fda8d8e9ddb7bea4cd59 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Mon, 2 Sep 2024 21:26:56 +0700 Subject: [PATCH 07/15] #7761 Removed unnecessary code --- .../im/vector/app/core/services/CallAndroidService.kt | 10 ---------- .../main/java/im/vector/app/features/MainActivity.kt | 11 +---------- .../features/call/conference/VectorJitsiActivity.kt | 1 - 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index b918ca32b23..a4e3872e0f2 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -23,14 +23,12 @@ import android.os.Binder import android.support.v4.media.session.MediaSessionCompat import android.view.KeyEvent import androidx.core.app.NotificationManagerCompat -import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import androidx.media.session.MediaButtonReceiver import com.airbnb.mvrx.Mavericks import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.startForegroundCompat -import im.vector.app.features.MainActivity import im.vector.app.features.call.CallArgs import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.telecom.CallConnection @@ -218,14 +216,6 @@ class CallAndroidService : VectorAndroidService() { } } - private fun handleJitsiCallTerminated(intent: Intent) { - val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty() - alertManager.cancelAlert(callId) - mediaSession?.isActive = false - myStopSelf() - } - - private fun showCallScreen(call: WebRtcCall, mode: String) { val intent = VectorCallActivity.newIntent( context = this, diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index acef05b51cb..f408fe61d33 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -115,18 +115,9 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } // Shortcuts can't have intents with parcelables - fun shortcutIntent( - context: Context, - roomId: String, - intentFlags: Int? = null, - ): Intent { + fun shortcutIntent(context: Context, roomId: String): Intent { return Intent(context, MainActivity::class.java).apply { action = ACTION_ROOM_DETAILS_FROM_SHORTCUT - - intentFlags?.let { - flags = it - } - putExtra(EXTRA_ROOM_ID, roomId) } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 16c11e3a63b..ea25b42669a 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -39,7 +39,6 @@ import com.facebook.react.modules.core.PermissionListener import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.platform.VectorBaseActivity -import im.vector.app.core.services.CallAndroidService import im.vector.app.databinding.ActivityJitsiBinding import im.vector.lib.core.utils.compat.getParcelableExtraCompat import im.vector.lib.strings.CommonStrings From aa58edec98d72a05dc122526a6942f9dacaf44dc Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Mon, 2 Sep 2024 23:15:36 +0700 Subject: [PATCH 08/15] #7761 Added changelog file --- changelog.d/7761.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7761.feature diff --git a/changelog.d/7761.feature b/changelog.d/7761.feature new file mode 100644 index 00000000000..fb33ab1491f --- /dev/null +++ b/changelog.d/7761.feature @@ -0,0 +1 @@ +Added ring for group calls \ No newline at end of file From 259087ad4cfdfa3cac9756a3e81325a4e7aa382e Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Tue, 10 Sep 2024 01:22:44 +0700 Subject: [PATCH 09/15] Updated strings_sc, fixed resources --- .../ui-strings/src/main/res/values-ru/strings_sc.xml | 2 ++ library/ui-strings/src/main/res/values/strings_sc.xml | 3 +++ .../app/features/notifications/NotificationUtils.kt | 10 +++++----- .../vector/app/features/settings/VectorPreferences.kt | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/library/ui-strings/src/main/res/values-ru/strings_sc.xml b/library/ui-strings/src/main/res/values-ru/strings_sc.xml index d14411c18f5..c47f766f5d6 100644 --- a/library/ui-strings/src/main/res/values-ru/strings_sc.xml +++ b/library/ui-strings/src/main/res/values-ru/strings_sc.xml @@ -180,4 +180,6 @@ Очистить выделение при прокрутке Разрешить резервный сервер звонков ⚠️ Эта настройка по умолчанию (если не изменена конфигурацией Вашего домашнего сервера) включает доступ к \"Scalar\", менеджеру интеграций от Element. К сожалению, он является проприетарным, т.е. его исходый код не открытый и не может быть проверен пользователями или разработчиками SchildiChat. + Открыть приложение + Включить уведомление для групповых звонков \ No newline at end of file diff --git a/library/ui-strings/src/main/res/values/strings_sc.xml b/library/ui-strings/src/main/res/values/strings_sc.xml index 148d267d8cf..2bf4ef3c371 100644 --- a/library/ui-strings/src/main/res/values/strings_sc.xml +++ b/library/ui-strings/src/main/res/values/strings_sc.xml @@ -237,6 +237,9 @@ Allow fallback call assist server Will use %s as assist when your homeserver does not offer one (your IP address will be seen by the stun server during a call + + Open App + Ring for group calls ) ⚠️ This setting by default (unless overridden by your homeserver\'s configuration) enables access to \"scalar\", Element\'s integration manager which is unfortunately proprietary, i.e. its source code is not open and can not be checked by the public or the SchildiChat developers. 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 84e5ce308f5..41d92166066 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 @@ -382,12 +382,12 @@ class NotificationUtils @Inject constructor( title: String, fromBg: Boolean, ): Notification { - val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color) + val accentColor = ContextCompat.getColor(context, im.vector.lib.ui.styles.R.color.notification_accent_color) val notificationChannel = if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID val builder = NotificationCompat.Builder(context, notificationChannel) .setContentTitle(ensureTitleNotEmpty(title)) .apply { - setContentText(stringProvider.getString(R.string.incoming_video_call)) + setContentText(stringProvider.getString(CommonStrings.incoming_video_call)) setSmallIcon(R.drawable.ic_call_answer_video) } .setCategory(NotificationCompat.CATEGORY_CALL) @@ -418,8 +418,8 @@ class NotificationUtils @Inject constructor( builder.addAction( NotificationCompat.Action( IconCompat.createWithResource(context, R.drawable.ic_call_hangup) - .setTint(ThemeUtils.getColor(context, R.attr.colorError)), - getActionText(R.string.call_notification_reject, R.attr.colorError), + .setTint(ThemeUtils.getColor(context, android.R.attr.colorError)), + getActionText(CommonStrings.call_notification_reject, android.R.attr.colorError), rejectCallPendingIntent ) ) @@ -427,7 +427,7 @@ class NotificationUtils @Inject constructor( builder.addAction( NotificationCompat.Action( R.drawable.ic_call_answer, - getActionText(R.string.call_notification_open_app, R.attr.colorPrimary), + getActionText(CommonStrings.call_notification_open_app, android.R.attr.colorPrimary), answerCallPendingIntent ) ) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 347650676dc..5149bedd02f 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -1600,7 +1600,7 @@ class VectorPreferences @Inject constructor( fun isJitsiCallNotificationEnabled(): Boolean { return vectorFeatures.isJitsiCallNotificationEnabled() && - defaultPrefs.getBoolean(SETTINGS_LABS_JITSI_CALL_NOTIFICATION_KEY, getDefault(R.bool.settings_labs_enable_jitsi_call_notifications_default)) + defaultPrefs.getBoolean(SETTINGS_LABS_JITSI_CALL_NOTIFICATION_KEY, getDefault(im.vector.app.config.R.bool.settings_labs_enable_jitsi_call_notifications_default)) } fun showIpAddressInSessionManagerScreens(): Boolean { From aa380933d1043390d0d0a7f970aa43909f84ae76 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Wed, 11 Sep 2024 00:06:45 +0700 Subject: [PATCH 10/15] Fixed cancel notification --- .../app/features/call/service/CallHeadsUpActionReceiver.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt index e6a47068eb9..ef28c258a53 100644 --- a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt @@ -22,6 +22,7 @@ import android.content.Intent import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.services.CallAndroidService import im.vector.app.features.call.webrtc.WebRtcCallManager +import im.vector.app.features.notifications.NotificationDrawerManager import timber.log.Timber class CallHeadsUpActionReceiver : BroadcastReceiver() { @@ -38,7 +39,7 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() { when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) { CALL_ACTION_REJECT -> { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return - notificationUtils.cancelAllNotifications() + notificationUtils.cancelNotificationMessage(callId, NotificationDrawerManager.JITSI_CALL_NOTIFICATION_ID) onCallRejectClicked(webRtcCallManager, callId) } } From 00668f80d7477de9efa2b3e3bf2de77cc0b329e5 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Wed, 11 Sep 2024 09:29:23 +0700 Subject: [PATCH 11/15] Removed redundant string resources --- library/ui-strings/src/main/res/values-ru/strings.xml | 2 -- library/ui-strings/src/main/res/values/strings.xml | 2 -- library/ui-strings/src/main/res/values/strings_sc.xml | 5 ++--- .../vector/app/features/notifications/NotificationUtils.kt | 2 +- vector/src/main/res/xml/vector_settings_labs.xml | 2 +- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml index a9ea0314cbf..68c2f958ea8 100644 --- a/library/ui-strings/src/main/res/values-ru/strings.xml +++ b/library/ui-strings/src/main/res/values-ru/strings.xml @@ -239,7 +239,6 @@ Настройки Принять - Открыть приложение Отклонить Завершить звонок OK @@ -2876,7 +2875,6 @@ Универсальное безопасное приложение для переписок с командами, друзьями и организациями. Создайте переписку или присоеденитесь к уже существующей, чтобы начать. Пространства — новый способ групировать комнаты и людей. Добавьте существующую комнату или создайте новую, используя кнопку слева снизу. Возможность записывать и отправлять голосовые трансляции в ленту комнаты. - Включить уведомление для групповых звонков Получите лучший надзор и контроль над всеми вашими сессиями. Заверенные сеансы есть везде, где вы используете эту учётную запись после ввода своей мнемонической фразы или подтверждения своей личности с помощью другого заверенного сеанса. \n diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index f13846b33d7..0f341c155d9 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -373,7 +373,6 @@ Done Accept - Open App Decline Hang Up @@ -3494,7 +3493,6 @@ Record the client name, version, and url to recognise sessions more easily in session manager. Enable voice broadcast Be able to record and send voice broadcast in room timeline. - Ring for group calls. %s\nis looking a little empty. diff --git a/library/ui-strings/src/main/res/values/strings_sc.xml b/library/ui-strings/src/main/res/values/strings_sc.xml index 2bf4ef3c371..9adfff72804 100644 --- a/library/ui-strings/src/main/res/values/strings_sc.xml +++ b/library/ui-strings/src/main/res/values/strings_sc.xml @@ -237,11 +237,10 @@ Allow fallback call assist server Will use %s as assist when your homeserver does not offer one (your IP address will be seen by the stun server during a call - - Open App - Ring for group calls ) + Ring for group calls + Open App ⚠️ This setting by default (unless overridden by your homeserver\'s configuration) enables access to \"scalar\", Element\'s integration manager which is unfortunately proprietary, i.e. its source code is not open and can not be checked by the public or the SchildiChat developers. 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 41d92166066..564ee90b69c 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 @@ -427,7 +427,7 @@ class NotificationUtils @Inject constructor( builder.addAction( NotificationCompat.Action( R.drawable.ic_call_answer, - getActionText(CommonStrings.call_notification_open_app, android.R.attr.colorPrimary), + getActionText(CommonStrings.call_notification_open_app_action, android.R.attr.colorPrimary), answerCallPendingIntent ) ) diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml index f7a496fd2bf..752a41aa376 100644 --- a/vector/src/main/res/xml/vector_settings_labs.xml +++ b/vector/src/main/res/xml/vector_settings_labs.xml @@ -205,7 +205,7 @@ From be9fa3b97d95de9e62ba301980b2e91fcfcf2720 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Wed, 25 Sep 2024 08:22:53 +0700 Subject: [PATCH 12/15] Fixed code style --- vector/src/main/java/im/vector/app/features/MainActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index f408fe61d33..e785f6b5ef1 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -239,8 +239,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut) } finish() - } - else { + } else { args = parseArgs() if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { clearNotifications() From 9fa60139c2b741884e29f139d306aebbfe80f4d7 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Mon, 30 Sep 2024 01:29:28 +0700 Subject: [PATCH 13/15] Removed russian translation. Fixed gradle. Deleted unnecessary file --- changelog.d/7761.feature | 1 - library/external/textdrawable/build.gradle | 2 -- library/ui-strings/src/main/res/values-ru/strings_sc.xml | 4 +--- 3 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 changelog.d/7761.feature diff --git a/changelog.d/7761.feature b/changelog.d/7761.feature deleted file mode 100644 index fb33ab1491f..00000000000 --- a/changelog.d/7761.feature +++ /dev/null @@ -1 +0,0 @@ -Added ring for group calls \ No newline at end of file diff --git a/library/external/textdrawable/build.gradle b/library/external/textdrawable/build.gradle index 5eb27bf6aa3..dcaf2d76ccd 100644 --- a/library/external/textdrawable/build.gradle +++ b/library/external/textdrawable/build.gradle @@ -1,7 +1,5 @@ apply plugin: 'com.android.library' -apply plugin: 'com.android.library' - android { namespace "com.amulyakhare.textdrawable" diff --git a/library/ui-strings/src/main/res/values-ru/strings_sc.xml b/library/ui-strings/src/main/res/values-ru/strings_sc.xml index c47f766f5d6..4e510848b74 100644 --- a/library/ui-strings/src/main/res/values-ru/strings_sc.xml +++ b/library/ui-strings/src/main/res/values-ru/strings_sc.xml @@ -180,6 +180,4 @@ Очистить выделение при прокрутке Разрешить резервный сервер звонков ⚠️ Эта настройка по умолчанию (если не изменена конфигурацией Вашего домашнего сервера) включает доступ к \"Scalar\", менеджеру интеграций от Element. К сожалению, он является проприетарным, т.е. его исходый код не открытый и не может быть проверен пользователями или разработчиками SchildiChat. - Открыть приложение - Включить уведомление для групповых звонков - \ No newline at end of file + From b63af0936839a867d43909d8aa67e741384a2ea4 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Sun, 6 Oct 2024 04:38:19 +0700 Subject: [PATCH 14/15] Disabled settings_labs_enable_jitsi_call_notifications_default by default. Changed detection for group calls more accurate. Added tracking visibility status for jitsi notifications --- .../sdk/api/session/events/model/Event.kt | 4 ++- .../sdk/api/session/events/model/EventType.kt | 3 ++ .../session/room/model/JitsiEventContent.kt | 29 +++++++++++++++++++ .../src/main/res/values/config-settings.xml | 2 +- .../notifications/NotifiableEventProcessor.kt | 17 +++++++++-- .../notifications/NotifiableEventResolver.kt | 3 +- .../notifications/NotifiableJitsiEvent.kt | 9 ++++++ .../notifications/NotificationFactory.kt | 14 ++++++--- .../notifications/NotificationRenderer.kt | 7 ++--- 9 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/JitsiEventContent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index c691f7839be..2b575608f6c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -23,7 +23,9 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult +import org.matrix.android.sdk.api.session.events.model.EventType.IS_JITSI_CALL import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent +import org.matrix.android.sdk.api.session.room.model.JitsiEventContent import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent @@ -498,7 +500,7 @@ fun Event.getPollContent(): MessagePollContent? { return getClearContent().toModel() } -fun Event.isJitsiEvent() = this.getClearType() == EventType.STATE_ROOM_WIDGET_LEGACY +fun Event.isJitsiEvent() = content?.toModel()?.type == IS_JITSI_CALL && content.toModel()?.name == IS_JITSI_CALL fun Event.supportsNotification() = this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 170254078f7..94e336679ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -108,6 +108,9 @@ object EventType { // Relation Events const val REACTION = "m.reaction" + // Jitsi call + const val IS_JITSI_CALL = "jitsi" + // Poll val POLL_START = StableUnstableId(stable = "m.poll.start", unstable = "org.matrix.msc3381.poll.start") val POLL_RESPONSE = StableUnstableId(stable = "m.poll.response", unstable = "org.matrix.msc3381.poll.response") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/JitsiEventContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/JitsiEventContent.kt new file mode 100644 index 00000000000..956c343ac96 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/JitsiEventContent.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.api.session.room.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing the Jitsi call state event. + */ +@JsonClass(generateAdapter = true) +data class JitsiEventContent( + @Json(name = "type") val type: String? = null, + @Json(name = "name") val name: String? = null, +) diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml index 90e24e71ffa..6d83f32a047 100755 --- a/vector-config/src/main/res/values/config-settings.xml +++ b/vector-config/src/main/res/values/config-settings.xml @@ -51,7 +51,7 @@ false true false - true + false diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt index 6e91ecef087..b63ec903a20 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -41,18 +41,29 @@ class NotifiableEventProcessor @Inject constructor( .also { Timber.d("notification message removed due to being read") } else -> KEEP } - is NotifiableJitsiEvent -> KEEP + is NotifiableJitsiEvent -> { + if (it.isReceived != true) { + KEEP + } else { + REMOVE + } + } is SimpleNotifiableEvent -> when (it.type) { EventType.REDACTION -> REMOVE else -> KEEP } } - ProcessedEvent(type, it) + + val updatedEvent = if (it is NotifiableJitsiEvent) it.updateReceivedStatus() else it + ProcessedEvent(type, updatedEvent) } val removedEventsDiff = renderedEvents.filter { renderedEvent -> queuedEvents.none { it.eventId == renderedEvent.event.eventId } - }.map { ProcessedEvent(REMOVE, it.event) } + }.map { + val updatedEvent = if (it.event is NotifiableJitsiEvent) it.event.updateReceivedStatus() else it.event + ProcessedEvent(REMOVE, updatedEvent) + } return removedEventsDiff + processedEvents } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index cf63b946f74..e93411bc233 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -174,7 +174,8 @@ class NotifiableEventResolver @Inject constructor( ContentUrlResolver.ThumbnailMethod.SCALE ), matrixID = session.myUserId, - soundName = null + soundName = null, + isReceived = null, ) } else { null diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt index 90cf6091055..ca43d31f319 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableJitsiEvent.kt @@ -37,6 +37,7 @@ data class NotifiableJitsiEvent( // This is used for >N notification, as the result of a smart reply val outGoingMessage: Boolean = false, val outGoingMessageFailed: Boolean = false, + var isReceived: Boolean? = null, override val isRedacted: Boolean = false, override val isUpdated: Boolean = false ) : NotifiableEvent { @@ -44,4 +45,12 @@ data class NotifiableJitsiEvent( val type: String = EventType.MESSAGE val description: String = body ?: "" val title: String = senderName ?: "" + + fun updateReceivedStatus() = this.copy( + isReceived = when (isReceived) { + null -> false + false -> true + true -> true + } + ) } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt index 29851f6a625..5bef05bf75d 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -30,14 +30,20 @@ class NotificationFactory @Inject constructor( fun Map.toNotifications(): List { return map { (roomId, events) -> + if (events.all { it.event.isReceived == true }) { + return emptyList() + } + + val eventToShow = events.first { it.event.isReceived == false } + JitsiNotification.IncomingCall( roomId = roomId, - eventId = events.firstOrNull()?.event?.eventId.orEmpty(), - roomName = events.firstOrNull()?.event?.roomName.orEmpty(), + eventId = eventToShow.event.eventId, + roomName = eventToShow.event.roomName.orEmpty(), notification = notificationUtils.buildIncomingJitsiCallNotification( - callId = events.firstOrNull()?.event?.eventId.orEmpty().ifEmpty { roomId }, + callId = eventToShow.event.eventId.ifEmpty { roomId }, signalingRoomId = roomId, - title = events.firstOrNull()?.event?.roomName.orEmpty(), + title = eventToShow.event.roomName.orEmpty(), fromBg = true, ) ) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt index 0ab5e725edb..098d173413f 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -76,7 +76,7 @@ class NotificationRenderer @Inject constructor( jitsiNotifications.forEach { wrapper -> when (wrapper) { is JitsiNotification.IncomingCall -> { - Timber.d("Updating jitsi call notification ${wrapper.roomId} for room ${wrapper.roomName}") + Timber.d("Updating jitsi call notification ${wrapper.eventId} for room ${wrapper.roomName}") if (wrapper.eventId.isNotEmpty() || wrapper.roomId.isNotEmpty()) { val tag = wrapper.eventId.ifEmpty { wrapper.roomId } notificationDisplayer.showNotificationMessage(tag, JITSI_CALL_NOTIFICATION_ID, wrapper.notification) @@ -135,10 +135,7 @@ private fun List>.groupByType(): GroupedNotifica } is NotifiableJitsiEvent -> { val jitsiEvents = roomIdToJitsiEventMap.getOrPut(event.roomId) { ArrayList() } - val diffInMillis = System.currentTimeMillis() - (it.event as NotifiableJitsiEvent).timestamp - if (diffInMillis < 10000) { - jitsiEvents.add(it.castedToEventType()) - } + jitsiEvents.add(it.castedToEventType()) } is SimpleNotifiableEvent -> simpleEvents.add(it.castedToEventType()) } From b00181feec647e921f859242a9ea01c2a4a633a4 Mon Sep 17 00:00:00 2001 From: "roman.turubayev@econophysica.com" Date: Mon, 7 Oct 2024 21:38:38 +0700 Subject: [PATCH 15/15] Added formatting name and type to lowercase --- .../org/matrix/android/sdk/api/session/events/model/Event.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 2b575608f6c..053af048151 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -500,7 +500,8 @@ fun Event.getPollContent(): MessagePollContent? { return getClearContent().toModel() } -fun Event.isJitsiEvent() = content?.toModel()?.type == IS_JITSI_CALL && content.toModel()?.name == IS_JITSI_CALL +fun Event.isJitsiEvent() = content?.toModel()?.type?.lowercase() == IS_JITSI_CALL && + content.toModel()?.name?.lowercase() == IS_JITSI_CALL fun Event.supportsNotification() = this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values