From 9b9eadb6b7f071a5db134473657989b2ca6c8ca5 Mon Sep 17 00:00:00 2001
From: pfurovYnP <165936357+pfurovYnP@users.noreply.github.com>
Date: Thu, 12 Mar 2026 15:00:55 +0300
Subject: [PATCH] Improve incoming call notifications with native call style
---
app/src/main/AndroidManifest.xml | 1 +
.../CallNotificationActivity.kt | 10 +++
.../nextcloud/talk/jobs/NotificationWorker.kt | 81 +++++++++++++++----
.../receivers/IncomingCallDeclineReceiver.kt | 23 ++++++
4 files changed, 98 insertions(+), 17 deletions(-)
create mode 100644 app/src/main/java/com/nextcloud/talk/receivers/IncomingCallDeclineReceiver.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 29ebb5715ca..46e1033c5bd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -285,6 +285,7 @@
+
0
override fun onStop() {
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
index b0a667ee552..98e1bc0a635 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
@@ -59,6 +59,7 @@ import com.nextcloud.talk.models.json.push.DecryptedPushMessage
import com.nextcloud.talk.models.json.push.NotificationUser
import com.nextcloud.talk.receivers.DirectReplyReceiver
import com.nextcloud.talk.receivers.DismissRecordingAvailableReceiver
+import com.nextcloud.talk.receivers.IncomingCallDeclineReceiver
import com.nextcloud.talk.receivers.MarkAsReadReceiver
import com.nextcloud.talk.receivers.ShareRecordingToChatReceiver
import com.nextcloud.talk.users.UserManager
@@ -76,6 +77,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_DISMISS_RECORDING_URL
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
+import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MESSAGE_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RESTRICT_DELETION
@@ -261,28 +263,73 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
}
)
+ val answerIntent = Intent(context, CallNotificationActivity::class.java).apply {
+ putExtras(bundle)
+ putExtra(KEY_CALL_VOICE_ONLY, !isInCallWithVideo(conversation.callFlag))
+ flags = getIntentFlags()
+ }
+ val answerPendingIntent = PendingIntent.getActivity(
+ context,
+ requestCode + 1,
+ answerIntent,
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+ )
+
+ val declineIntent = Intent(context, IncomingCallDeclineReceiver::class.java).apply {
+ putExtra(KEY_NOTIFICATION_TIMESTAMP, pushMessage.timestamp.toInt())
+ }
+ val declinePendingIntent = PendingIntent.getBroadcast(
+ context,
+ requestCode + 2,
+ declineIntent,
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+ )
+
val soundUri = getCallRingtoneUri(applicationContext, appPreferences)
val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name
val uri = signatureVerification.user!!.baseUrl!!.toUri()
val baseUrl = uri.host
- val notification =
- NotificationCompat.Builder(applicationContext, notificationChannelId)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setCategory(NotificationCompat.CATEGORY_CALL)
- .setSmallIcon(R.drawable.ic_call_black_24dp)
- .setSubText(baseUrl)
- .setShowWhen(true)
- .setWhen(pushMessage.timestamp)
- .setContentTitle(EmojiCompat.get().process(pushMessage.subject))
- // auto cancel is set to false because notification (including sound) should continue while
- // CallNotificationActivity is active
- .setAutoCancel(false)
- .setOngoing(true)
- .setContentIntent(fullScreenPendingIntent)
- .setFullScreenIntent(fullScreenPendingIntent, true)
- .setSound(soundUri)
- .build()
+ val caller = Person.Builder()
+ .setName(EmojiCompat.get().process(pushMessage.subject))
+ .setImportant(true)
+ .build()
+
+ val builder = NotificationCompat.Builder(applicationContext, notificationChannelId)
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .setCategory(NotificationCompat.CATEGORY_CALL)
+ .setSmallIcon(R.drawable.ic_call_black_24dp)
+ .setSubText(baseUrl)
+ .setShowWhen(true)
+ .setWhen(pushMessage.timestamp)
+ .setContentTitle(EmojiCompat.get().process(pushMessage.subject))
+ // auto cancel is set to false because notification (including sound) should continue while
+ // CallNotificationActivity is active
+ .setAutoCancel(false)
+ .setOngoing(true)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setContentIntent(fullScreenPendingIntent)
+ .setFullScreenIntent(fullScreenPendingIntent, true)
+ .setSound(soundUri)
+ .setColorized(true)
+
+ builder.setStyle(
+ NotificationCompat.CallStyle.forIncomingCall(
+ caller,
+ declinePendingIntent,
+ answerPendingIntent
+ )
+ )
+
+ val notification = builder.build()
notification.flags = notification.flags or Notification.FLAG_INSISTENT
sendNotification(pushMessage.timestamp.toInt(), notification)
diff --git a/app/src/main/java/com/nextcloud/talk/receivers/IncomingCallDeclineReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/IncomingCallDeclineReceiver.kt
new file mode 100644
index 00000000000..02130294508
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/receivers/IncomingCallDeclineReceiver.kt
@@ -0,0 +1,23 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Nextcloud contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+package com.nextcloud.talk.receivers
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.NotificationManagerCompat
+import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
+
+class IncomingCallDeclineReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val notificationId = intent.getIntExtra(KEY_NOTIFICATION_TIMESTAMP, -1)
+ if (notificationId == -1) {
+ return
+ }
+ NotificationManagerCompat.from(context).cancel(notificationId)
+ }
+}