Skip to content

Commit 7983501

Browse files
authored
Merge pull request #6015 from anakin78z/answering-calls
Improve answering calls
2 parents 5cbb5be + 9106d40 commit 7983501

6 files changed

Lines changed: 101 additions & 16 deletions

File tree

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* hidrohase <github@honesz.net>
3131
* Jan-Christoph Borchardt <hey@jancborchardt.net>
3232
* jld3103 <jld3103yt@gmail.com>
33+
* Jens Zalzala <jens@shakingearthdigital.com>
3334
* Joas Schilling <coding@schilljs.com>
3435
* John Molakvoæ <skjnldsv@protonmail.com>
3536
* Jos Poortvliet <jos@opensuse.org>

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@
293293

294294
<receiver android:name=".receivers.DirectReplyReceiver" />
295295
<receiver android:name=".receivers.MarkAsReadReceiver" />
296+
<receiver android:name=".receivers.DeclineCallReceiver" android:exported="false" />
296297
<receiver android:name=".receivers.DismissRecordingAvailableReceiver" />
297298
<receiver android:name=".receivers.ShareRecordingToChatReceiver" />
298299

app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import android.view.OrientationEventListener
4141
import android.view.View
4242
import android.view.View.OnTouchListener
4343
import androidx.activity.result.contract.ActivityResultContracts
44+
import androidx.core.app.NotificationManagerCompat
4445
import androidx.annotation.DrawableRes
4546
import androidx.appcompat.app.AlertDialog
4647
import androidx.compose.material3.MaterialTheme
@@ -110,7 +111,6 @@ import com.nextcloud.talk.utils.ApiUtils
110111
import com.nextcloud.talk.utils.CapabilitiesUtil
111112
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
112113
import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
113-
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom
114114
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
115115
import com.nextcloud.talk.utils.ReceiverFlag
116116
import com.nextcloud.talk.utils.SpreedFeatures
@@ -121,6 +121,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION
121121
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
122122
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD
123123
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL
124+
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
124125
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_BREAKOUT_ROOM
125126
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_MODERATOR
126127
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MODIFIED_BASE_URL
@@ -377,7 +378,7 @@ class CallActivity : CallBaseActivity() {
377378

378379
private var isFrontCamera by mutableStateOf(true)
379380

380-
@SuppressLint("ClickableViewAccessibility")
381+
@SuppressLint("ClickableViewAccessibility", "Detekt.LongMethod")
381382
override fun onCreate(savedInstanceState: Bundle?) {
382383
Log.d(TAG, "onCreate")
383384
super.onCreate(savedInstanceState)
@@ -573,6 +574,11 @@ class CallActivity : CallBaseActivity() {
573574

574575
if (extras.containsKey(KEY_FROM_NOTIFICATION_START_CALL)) {
575576
isIncomingCallFromNotification = extras.getBoolean(KEY_FROM_NOTIFICATION_START_CALL)
577+
val notificationId = extras.getInt(KEY_NOTIFICATION_TIMESTAMP, 0)
578+
if (notificationId != 0) {
579+
// cancel the notification to stop the call ringing
580+
NotificationManagerCompat.from(this).cancel(notificationId)
581+
}
576582
}
577583
if (extras.containsKey(KEY_IS_BREAKOUT_ROOM)) {
578584
isBreakoutRoom = extras.getBoolean(KEY_IS_BREAKOUT_ROOM)
@@ -1051,7 +1057,10 @@ class CallActivity : CallBaseActivity() {
10511057
binding!!.selfVideoViewWrapper.visibility = View.GONE
10521058
} else if (permissionUtil!!.isCameraPermissionGranted()) {
10531059
binding!!.selfVideoViewWrapper.visibility = View.VISIBLE
1054-
onCameraClick()
1060+
// don't enable the camera if call was answered via notification
1061+
if (!isIncomingCallFromNotification) {
1062+
onCameraClick()
1063+
}
10551064
if (cameraEnumerator!!.deviceNames.isEmpty()) {
10561065
binding!!.cameraButton.visibility = View.GONE
10571066
}
@@ -1608,13 +1617,6 @@ class CallActivity : CallBaseActivity() {
16081617
}
16091618
ApplicationWideCurrentRoomHolder.getInstance().isInCall = true
16101619
ApplicationWideCurrentRoomHolder.getInstance().isDialing = false
1611-
if (!TextUtils.isEmpty(roomToken)) {
1612-
cancelExistingNotificationsForRoom(
1613-
applicationContext,
1614-
conversationUser!!,
1615-
roomToken!!
1616-
)
1617-
}
16181620
if (!hasExternalSignalingServer) {
16191621
pullSignalingMessages()
16201622
}
@@ -1835,6 +1837,7 @@ class CallActivity : CallBaseActivity() {
18351837
fetchSignalingSettings()
18361838
}
18371839

1840+
@Suppress("Detekt.NestedBlockDepth")
18381841
@Subscribe(threadMode = ThreadMode.BACKGROUND)
18391842
fun onMessageEvent(webSocketCommunicationEvent: WebSocketCommunicationEvent) {
18401843
if (currentCallStatus === CallStatus.LEAVING) {

app/src/main/java/com/nextcloud/talk/activities/CallBaseActivity.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import android.annotation.SuppressLint;
1010
import android.app.AppOpsManager;
11-
import android.app.KeyguardManager;
1211
import android.app.PictureInPictureParams;
1312
import android.content.Context;
1413
import android.content.pm.PackageManager;
@@ -76,10 +75,7 @@ void dismissKeyguard() {
7675
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
7776
setShowWhenLocked(true);
7877
setTurnScreenOn(true);
79-
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
80-
keyguardManager.requestDismissKeyguard(this, null);
8178
} else {
82-
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
8379
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
8480
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
8581
}

app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import coil.request.ImageRequest
4545
import com.bluelinelabs.logansquare.LoganSquare
4646
import com.nextcloud.talk.BuildConfig
4747
import com.nextcloud.talk.R
48+
import com.nextcloud.talk.activities.CallActivity
4849
import com.nextcloud.talk.activities.MainActivity
4950
import com.nextcloud.talk.api.NcApi
5051
import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -61,6 +62,7 @@ import com.nextcloud.talk.models.json.participants.Participant
6162
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
6263
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
6364
import com.nextcloud.talk.models.json.push.NotificationUser
65+
import com.nextcloud.talk.receivers.DeclineCallReceiver
6466
import com.nextcloud.talk.receivers.DirectReplyReceiver
6567
import com.nextcloud.talk.receivers.DismissRecordingAvailableReceiver
6668
import com.nextcloud.talk.receivers.MarkAsReadReceiver
@@ -95,7 +97,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_THREAD_ID
9597
import com.nextcloud.talk.utils.preferences.AppPreferences
9698
import io.reactivex.Observable
9799
import io.reactivex.Observer
98-
import io.reactivex.android.schedulers.AndroidSchedulers
99100
import io.reactivex.disposables.Disposable
100101
import io.reactivex.schedulers.Schedulers
101102
import okhttp3.JavaNetCookieJar
@@ -268,11 +269,65 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
268269
}
269270
)
270271

272+
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
273+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
274+
} else {
275+
PendingIntent.FLAG_UPDATE_CURRENT
276+
}
277+
278+
val answerVoiceBundle = Bundle(bundle).apply { putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true) }
279+
val answerVoicePendingIntent = PendingIntent.getActivity(
280+
applicationContext,
281+
requestCode + ANSWER_VOICE_REQUEST_OFFSET,
282+
Intent(applicationContext, CallActivity::class.java).apply {
283+
putExtras(answerVoiceBundle)
284+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
285+
},
286+
pendingIntentFlags
287+
)
288+
289+
val answerVideoBundle = Bundle(bundle).apply { putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false) }
290+
val answerVideoPendingIntent = PendingIntent.getActivity(
291+
applicationContext,
292+
requestCode + ANSWER_VIDEO_REQUEST_OFFSET,
293+
Intent(applicationContext, CallActivity::class.java).apply {
294+
putExtras(answerVideoBundle)
295+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
296+
},
297+
pendingIntentFlags
298+
)
299+
300+
val declinePendingIntent = PendingIntent.getBroadcast(
301+
applicationContext,
302+
requestCode + DECLINE_CALL_REQUEST_OFFSET,
303+
Intent(applicationContext, DeclineCallReceiver::class.java).apply {
304+
putExtra(KEY_NOTIFICATION_TIMESTAMP, pushMessage.timestamp.toInt())
305+
},
306+
pendingIntentFlags
307+
)
308+
271309
val soundUri = getCallRingtoneUri(applicationContext, appPreferences)
272310
val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name
273311
val uri = signatureVerification.user!!.baseUrl!!.toUri()
274312
val baseUrl = uri.host
275313

314+
val callerPersonBuilder = Person.Builder()
315+
.setName(conversation.displayName)
316+
.setImportant(true)
317+
if (conversation.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
318+
val avatarUrl = ApiUtils.getUrlForAvatar(
319+
signatureVerification.user!!.baseUrl!!,
320+
conversation.name,
321+
false,
322+
darkMode = DisplayUtils.isDarkModeOn(applicationContext)
323+
)
324+
loadAvatarSync(avatarUrl, applicationContext)?.let { callerPersonBuilder.setIcon(it) }
325+
}
326+
val callerPerson = callerPersonBuilder.build()
327+
328+
val isVideoCall = (conversation.callFlag and Participant.InCallFlags.WITH_VIDEO) > 0
329+
val primaryAnswerIntent = if (isVideoCall) answerVideoPendingIntent else answerVoicePendingIntent
330+
276331
val notification =
277332
NotificationCompat.Builder(applicationContext, notificationChannelId)
278333
.setPriority(NotificationCompat.PRIORITY_HIGH)
@@ -289,6 +344,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
289344
.setContentIntent(fullScreenPendingIntent)
290345
.setFullScreenIntent(fullScreenPendingIntent, true)
291346
.setSound(soundUri)
347+
.setStyle(
348+
NotificationCompat.CallStyle
349+
.forIncomingCall(callerPerson, declinePendingIntent, primaryAnswerIntent)
350+
.setIsVideo(isVideoCall)
351+
)
292352
.build()
293353
notification.flags = notification.flags or Notification.FLAG_INSISTENT
294354

@@ -299,7 +359,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
299359

300360
chatNetworkDataSource?.getRoom(userBeingCalled, roomToken = pushMessage.id!!)
301361
?.subscribeOn(Schedulers.io())
302-
?.observeOn(AndroidSchedulers.mainThread())
362+
?.observeOn(Schedulers.io())
303363
?.subscribe(object : Observer<ConversationModel> {
304364
override fun onSubscribe(d: Disposable) {
305365
// unused atm
@@ -1107,5 +1167,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
11071167
private const val TIMER_COUNT = 12
11081168
private const val TIMER_DELAY: Long = 5
11091169
private const val LINEBREAK: String = "\n"
1170+
private const val ANSWER_VOICE_REQUEST_OFFSET = 1
1171+
private const val ANSWER_VIDEO_REQUEST_OFFSET = 2
1172+
private const val DECLINE_CALL_REQUEST_OFFSET = 3
11101173
}
11111174
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: GPL-3.0-or-later
6+
*/
7+
package com.nextcloud.talk.receivers
8+
9+
import android.content.BroadcastReceiver
10+
import android.content.Context
11+
import android.content.Intent
12+
import androidx.core.app.NotificationManagerCompat
13+
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
14+
15+
class DeclineCallReceiver : BroadcastReceiver() {
16+
17+
override fun onReceive(context: Context, intent: Intent) {
18+
val notificationId = intent.getIntExtra(KEY_NOTIFICATION_TIMESTAMP, 0)
19+
NotificationManagerCompat.from(context).cancel(notificationId)
20+
}
21+
}

0 commit comments

Comments
 (0)