Skip to content

Commit 89194e8

Browse files
authored
Merge pull request #6085 from nextcloud/backport/6079/Improve-answering-calls
Backport/6079/improve answering calls
2 parents 221fc11 + a952d48 commit 89194e8

6 files changed

Lines changed: 118 additions & 27 deletions

File tree

app/src/main/AndroidManifest.xml

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

284284
<receiver android:name=".receivers.DirectReplyReceiver" />
285285
<receiver android:name=".receivers.MarkAsReadReceiver" />
286+
<receiver android:name=".receivers.DeclineCallReceiver" android:exported="false" />
286287
<receiver android:name=".receivers.DismissRecordingAvailableReceiver" />
287288
<receiver android:name=".receivers.ShareRecordingToChatReceiver" />
288289

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

Lines changed: 11 additions & 9 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
@@ -107,7 +108,6 @@ import com.nextcloud.talk.utils.ApiUtils
107108
import com.nextcloud.talk.utils.CapabilitiesUtil
108109
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
109110
import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
110-
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom
111111
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
112112
import com.nextcloud.talk.utils.ReceiverFlag
113113
import com.nextcloud.talk.utils.SpreedFeatures
@@ -118,6 +118,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION
118118
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
119119
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD
120120
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL
121+
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
121122
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_BREAKOUT_ROOM
122123
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_MODERATOR
123124
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MODIFIED_BASE_URL
@@ -553,6 +554,11 @@ class CallActivity : CallBaseActivity() {
553554

554555
if (extras.containsKey(KEY_FROM_NOTIFICATION_START_CALL)) {
555556
isIncomingCallFromNotification = extras.getBoolean(KEY_FROM_NOTIFICATION_START_CALL)
557+
val notificationId = extras.getInt(KEY_NOTIFICATION_TIMESTAMP, 0)
558+
if (notificationId != 0) {
559+
// cancel the notification to stop the call ringing
560+
NotificationManagerCompat.from(this).cancel(notificationId)
561+
}
556562
}
557563
if (extras.containsKey(KEY_IS_BREAKOUT_ROOM)) {
558564
isBreakoutRoom = extras.getBoolean(KEY_IS_BREAKOUT_ROOM)
@@ -1031,7 +1037,10 @@ class CallActivity : CallBaseActivity() {
10311037
binding!!.selfVideoViewWrapper.visibility = View.GONE
10321038
} else if (permissionUtil!!.isCameraPermissionGranted()) {
10331039
binding!!.selfVideoViewWrapper.visibility = View.VISIBLE
1034-
onCameraClick()
1040+
// don't enable the camera if call was answered via notification
1041+
if (!isIncomingCallFromNotification) {
1042+
onCameraClick()
1043+
}
10351044
if (cameraEnumerator!!.deviceNames.isEmpty()) {
10361045
binding!!.cameraButton.visibility = View.GONE
10371046
}
@@ -1582,13 +1591,6 @@ class CallActivity : CallBaseActivity() {
15821591
}
15831592
ApplicationWideCurrentRoomHolder.getInstance().isInCall = true
15841593
ApplicationWideCurrentRoomHolder.getInstance().isDialing = false
1585-
if (!TextUtils.isEmpty(roomToken)) {
1586-
cancelExistingNotificationsForRoom(
1587-
applicationContext,
1588-
conversationUser!!,
1589-
roomToken!!
1590-
)
1591-
}
15921594
if (!hasExternalSignalingServer) {
15931595
pullSignalingMessages()
15941596
}

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: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import autodagger.AutoInjector
4141
import com.bluelinelabs.logansquare.LoganSquare
4242
import com.nextcloud.talk.BuildConfig
4343
import com.nextcloud.talk.R
44+
import com.nextcloud.talk.activities.CallActivity
4445
import com.nextcloud.talk.activities.MainActivity
4546
import com.nextcloud.talk.api.NcApi
4647
import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -57,6 +58,7 @@ import com.nextcloud.talk.models.json.participants.Participant
5758
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
5859
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
5960
import com.nextcloud.talk.models.json.push.NotificationUser
61+
import com.nextcloud.talk.receivers.DeclineCallReceiver
6062
import com.nextcloud.talk.receivers.DirectReplyReceiver
6163
import com.nextcloud.talk.receivers.DismissRecordingAvailableReceiver
6264
import com.nextcloud.talk.receivers.MarkAsReadReceiver
@@ -90,7 +92,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPENED_VIA_NOTIFICATION
9092
import com.nextcloud.talk.utils.preferences.AppPreferences
9193
import io.reactivex.Observable
9294
import io.reactivex.Observer
93-
import io.reactivex.android.schedulers.AndroidSchedulers
9495
import io.reactivex.disposables.Disposable
9596
import io.reactivex.schedulers.Schedulers
9697
import okhttp3.JavaNetCookieJar
@@ -261,11 +262,64 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
261262
}
262263
)
263264

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

307+
val callerPersonBuilder = Person.Builder()
308+
.setName(conversation.displayName)
309+
.setImportant(true)
310+
if (conversation.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
311+
val avatarUrl = ApiUtils.getUrlForAvatar(
312+
signatureVerification.user!!.baseUrl!!,
313+
conversation.name,
314+
false
315+
)
316+
loadAvatarSync(avatarUrl, applicationContext)?.let { callerPersonBuilder.setIcon(it) }
317+
}
318+
val callerPerson = callerPersonBuilder.build()
319+
320+
val isVideoCall = (conversation.callFlag and Participant.InCallFlags.WITH_VIDEO) > 0
321+
val primaryAnswerIntent = if (isVideoCall) answerVideoPendingIntent else answerVoicePendingIntent
322+
269323
val notification =
270324
NotificationCompat.Builder(applicationContext, notificationChannelId)
271325
.setPriority(NotificationCompat.PRIORITY_HIGH)
@@ -282,6 +336,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
282336
.setContentIntent(fullScreenPendingIntent)
283337
.setFullScreenIntent(fullScreenPendingIntent, true)
284338
.setSound(soundUri)
339+
.setStyle(
340+
NotificationCompat.CallStyle
341+
.forIncomingCall(callerPerson, declinePendingIntent, primaryAnswerIntent)
342+
.setIsVideo(isVideoCall)
343+
)
285344
.build()
286345
notification.flags = notification.flags or Notification.FLAG_INSISTENT
287346

@@ -292,7 +351,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
292351

293352
chatNetworkDataSource?.getRoom(userBeingCalled, roomToken = pushMessage.id!!)
294353
?.subscribeOn(Schedulers.io())
295-
?.observeOn(AndroidSchedulers.mainThread())
354+
?.observeOn(Schedulers.io())
296355
?.subscribe(object : Observer<ConversationModel> {
297356
override fun onSubscribe(d: Disposable) {
298357
// unused atm
@@ -1037,5 +1096,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
10371096
private const val TIMER_COUNT = 12
10381097
private const val TIMER_DELAY: Long = 5
10391098
private const val LINEBREAK: String = "\n"
1099+
private const val ANSWER_VOICE_REQUEST_OFFSET = 1
1100+
private const val ANSWER_VIDEO_REQUEST_OFFSET = 2
1101+
private const val DECLINE_CALL_REQUEST_OFFSET = 3
10401102
}
10411103
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
*
5+
* SPDX-FileCopyrightText: 2026 Jens Zalzala <jens@shakingearthdigital.com>
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: GPL-3.0-or-later
8+
*
9+
*/
10+
package com.nextcloud.talk.receivers
11+
12+
import android.content.BroadcastReceiver
13+
import android.content.Context
14+
import android.content.Intent
15+
import androidx.core.app.NotificationManagerCompat
16+
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
17+
18+
class DeclineCallReceiver : BroadcastReceiver() {
19+
20+
override fun onReceive(context: Context, intent: Intent) {
21+
val notificationId = intent.getIntExtra(KEY_NOTIFICATION_TIMESTAMP, 0)
22+
NotificationManagerCompat.from(context).cancel(notificationId)
23+
}
24+
}

gradle/verification-metadata.xml

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@
13471347
</component>
13481348
<component group="androidx.compose" name="compose-bom" version="2026.02.00">
13491349
<artifact name="compose-bom-2026.02.00.pom">
1350-
<sha256 value="f6f44c9c81f0c040a39fe92fa686b2046f5948087387cefcbce9f0524220512a" origin="Generated by Gradle"/>
1350+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="f6f44c9c81f0c040a39fe92fa686b2046f5948087387cefcbce9f0524220512a"/>
13511351
</artifact>
13521352
</component>
13531353
<component group="androidx.compose.animation" name="animation" version="1.10.0">
@@ -1502,7 +1502,7 @@
15021502
</component>
15031503
<component group="androidx.compose.animation" name="animation-core" version="1.2.1">
15041504
<artifact name="animation-core-1.2.1.module">
1505-
<sha256 value="9083ac53ad162f4167ecfb895469943504f4cd6a624e612936a5209144c5b6bf" origin="Generated by Gradle"/>
1505+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="9083ac53ad162f4167ecfb895469943504f4cd6a624e612936a5209144c5b6bf"/>
15061506
</artifact>
15071507
</component>
15081508
<component group="androidx.compose.animation" name="animation-core" version="1.7.7">
@@ -2644,7 +2644,7 @@
26442644
<sha256 value="1943daa4a3412861b9a2bdc1a7c8c2ff05d9b8191c1d3e56ebb223d2eb4a8526" origin="Generated by Gradle"/>
26452645
</artifact>
26462646
<artifact name="ui-1.0.1.module">
2647-
<sha256 value="57031a6ac9b60e5b56792ebf5cde6e16812ff566ed9190cbd188b00b46c13779" origin="Generated by Gradle"/>
2647+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="57031a6ac9b60e5b56792ebf5cde6e16812ff566ed9190cbd188b00b46c13779"/>
26482648
</artifact>
26492649
</component>
26502650
<component group="androidx.compose.ui" name="ui" version="1.10.0">
@@ -16722,7 +16722,9 @@
1672216722
<sha256 value="6c8989f2945d50ab38b3e0300064f1f8d2d75bbcae1434fe535d9fb6898e9ad6" origin="Generated by Gradle" reason="Artifact is not signed"/>
1672316723
</artifact>
1672416724
<artifact name="PhotoView-2.3.0.pom">
16725-
<sha256 value="5b99aa67a3d5422c6bd909a0bd9abebdb742157490399a6680aefe17384f0c68" origin="Generated by Gradle" reason="Artifact is not signed"/>
16725+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="5b99aa67a3d5422c6bd909a0bd9abebdb742157490399a6680aefe17384f0c68">
16726+
<also-trust value="7ec99dc514655aa11761da29a9af5d758e5c561d6ab14085f0d1ed8ea1c3102d"/>
16727+
</sha256>
1672616728
</artifact>
1672716729
</component>
1672816730
<component group="com.github.ddB0515.FlexibleAdapter" name="flexible-adapter" version="5.1.1">
@@ -16831,15 +16833,19 @@
1683116833
<sha256 value="e35716ca794a1678f5a5d5c57967fbf2e5e6d1b5abd9b08c5fbef590f95aa171" origin="Generated by Gradle" reason="Artifact is not signed"/>
1683216834
</artifact>
1683316835
<artifact name="ChatKit-0.4.2.module">
16834-
<sha256 value="7a0c1c77c2cbacaf733e49244049afb9b21385da53fd3f18771b2ec86bd6f2e9" origin="Generated by Gradle" reason="Artifact is not signed"/>
16836+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="7a0c1c77c2cbacaf733e49244049afb9b21385da53fd3f18771b2ec86bd6f2e9">
16837+
<also-trust value="20dcd1b7c0163369e7065646ccd33f530494b6a8ee8e3ed9106f5e2df81f2093"/>
16838+
</sha256>
1683516839
</artifact>
1683616840
</component>
1683716841
<component group="com.github.nextcloud-deps" name="ImagePicker" version="2.1.0.2">
1683816842
<artifact name="ImagePicker-2.1.0.2.aar">
1683916843
<sha256 value="d440ffb62d1d8e42706b30c330774f2d129f78fa56c75679c7aa154f2c8e8a7d" origin="Generated by Gradle" reason="Artifact is not signed"/>
1684016844
</artifact>
1684116845
<artifact name="ImagePicker-2.1.0.2.module">
16842-
<sha256 value="5369620cc8054a2e600bf7e539c40e1ca920e59cf43fd35d8a557005f0290388" origin="Generated by Gradle" reason="Artifact is not signed"/>
16846+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="5369620cc8054a2e600bf7e539c40e1ca920e59cf43fd35d8a557005f0290388">
16847+
<also-trust value="74d17ee6bc50900ace04f16844189c575ea27678522cc89ab351aa8022f4afbe"/>
16848+
</sha256>
1684316849
</artifact>
1684416850
</component>
1684516851
<component group="com.github.nextcloud-deps" name="android-talk-webrtc" version="110.5481.0">
@@ -17060,10 +17066,10 @@
1706017066
</component>
1706117067
<component group="com.github.nextcloud.android-common" name="core" version="0.33.0">
1706217068
<artifact name="core-0.33.0.aar">
17063-
<sha256 value="5667a7a23055bcff6d88904e5d8e5a8300dd59476a835446b0cb169249b3bf75" origin="Generated by Gradle"/>
17069+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="5667a7a23055bcff6d88904e5d8e5a8300dd59476a835446b0cb169249b3bf75"/>
1706417070
</artifact>
1706517071
<artifact name="core-0.33.0.module">
17066-
<sha256 value="c2340d4f6b51d241067bb1c14f5a16884e5fce28d4d92fcba3bf5aa6c496ba48" origin="Generated by Gradle"/>
17072+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="c2340d4f6b51d241067bb1c14f5a16884e5fce28d4d92fcba3bf5aa6c496ba48"/>
1706717073
</artifact>
1706817074
</component>
1706917075
<component group="com.github.nextcloud.android-common" name="material-color-utilities" version="0.18.0">
@@ -17192,10 +17198,10 @@
1719217198
</component>
1719317199
<component group="com.github.nextcloud.android-common" name="material-color-utilities" version="0.33.0">
1719417200
<artifact name="material-color-utilities-0.33.0.jar">
17195-
<sha256 value="61568a8c8f0466aea4fec621653ca8d2c08c9c687ffd6e364f909a2d19185992" origin="Generated by Gradle"/>
17201+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="61568a8c8f0466aea4fec621653ca8d2c08c9c687ffd6e364f909a2d19185992"/>
1719617202
</artifact>
1719717203
<artifact name="material-color-utilities-0.33.0.module">
17198-
<sha256 value="8bf80f41da67e7da9bea847ff3f06a595f5fecec88aa8fd3ac13dc48619a8f71" origin="Generated by Gradle"/>
17204+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="8bf80f41da67e7da9bea847ff3f06a595f5fecec88aa8fd3ac13dc48619a8f71"/>
1719917205
</artifact>
1720017206
</component>
1720117207
<component group="com.github.nextcloud.android-common" name="ui" version="0.18.0">
@@ -17320,10 +17326,10 @@
1732017326
</component>
1732117327
<component group="com.github.nextcloud.android-common" name="ui" version="0.33.0">
1732217328
<artifact name="ui-0.33.0.aar">
17323-
<sha256 value="906d22fe924e01b992b456c9fa2022f83941e11a3e957b32f31352eb318d3ea9" origin="Generated by Gradle"/>
17329+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="906d22fe924e01b992b456c9fa2022f83941e11a3e957b32f31352eb318d3ea9"/>
1732417330
</artifact>
1732517331
<artifact name="ui-0.33.0.module">
17326-
<sha256 value="d24b70b167941dec25fb3d00cd89ea51753c289597b6c31ac2941517f52af80f" origin="Generated by Gradle"/>
17332+
<sha256 origin="Generated by Gradle" reason="Artifact is not signed" value="d24b70b167941dec25fb3d00cd89ea51753c289597b6c31ac2941517f52af80f"/>
1732717333
</artifact>
1732817334
</component>
1732917335
<component group="com.github.spotbugs" name="spotbugs" version="4.8.6">

0 commit comments

Comments
 (0)