Skip to content

Commit 3c21cb8

Browse files
authored
fix(voip): prevent duplicate ringtone on Android incoming call (#7158)
1 parent 9c84d4c commit 3c21cb8

2 files changed

Lines changed: 4 additions & 31 deletions

File tree

android/app/src/main/java/chat/rocket/reactnative/voip/IncomingCallActivity.kt

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import android.content.Context
77
import android.content.Intent
88
import android.content.IntentFilter
99
import android.graphics.drawable.GradientDrawable
10-
import android.media.Ringtone
11-
import android.media.RingtoneManager
1210
import android.os.Build
1311
import android.os.Bundle
1412
import android.os.Handler
@@ -31,14 +29,16 @@ import chat.rocket.reactnative.notification.Ejson
3129
/**
3230
* Full-screen Activity displayed when an incoming VoIP call arrives.
3331
* Shows on lock screen and handles user actions (Accept/Decline).
32+
*
33+
* Ring audio is owned by [VoipNotification]'s NotificationChannel — do not
34+
* play a ringtone here; a second source would double-ring on most Android versions.
3435
*/
3536
class IncomingCallActivity : Activity() {
3637

3738
companion object {
3839
private const val TAG = "RocketChat.IncomingCall"
3940
}
4041

41-
private var ringtone: Ringtone? = null
4242
private var voipPayload: VoipPayload? = null
4343
private var isCallStateReceiverRegistered = false
4444
private val timeoutHandler = Handler(Looper.getMainLooper())
@@ -51,7 +51,6 @@ class IncomingCallActivity : Activity() {
5151
}
5252

5353
clearTimeout()
54-
stopRingtone()
5554
finish()
5655
}
5756
}
@@ -97,7 +96,6 @@ class IncomingCallActivity : Activity() {
9796
Log.d(TAG, "IncomingCallActivity created - callId: ${voipPayload.callId}, caller: ${voipPayload.caller}")
9897

9998
updateUI(voipPayload)
100-
startRingtone()
10199
setupButtons(voipPayload)
102100
scheduleTimeout(voipPayload)
103101
val intentFilter = IntentFilter().apply {
@@ -225,27 +223,6 @@ class IncomingCallActivity : Activity() {
225223
}
226224
}
227225

228-
private fun startRingtone() {
229-
try {
230-
val ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
231-
ringtone = RingtoneManager.getRingtone(applicationContext, ringtoneUri)
232-
ringtone?.play()
233-
Log.d(TAG, "Ringtone started")
234-
} catch (e: Exception) {
235-
Log.e(TAG, "Failed to start ringtone", e)
236-
}
237-
}
238-
239-
private fun stopRingtone() {
240-
try {
241-
ringtone?.stop()
242-
ringtone = null
243-
Log.d(TAG, "Ringtone stopped")
244-
} catch (e: Exception) {
245-
Log.e(TAG, "Failed to stop ringtone", e)
246-
}
247-
}
248-
249226
private fun setupButtons(payload: VoipPayload) {
250227
findViewById<LinearLayout>(R.id.btn_accept)?.setOnClickListener {
251228
handleAccept(payload)
@@ -259,14 +236,12 @@ class IncomingCallActivity : Activity() {
259236
private fun scheduleTimeout(payload: VoipPayload) {
260237
val remainingLifetimeMs = payload.getRemainingLifetimeMs()
261238
if (remainingLifetimeMs == null || remainingLifetimeMs <= 0L) {
262-
stopRingtone()
263239
finish()
264240
return
265241
}
266242

267243
clearTimeout()
268244
timeoutRunnable = Runnable {
269-
stopRingtone()
270245
VoipNotification.handleTimeout(this, payload)
271246
finish()
272247
}.also { timeoutHandler.postDelayed(it, remainingLifetimeMs) }
@@ -281,7 +256,6 @@ class IncomingCallActivity : Activity() {
281256
Log.d(TAG, "Call accepted - callId: ${payload.callId}")
282257
clearTimeout()
283258
VoipNotification.cancelTimeout(payload.callId)
284-
stopRingtone()
285259
VoipNotification.handleAcceptAction(this, payload)
286260
// Activity finishes when ACTION_DISMISS is broadcast from handleAcceptAction (async DDP).
287261
}
@@ -290,7 +264,6 @@ class IncomingCallActivity : Activity() {
290264
Log.d(TAG, "Call declined - callId: ${payload.callId}")
291265
clearTimeout()
292266
VoipNotification.cancelTimeout(payload.callId)
293-
stopRingtone()
294267
VoipNotification.handleDeclineAction(this, payload)
295268

296269
finish()
@@ -303,7 +276,6 @@ class IncomingCallActivity : Activity() {
303276
LocalBroadcastManager.getInstance(this).unregisterReceiver(callStateReceiver)
304277
isCallStateReceiverRegistered = false
305278
}
306-
stopRingtone()
307279
}
308280

309281
override fun onBackPressed() {

android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ class VoipNotification(private val context: Context) {
802802
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
803803
setAutoCancel(false)
804804
setOngoing(true)
805+
setOnlyAlertOnce(true)
805806
setTimeoutAfter(remainingLifetimeMs)
806807
addAction(0, "Decline", declinePendingIntent)
807808
addAction(0, "Accept", acceptPendingIntent)

0 commit comments

Comments
 (0)