Missed call: start showing notification when there is a missed call

This commit is contained in:
ganfra 2021-07-19 20:25:09 +02:00
parent 765f7d634e
commit f7e2e741c0
5 changed files with 80 additions and 27 deletions

View File

@ -46,7 +46,8 @@ import timber.log.Timber
class CallService : VectorService() {
private val connections = mutableMapOf<String, CallConnection>()
private val knownCalls = mutableSetOf<String>()
private val knownCalls = mutableSetOf<CallInformation>()
private val ongoingCallIds = mutableSetOf<String>()
private lateinit var notificationManager: NotificationManagerCompat
private lateinit var notificationUtils: NotificationUtils
@ -115,19 +116,19 @@ class CallService : VectorService() {
callRingPlayerOutgoing?.start()
displayOutgoingRingingCallNotification(intent)
}
ACTION_ONGOING_CALL -> {
ACTION_ONGOING_CALL -> {
callRingPlayerIncoming?.stop()
callRingPlayerOutgoing?.stop()
displayCallInProgressNotification(intent)
}
ACTION_CALL_CONNECTING -> {
ACTION_CALL_CONNECTING -> {
// lower notification priority
displayCallInProgressNotification(intent)
// stop ringing
callRingPlayerIncoming?.stop()
callRingPlayerOutgoing?.stop()
}
ACTION_CALL_TERMINATED -> {
ACTION_CALL_TERMINATED -> {
handleCallTerminated(intent)
}
else -> {
@ -153,9 +154,9 @@ class CallService : VectorService() {
val call = callManager.getCallById(callId) ?: return Unit.also {
handleUnexpectedState(callId)
}
val callInformation = call.toCallInformation()
val isVideoCall = call.mxCall.isVideoCall
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
val opponentMatrixItem = getOpponentMatrixItem(call)
Timber.v("displayIncomingCallNotification : display the dedicated notification")
val incomingCallAlert = IncomingCallAlert(callId,
shouldBeDisplayedIn = { activity ->
@ -165,7 +166,7 @@ class CallService : VectorService() {
}
).apply {
viewBinder = IncomingCallAlert.ViewBinder(
matrixItem = opponentMatrixItem,
matrixItem = callInformation.matrixItem,
avatarRenderer = avatarRenderer,
isVideoCall = isVideoCall,
onAccept = { showCallScreen(call, VectorCallActivity.INCOMING_ACCEPT) },
@ -177,7 +178,7 @@ class CallService : VectorService() {
alertManager.postVectorAlert(incomingCallAlert)
val notification = notificationUtils.buildIncomingCallNotification(
call = call,
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
title = callInformation.matrixItem?.getBestName() ?: callInformation.opponentUserId,
fromBg = fromBg
)
if (knownCalls.isEmpty()) {
@ -185,19 +186,33 @@ class CallService : VectorService() {
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callId)
knownCalls.add(callInformation)
}
private fun handleCallTerminated(intent: Intent) {
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
val isRejected = intent.getBooleanExtra(EXTRA_IS_REJECTED, false)
alertManager.cancelAlert(callId)
if (!knownCalls.remove(callId)) {
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
if (terminatedCall == null) {
Timber.v("Call terminated for unknown call $callId$")
handleUnexpectedState(callId)
return
}
val notification = notificationUtils.buildCallEndedNotification()
notificationManager.notify(callId.hashCode(), notification)
knownCalls.remove(terminatedCall)
val wasOngoing = ongoingCallIds.remove(callId)
if (wasOngoing || isRejected) {
val notification = notificationUtils.buildCallEndedNotification()
notificationManager.notify(callId.hashCode(), notification)
} else {
val notification = notificationUtils.buildCallMissedNotification(
roomId = terminatedCall.nativeRoomId,
caller = terminatedCall.matrixItem?.getBestName() ?: terminatedCall.opponentUserId
)
notificationManager.cancel(callId.hashCode())
notificationManager.notify(MISSED_CALL_TAG, callId.hashCode(), notification)
}
if (knownCalls.isEmpty()) {
mediaSession?.isActive = false
myStopSelf()
@ -218,18 +233,18 @@ class CallService : VectorService() {
val call = callManager.getCallById(callId) ?: return Unit.also {
handleUnexpectedState(callId)
}
val opponentMatrixItem = getOpponentMatrixItem(call)
val callInformation = call.toCallInformation()
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
val notification = notificationUtils.buildOutgoingRingingCallNotification(
call = call,
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
title = callInformation.matrixItem?.getBestName() ?: callInformation.opponentUserId
)
if (knownCalls.isEmpty()) {
startForeground(callId.hashCode(), notification)
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callId)
knownCalls.add(callInformation)
}
/**
@ -238,21 +253,22 @@ class CallService : VectorService() {
private fun displayCallInProgressNotification(intent: Intent) {
Timber.v("## VOIP displayCallInProgressNotification")
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
ongoingCallIds.add(callId)
val call = callManager.getCallById(callId) ?: return Unit.also {
handleUnexpectedState(callId)
}
val opponentMatrixItem = getOpponentMatrixItem(call)
alertManager.cancelAlert(callId)
val callInformation = call.toCallInformation()
val notification = notificationUtils.buildPendingCallNotification(
call = call,
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
title = callInformation.matrixItem?.getBestName() ?: callInformation.opponentUserId
)
if (knownCalls.isEmpty()) {
startForeground(callId.hashCode(), notification)
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callId)
knownCalls.add(callInformation)
}
private fun handleUnexpectedState(callId: String?) {
@ -274,14 +290,27 @@ class CallService : VectorService() {
connections[callConnection.callId] = callConnection
}
private fun getOpponentMatrixItem(call: WebRtcCall): MatrixItem? {
return vectorComponent().activeSessionHolder().getSafeActiveSession()?.let {
call.getOpponentAsMatrixItem(it)
}
private fun WebRtcCall.toCallInformation(): CallInformation{
return CallInformation(
callId = this.callId,
nativeRoomId = this.nativeRoomId,
opponentUserId = this.mxCall.opponentUserId,
matrixItem = vectorComponent().activeSessionHolder().getSafeActiveSession()?.let {
this.getOpponentAsMatrixItem(it)
}
)
}
private data class CallInformation(
val callId: String,
val nativeRoomId: String,
val opponentUserId: String,
val matrixItem: MatrixItem?
)
companion object {
private const val DEFAULT_NOTIFICATION_ID = 6480
private const val MISSED_CALL_TAG = "MISSED_CALL_TAG"
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"
@ -294,6 +323,7 @@ class CallService : VectorService() {
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG"
private const val EXTRA_IS_REJECTED = "EXTRA_IS_REJECTED"
fun onIncomingCallRinging(context: Context,
callId: String,
@ -329,11 +359,12 @@ class CallService : VectorService() {
ContextCompat.startForegroundService(context, intent)
}
fun onCallTerminated(context: Context, callId: String) {
fun onCallTerminated(context: Context, callId: String, isRejected: Boolean) {
val intent = Intent(context, CallService::class.java)
.apply {
action = ACTION_CALL_TERMINATED
putExtra(EXTRA_CALL_ID, callId)
putExtra(EXTRA_IS_REJECTED, isRejected)
}
ContextCompat.startForegroundService(context, intent)
}

View File

@ -99,7 +99,7 @@ class WebRtcCall(
private val sessionProvider: Provider<Session?>,
private val peerConnectionFactoryProvider: Provider<PeerConnectionFactory?>,
private val onCallBecomeActive: (WebRtcCall) -> Unit,
private val onCallEnded: (String) -> Unit
private val onCallEnded: (String, Boolean) -> Unit
) : MxCall.StateListener {
interface Listener : MxCall.StateListener {
@ -810,7 +810,8 @@ class WebRtcCall(
val wasRinging = mxCall.state is CallState.LocalRinging
mxCall.state = CallState.Terminated
release()
onCallEnded(callId)
val isRejected = wasRinging && sendEndSignaling
onCallEnded(callId, isRejected)
if (sendEndSignaling) {
if (wasRinging) {
mxCall.reject()

View File

@ -232,12 +232,12 @@ class WebRtcCallManager @Inject constructor(
this.currentCall.setAndNotify(call)
}
private fun onCallEnded(callId: String) {
private fun onCallEnded(callId: String, isRejected: Boolean) {
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: $callId")
val webRtcCall = callsByCallId.remove(callId) ?: return Unit.also {
Timber.v("On call ended for unknown call $callId")
}
CallService.onCallTerminated(context, callId)
CallService.onCallTerminated(context, callId, isRejected)
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
transferees.remove(callId)
@ -420,7 +420,7 @@ class WebRtcCallManager @Inject constructor(
override fun onCallManagedByOtherSession(callId: String) {
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
onCallEnded(callId)
onCallEnded(callId, false)
}
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {

View File

@ -459,6 +459,26 @@ class NotificationUtils @Inject constructor(private val context: Context,
.build()
}
/**
* Build notification for the CallService, when a call is missed
*/
fun buildCallMissedNotification(roomId: String, caller: String): Notification {
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
.setContentTitle(caller)
.setContentText(stringProvider.getString(R.string.call_missed, caller))
.setSmallIcon(R.drawable.ic_material_call_end_grey)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_CALL)
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId)))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(contentPendingIntent)
return builder.build()
}
fun buildDownloadFileNotification(uri: Uri, fileName: String, mimeType: String): Notification {
return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
.setGroup(stringProvider.getString(R.string.app_name))

View File

@ -727,6 +727,7 @@
<string name="call_connected">Call connected</string>
<string name="call_connecting">Call connecting…</string>
<string name="call_ended">Call ended</string>
<string name="call_missed">You missed a call from %s</string>
<string name="call_ring">Calling…</string>
<string name="incoming_call">Incoming Call</string>
<string name="incoming_video_call">Incoming Video Call</string>