Incoming notification + ringing

This commit is contained in:
Valere 2020-06-17 15:03:51 +02:00
parent d8cf44fdc9
commit 843da1d48d
13 changed files with 607 additions and 179 deletions

View file

@ -38,6 +38,7 @@ import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.send.RoomEventSender
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
@ -100,8 +101,8 @@ internal class DefaultCallSignalingService @Inject constructor(
override fun removeCallListener(listener: CallsListener) {
callListeners.remove(listener)
}
override fun getCallWithId(callId: String): MxCall? {
Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}")
return activeCalls.find { it.callId == callId }
}

View file

@ -83,6 +83,7 @@ internal class MxCallImpl(
override fun offerSdp(sdp: SessionDescription) {
if (!isOutgoing) return
Timber.v("## VOIP offerSdp $callId")
state = CallState.DIALING
CallInviteContent(
callId = callId,
@ -113,6 +114,7 @@ internal class MxCallImpl(
}
override fun hangUp() {
Timber.v("## VOIP hangup $callId")
CallHangupContent(
callId = callId
)
@ -122,6 +124,7 @@ internal class MxCallImpl(
}
override fun accept(sdp: SessionDescription) {
Timber.v("## VOIP accept $callId")
if (isOutgoing) return
state = CallState.ANSWERING
CallAnswerContent(

View file

@ -365,5 +365,6 @@
<string name="call_notification_answer">Accept</string>
<string name="call_notification_reject">Decline</string>
<string name="call_notification_hangup">Hang Up</string>
</resources>

View file

@ -19,6 +19,7 @@
<!-- READ_PHONE_STATE is needed only if your calling app reads numbers from the `PHONE_STATE`
intent action. -->
<!-- Needed to show incoming calls activity in lock screen-->
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Needed for incoming calls -->

View file

@ -17,9 +17,14 @@
package im.vector.riotx.core.extensions
import androidx.fragment.app.FragmentTransaction
import im.vector.matrix.android.api.extensions.tryThis
inline fun androidx.fragment.app.FragmentManager.commitTransactionNow(func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commitNow()
// Could throw and make the app crash
// e.g sharedActionViewModel.observe()
tryThis("Failed to commitTransactionNow") {
beginTransaction().func().commitNow()
}
}
inline fun androidx.fragment.app.FragmentManager.commitTransaction(func: FragmentTransaction.() -> FragmentTransaction) {

View file

@ -165,6 +165,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
}
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("onCreate Activity ${this.javaClass.simpleName}")
val vectorComponent = getVectorComponent()
screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this)
val timeForInjection = measureTimeMillis {
@ -252,6 +253,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onDestroy() {
super.onDestroy()
Timber.i("onDestroy Activity ${this.javaClass.simpleName}")
unBinder?.unbind()
unBinder = null
@ -279,6 +281,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onPause() {
super.onPause()
Timber.i("onPause Activity ${this.javaClass.simpleName}")
rageShake.stop()

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.core.services
import android.content.Context
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Build
import im.vector.riotx.R
import timber.log.Timber
class CallRingPlayer(
context: Context
) {
private val applicationContext = context.applicationContext
private var player: MediaPlayer? = null
fun start() {
val audioManager: AudioManager = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
player?.release()
player = createPlayer()
// Check if sound is enabled
val ringerMode = audioManager.ringerMode
if (player != null && ringerMode == AudioManager.RINGER_MODE_NORMAL) {
try {
if (player?.isPlaying == false) {
player?.start()
Timber.v("## VOIP Starting ringing")
} else {
Timber.v("## VOIP already playing")
}
} catch (failure: Throwable) {
Timber.e(failure, "## VOIP Failed to start ringing")
player = null
}
} else {
Timber.v("## VOIP Can't play $player ode $ringerMode")
}
}
fun stop() {
player?.release()
player = null
}
private fun createPlayer(): MediaPlayer? {
try {
val mediaPlayer = MediaPlayer.create(applicationContext, R.raw.ring)
mediaPlayer.setOnErrorListener(MediaPlayerErrorListener())
mediaPlayer.isLooping = true
if (Build.VERSION.SDK_INT <= 21) {
@Suppress("DEPRECATION")
mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING)
} else {
mediaPlayer.setAudioAttributes(AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
.build())
}
return mediaPlayer
} catch (failure: Throwable) {
Timber.e(failure, "Failed to create Call ring player")
return null
}
}
inner class MediaPlayerErrorListener : MediaPlayer.OnErrorListener {
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
Timber.w("onError($mp, $what, $extra")
player = null
return false
}
}
}

View file

@ -1,6 +1,6 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ package im.vector.riotx.core.services
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.text.TextUtils
import androidx.core.content.ContextCompat
import im.vector.riotx.core.extensions.vectorComponent
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
@ -38,7 +37,7 @@ class CallService : VectorService() {
/**
* call in progress (foreground notification)
*/
private var mCallIdInProgress: String? = null
// private var mCallIdInProgress: String? = null
private lateinit var notificationUtils: NotificationUtils
private lateinit var webRtcPeerConnectionManager: WebRtcPeerConnectionManager
@ -46,15 +45,19 @@ class CallService : VectorService() {
/**
* incoming (foreground notification)
*/
private var mIncomingCallId: String? = null
// private var mIncomingCallId: String? = null
private var callRingPlayer: CallRingPlayer? = null
override fun onCreate() {
super.onCreate()
notificationUtils = vectorComponent().notificationUtils()
webRtcPeerConnectionManager = vectorComponent().webRtcPeerConnectionManager()
callRingPlayer = CallRingPlayer(applicationContext)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.v("## VOIP onStartCommand $intent")
if (intent == null) {
// Service started again by the system.
// TODO What do we do here?
@ -62,12 +65,34 @@ class CallService : VectorService() {
}
when (intent.action) {
ACTION_INCOMING_CALL -> displayIncomingCallNotification(intent)
ACTION_PENDING_CALL -> displayCallInProgressNotification(intent)
ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
else ->
ACTION_INCOMING_RINGING_CALL -> {
callRingPlayer?.start()
displayIncomingCallNotification(intent)
}
ACTION_OUTGOING_RINGING_CALL -> {
callRingPlayer?.start()
displayOutgoingRingingCallNotification(intent)
}
ACTION_ONGOING_CALL -> {
callRingPlayer?.stop()
displayCallInProgressNotification(intent)
}
ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
ACTION_CALL_CONNECTING -> {
// lower notification priority
displayCallInProgressNotification(intent)
// stop ringing
callRingPlayer?.stop()
}
ACTION_ONGOING_CALL_BG -> {
// there is an ongoing call but call activity is in background
displayCallOnGoingInBackground(intent)
}
else -> {
// Should not happen
callRingPlayer?.stop()
myStopSelf()
}
}
// We want the system to restore the service if killed
@ -87,29 +112,29 @@ class CallService : VectorService() {
* @param callId the callId
*/
private fun displayIncomingCallNotification(intent: Intent) {
Timber.v("displayIncomingCallNotification")
Timber.v("## VOIP displayIncomingCallNotification $intent")
// the incoming call in progress is already displayed
if (!TextUtils.isEmpty(mIncomingCallId)) {
Timber.v("displayIncomingCallNotification : the incoming call in progress is already displayed")
} else if (!TextUtils.isEmpty(mCallIdInProgress)) {
Timber.v("displayIncomingCallNotification : a 'call in progress' notification is displayed")
} else
// if (null == webRtcPeerConnectionManager.currentCall)
{
val callId = intent.getStringExtra(EXTRA_CALL_ID)
// if (!TextUtils.isEmpty(mIncomingCallId)) {
// Timber.v("displayIncomingCallNotification : the incoming call in progress is already displayed")
// } else if (!TextUtils.isEmpty(mCallIdInProgress)) {
// Timber.v("displayIncomingCallNotification : a 'call in progress' notification is displayed")
// } else
// // if (null == webRtcPeerConnectionManager.currentCall)
// {
val callId = intent.getStringExtra(EXTRA_CALL_ID)
Timber.v("displayIncomingCallNotification : display the dedicated notification")
val notification = notificationUtils.buildIncomingCallNotification(
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
callId ?: "")
startForeground(NOTIFICATION_ID, notification)
Timber.v("displayIncomingCallNotification : display the dedicated notification")
val notification = notificationUtils.buildIncomingCallNotification(
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
callId ?: "")
startForeground(NOTIFICATION_ID, notification)
mIncomingCallId = callId
// mIncomingCallId = callId
// turn the screen on for 3 seconds
// turn the screen on for 3 seconds
// if (Matrix.getInstance(VectorApp.getInstance())!!.pushManager.isScreenTurnedOn) {
// try {
// val pm = getSystemService(Context.POWER_SERVICE) as PowerManager
@ -123,16 +148,29 @@ class CallService : VectorService() {
// }
//
// }
}
// }
// else {
// Timber.i("displayIncomingCallNotification : do not display the incoming call notification because there is a pending call")
// }
}
private fun displayOutgoingRingingCallNotification(intent: Intent) {
val callId = intent.getStringExtra(EXTRA_CALL_ID)
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
val notification = notificationUtils.buildOutgoingRingingCallNotification(
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
callId ?: "")
startForeground(NOTIFICATION_ID, notification)
}
/**
* Display a call in progress notification.
*/
private fun displayCallInProgressNotification(intent: Intent) {
Timber.v("## VOIP displayCallInProgressNotification")
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
val notification = notificationUtils.buildPendingCallNotification(
@ -144,7 +182,27 @@ class CallService : VectorService() {
startForeground(NOTIFICATION_ID, notification)
mCallIdInProgress = callId
// mCallIdInProgress = callId
}
/**
* Display a call in progress notification.
*/
private fun displayCallOnGoingInBackground(intent: Intent) {
Timber.v("## VOIP displayCallInProgressNotification")
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
val notification = notificationUtils.buildPendingCallNotification(
isVideo = intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
roomName = intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
roomId = intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
matrixId = intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
callId = callId,
fromBg = true)
startForeground(NOTIFICATION_ID, notification)
// mCallIdInProgress = callId
}
/**
@ -159,6 +217,11 @@ class CallService : VectorService() {
myStopSelf()
}
override fun onDestroy() {
super.onDestroy()
callRingPlayer?.stop()
}
fun addConnection(callConnection: CallConnection) {
connections[callConnection.callId] = callConnection
}
@ -166,10 +229,14 @@ class CallService : VectorService() {
companion object {
private const val NOTIFICATION_ID = 6480
private const val ACTION_INCOMING_CALL = "im.vector.riotx.core.services.CallService.INCOMING_CALL"
private const val ACTION_PENDING_CALL = "im.vector.riotx.core.services.CallService.PENDING_CALL"
private const val ACTION_INCOMING_RINGING_CALL = "im.vector.riotx.core.services.CallService.ACTION_INCOMING_RINGING_CALL"
private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.riotx.core.services.CallService.ACTION_OUTGOING_RINGING_CALL"
private const val ACTION_CALL_CONNECTING = "im.vector.riotx.core.services.CallService.ACTION_CALL_CONNECTING"
private const val ACTION_ONGOING_CALL = "im.vector.riotx.core.services.CallService.ACTION_ONGOING_CALL"
private const val ACTION_ONGOING_CALL_BG = "im.vector.riotx.core.services.CallService.ACTION_ONGOING_CALL_BG"
private const val ACTION_NO_ACTIVE_CALL = "im.vector.riotx.core.services.CallService.NO_ACTIVE_CALL"
// private const val ACTION_ON_ACTIVE_CALL = "im.vector.riotx.core.services.CallService.ACTIVE_CALL"
// private const val ACTION_ACTIVITY_VISIBLE = "im.vector.riotx.core.services.CallService.ACTION_ACTIVITY_VISIBLE"
// private const val ACTION_STOP_RINGING = "im.vector.riotx.core.services.CallService.ACTION_STOP_RINGING"
private const val EXTRA_IS_VIDEO = "EXTRA_IS_VIDEO"
private const val EXTRA_ROOM_NAME = "EXTRA_ROOM_NAME"
@ -177,15 +244,15 @@ class CallService : VectorService() {
private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
fun onIncomingCall(context: Context,
isVideo: Boolean,
roomName: String,
roomId: String,
matrixId: String,
callId: String) {
fun onIncomingCallRinging(context: Context,
isVideo: Boolean,
roomName: String,
roomId: String,
matrixId: String,
callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
action = ACTION_INCOMING_CALL
action = ACTION_INCOMING_RINGING_CALL
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)
@ -196,24 +263,43 @@ class CallService : VectorService() {
ContextCompat.startForegroundService(context, intent)
}
// fun onActiveCall(context: Context,
// isVideo: Boolean,
// roomName: String,
// roomId: String,
// matrixId: String,
// callId: String) {
// val intent = Intent(context, CallService::class.java)
// .apply {
// action = ACTION_ON_ACTIVE_CALL
// putExtra(EXTRA_IS_VIDEO, isVideo)
// putExtra(EXTRA_ROOM_NAME, roomName)
// putExtra(EXTRA_ROOM_ID, roomId)
// putExtra(EXTRA_MATRIX_ID, matrixId)
// putExtra(EXTRA_CALL_ID, callId)
// }
//
// ContextCompat.startForegroundService(context, intent)
// }
fun onOnGoingCallBackground(context: Context,
isVideo: Boolean,
roomName: String,
roomId: String,
matrixId: String,
callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
action = ACTION_ONGOING_CALL_BG
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)
putExtra(EXTRA_MATRIX_ID, matrixId)
putExtra(EXTRA_CALL_ID, callId)
}
ContextCompat.startForegroundService(context, intent)
}
fun onOutgoingCallRinging(context: Context,
isVideo: Boolean,
roomName: String,
roomId: String,
matrixId: String,
callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
action = ACTION_OUTGOING_RINGING_CALL
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)
putExtra(EXTRA_MATRIX_ID, matrixId)
putExtra(EXTRA_CALL_ID, callId)
}
ContextCompat.startForegroundService(context, intent)
}
fun onPendingCall(context: Context,
isVideo: Boolean,
@ -223,7 +309,7 @@ class CallService : VectorService() {
callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
action = ACTION_PENDING_CALL
action = ACTION_ONGOING_CALL
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)

View file

@ -20,6 +20,7 @@ package im.vector.riotx.features.call
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
@ -31,6 +32,7 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import butterknife.BindView
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import com.jakewharton.rxbinding3.view.clicks
@ -46,10 +48,11 @@ import im.vector.riotx.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
import im.vector.riotx.core.utils.allGranted
import im.vector.riotx.core.utils.checkPermissions
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_call.*
import kotlinx.android.synthetic.main.fragment_attachments_preview.*
import org.webrtc.EglBase
import org.webrtc.RendererCommon
import org.webrtc.SurfaceViewRenderer
@ -101,19 +104,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
override fun doBeforeSetContentView() {
// Set window styles for fullscreen-window size. Needs to be done before adding content.
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true)
setShowWhenLocked(true)
getSystemService(KeyguardManager::class.java)?.requestDismissKeyguard(this, null)
} else {
@Suppress("DEPRECATION")
window.addFlags(
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
)
}
hideSystemUI()
setContentView(R.layout.activity_call)
@ -179,31 +169,22 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
if (intent.hasExtra(MvRx.KEY_ARG)) {
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
} else {
Timber.e("## VOIP missing callArgs for VectorCall Activity")
CallService.onNoActiveCall(this)
finish()
}
Timber.v("## VOIP EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}")
if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) {
turnScreenOnAndKeyguardOff()
}
constraintLayout.clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { toggleUiSystemVisibility() }
.disposeOnDestroy()
if (isFirstCreation()) {
// Reduce priority of notification as the activity is on screen
CallService.onPendingCall(
this,
callArgs.isVideoCall,
callArgs.participantUserId,
callArgs.roomId,
"",
callArgs.callId ?: ""
)
}
rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
finish()
}
configureCallViews()
callViewModel.subscribe(this) {
@ -229,8 +210,68 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
}
override fun onDestroy() {
super.onDestroy()
peerConnectionManager.detachRenderers()
turnScreenOffAndKeyguardOn()
}
// override fun onResume() {
// super.onResume()
// }
//
// override fun onStop() {
// super.onStop()
// when(callViewModel.call?.state) {
// CallState.DIALING -> {
// CallService.onIncomingCall(
// this,
// callArgs.isVideoCall,
// callArgs.participantUserId,
// callArgs.roomId,
// "",
// callArgs.callId ?: ""
// )
// }
// CallState.LOCAL_RINGING -> {
// CallService.onIncomingCall(
// this,
// callArgs.isVideoCall,
// callArgs.participantUserId,
// callArgs.roomId,
// "",
// callArgs.callId ?: ""
// )
// }
// CallState.ANSWERING,
// CallState.CONNECTING,
// CallState.CONNECTED -> {
// CallService.onPendingCall(
// this,
// callArgs.isVideoCall,
// callArgs.participantUserId,
// callArgs.roomId,
// "",
// callArgs.callId ?: ""
// )
// }
// CallState.TERMINATED ,
// CallState.IDLE ,
// null -> {
//
// }
// }
// }
private fun renderState(state: VectorCallViewState) {
Timber.v("## VOIP renderState call $state")
if (state.callState is Fail) {
// be sure to clear notification
CallService.onNoActiveCall(this)
finish()
return
}
callControlsView.updateForState(state)
when (state.callState.invoke()) {
CallState.IDLE,
@ -271,6 +312,8 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
configureCallInfo(state)
callStatusText.text = null
}
// ensure all attached?
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer, null)
}
CallState.TERMINATED -> {
finish()
@ -290,20 +333,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
private fun configureCallViews() {
callControlsView.interactionListener = this
// if (callArgs.isVideoCall) {
// iv_call_speaker.isVisible = false
// iv_call_flip_camera.isVisible = true
// iv_call_videocam_off.isVisible = true
// } else {
// iv_call_speaker.isVisible = true
// iv_call_flip_camera.isVisible = false
// iv_call_videocam_off.isVisible = false
// }
//
// iv_end_call.setOnClickListener {
// callViewModel.handle(VectorCallViewActions.EndCall)
// finish()
// }
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
@ -315,7 +344,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
}
private fun start(): Boolean {
private fun start() {
rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
Timber.v("## VOIP rootEglBase is null")
finish()
}
// Init Picture in Picture renderer
pipRenderer.init(rootEglBase!!.eglBaseContext, null)
pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
@ -330,16 +364,31 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer,
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
return false
}
override fun onPause() {
peerConnectionManager.detachRenderers()
super.onPause()
}
// override fun onResume() {
// super.onResume()
// withState(callViewModel) {
// if(it.callState.invoke() == CallState.CONNECTED) {
// peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer)
// }
// }
// }
// override fun onPause() {
// peerConnectionManager.detachRenderers()
// super.onPause()
// }
private fun handleViewEvents(event: VectorCallViewEvents?) {
Timber.v("handleViewEvents $event")
Timber.v("## VOIP handleViewEvents $event")
when (event) {
VectorCallViewEvents.DismissNoCall -> {
CallService.onNoActiveCall(this)
finish()
}
null -> {
}
}
// when (event) {
// is VectorCallViewEvents.CallAnswered -> {
// }
@ -360,6 +409,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
fun newIntent(context: Context, mxCall: MxCallDetail): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
// what could be the best flags?
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.otherUserId, !mxCall.isOutgoing, mxCall.isVideoCall, false))
putExtra(EXTRA_MODE, OUTGOING_CREATED)
@ -375,7 +425,8 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
accept: Boolean,
mode: String?): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
// what could be the best flags?
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall, accept))
putExtra(EXTRA_MODE, mode)
}
@ -403,11 +454,48 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
override fun returnToChat() {
// TODO, what if the room is not in backstack??
val args = RoomDetailArgs(callArgs.roomId)
val intent = RoomDetailActivity.newIntent(this, args).apply {
flags = FLAG_ACTIVITY_CLEAR_TOP
}
startActivity(intent)
// is it needed?
finish()
}
override fun didTapMore() {
CallControlsBottomSheet().show(supportFragmentManager, "Controls")
}
// Needed to let you answer call when phone is locked
private fun turnScreenOnAndKeyguardOff() {
Timber.v("## VOIP turnScreenOnAndKeyguardOff")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
with(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requestDismissKeyguard(this@VectorCallActivity, null)
}
}
}
private fun turnScreenOffAndKeyguardOn() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(false)
setTurnScreenOn(false)
} else {
window.clearFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
}
}

View file

@ -17,6 +17,7 @@
package im.vector.riotx.features.call
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
@ -58,6 +59,7 @@ sealed class VectorCallViewActions : VectorViewModelAction {
sealed class VectorCallViewEvents : VectorViewEvents {
object DismissNoCall : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
// object CallAccepted : VectorCallViewEvents()
@ -117,6 +119,12 @@ class VectorCallViewModel @AssistedInject constructor(
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice()
)
}
} ?: run {
setState {
copy(
callState = Fail(IllegalArgumentException("No call"))
)
}
}
}
}

View file

@ -266,6 +266,23 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP attachViewRenderers localRendeder $localViewRenderer / $remoteViewRenderer")
this.localSurfaceRenderer = WeakReference(localViewRenderer)
this.remoteSurfaceRenderer = WeakReference(remoteViewRenderer)
// The call is going to resume from background, we can reduce notif
currentCall?.mxCall
?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED }
?.let { mxCall ->
val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.roomId
// Start background service with notification
CallService.onPendingCall(
context = context,
isVideo = mxCall.isVideoCall,
roomName = name,
roomId = mxCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = mxCall.callId)
}
getTurnServer { turnServer ->
val call = currentCall ?: return@getTurnServer
when (mode) {
@ -314,6 +331,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
private fun internalAcceptIncomingCall(callContext: CallContext, turnServerResponse: TurnServerResponse?) {
val mxCall = callContext.mxCall
// Update service state
val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.roomId
CallService.onPendingCall(
context = context,
isVideo = mxCall.isVideoCall,
roomName = name,
roomId = mxCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = mxCall.callId
)
executor.execute {
// 1) create peer connection
createPeerConnection(callContext, turnServerResponse)
@ -435,7 +465,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
fun acceptIncomingCall() {
Timber.v("## VOIP acceptIncomingCall from state ${currentCall?.mxCall?.state}")
if (currentCall?.mxCall?.state == CallState.LOCAL_RINGING) {
val mxCall = currentCall?.mxCall
if (mxCall?.state == CallState.LOCAL_RINGING) {
getTurnServer { turnServer ->
internalAcceptIncomingCall(currentCall!!, turnServer)
}
@ -443,6 +474,24 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
fun detachRenderers() {
// The call is going to continue in background, so ensure notification is visible
currentCall?.mxCall
?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED }
?.let { mxCall ->
// Start background service with notification
val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
CallService.onOnGoingCallBackground(
context = context,
isVideo = mxCall.isVideoCall,
roomName = name,
roomId = mxCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = mxCall.callId
)
}
Timber.v("## VOIP detachRenderers")
// currentCall?.localMediaStream?.let { currentCall?.peerConnection?.removeStream(it) }
localSurfaceRenderer?.get()?.let {
@ -496,6 +545,16 @@ class WebRtcPeerConnectionManager @Inject constructor(
audioManager.startForCall(createdCall)
currentCall = callContext
val name = sessionHolder.getSafeActiveSession()?.getUser(createdCall.otherUserId)?.getBestName()
?: createdCall.otherUserId
CallService.onOutgoingCallRinging(
context = context,
isVideo = createdCall.isVideoCall,
roomName = name,
roomId = createdCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = createdCall.callId)
executor.execute {
callContext.remoteCandidateSource = ReplaySubject.create()
}
@ -524,9 +583,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
// TODO What if a call is currently active?
if (currentCall != null) {
Timber.w("## VOIP TODO: Automatically reject incoming call?")
mxCall.hangUp()
audioManager.stop()
Timber.w("## VOIP receiving incoming call while already in call?")
// Just ignore, maybe we could answer from other session?
return
}
@ -537,12 +595,17 @@ class WebRtcPeerConnectionManager @Inject constructor(
callContext.remoteCandidateSource = ReplaySubject.create()
}
CallService.onIncomingCall(context,
mxCall.isVideoCall,
mxCall.otherUserId,
mxCall.roomId,
sessionHolder.getSafeActiveSession()?.myUserId ?: "",
mxCall.callId)
// Start background service with notification
val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
CallService.onIncomingCallRinging(
context = context,
isVideo = mxCall.isVideoCall,
roomName = name,
roomId = mxCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = mxCall.callId
)
callContext.offerSdp = callInviteContent.offer
}
@ -575,12 +638,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
fun endCall() {
// Update service state
CallService.onNoActiveCall(context)
// close tracks ASAP
currentCall?.localVideoTrack?.setEnabled(false)
currentCall?.localVideoTrack?.setEnabled(false)
currentCall?.cameraAvailabilityCallback?.let { cameraAvailabilityCallback ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
}
}
currentCall?.mxCall?.hangUp()
currentCall = null
audioManager.stop()
@ -592,6 +662,18 @@ class WebRtcPeerConnectionManager @Inject constructor(
if (call.mxCall.callId != callAnswerContent.callId) return Unit.also {
Timber.w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
}
val mxCall = call.mxCall
// Update service state
val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
CallService.onPendingCall(
context = context,
isVideo = mxCall.isVideoCall,
roomName = name,
roomId = mxCall.roomId,
matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
callId = mxCall.callId
)
executor.execute {
Timber.v("## VOIP onCallAnswerReceived ${callAnswerContent.callId}")
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
@ -626,7 +708,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
* One or more of the ICE transports on the connection is in the "failed" state.
*/
PeerConnection.PeerConnectionState.FAILED -> {
endCall()
// This can be temporary, e.g when other ice not yet received...
// callContext.mxCall.state = CallState.ERROR
}
/**
* At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state,
@ -711,7 +794,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
* It is, however, possible that the ICE agent did find compatible connections for some components.
*/
PeerConnection.IceConnectionState.FAILED -> {
callContext.mxCall.hangUp()
// I should not hangup here..
// because new candidates could arrive
// callContext.mxCall.hangUp()
}
/**
* The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components.
@ -742,7 +827,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
remoteVideoTrack.setEnabled(true)
callContext.remoteVideoTrack = remoteVideoTrack
// sink to renderer if attached
remoteSurfaceRenderer?.get().let { remoteVideoTrack.addSink(it) }
remoteSurfaceRenderer?.get()?.let { remoteVideoTrack.addSink(it) }
}
}
}

View file

@ -23,9 +23,15 @@ import im.vector.riotx.core.di.HasVectorInjector
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
import im.vector.riotx.features.notifications.NotificationUtils
import im.vector.riotx.features.settings.VectorLocale.context
import timber.log.Timber
class CallHeadsUpActionReceiver : BroadcastReceiver() {
companion object {
const val EXTRA_CALL_ACTION_KEY = "EXTRA_CALL_ACTION_KEY"
const val CALL_ACTION_REJECT = 0
}
private lateinit var peerConnectionManager: WebRtcPeerConnectionManager
private lateinit var notificationUtils: NotificationUtils
@ -38,10 +44,9 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
}
override fun onReceive(context: Context, intent: Intent?) {
// when (intent?.getIntExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, 0)) {
// CallHeadsUpService.CALL_ACTION_ANSWER -> onCallAnswerClicked(context)
// CallHeadsUpService.CALL_ACTION_REJECT -> onCallRejectClicked()
// }
when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) {
CALL_ACTION_REJECT -> onCallRejectClicked()
}
// Not sure why this should be needed
// val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
@ -51,10 +56,10 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
// context.stopService(Intent(context, CallHeadsUpService::class.java))
}
// private fun onCallRejectClicked() {
// Timber.d("onCallRejectClicked")
// peerConnectionManager.endCall()
// }
private fun onCallRejectClicked() {
Timber.d("onCallRejectClicked")
peerConnectionManager.endCall()
}
// private fun onCallAnswerClicked(context: Context) {
// Timber.d("onCallAnswerClicked")

View file

@ -283,31 +283,29 @@ class NotificationUtils @Inject constructor(private val context: Context,
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setLights(accentColor, 500, 500)
.setOngoing(true)
// Compat: Display the incoming call notification on the lock screen
builder.priority = NotificationCompat.PRIORITY_HIGH
// clear the activity stack to home activity
// val intent = Intent(context, HomeActivity::class.java)
// .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
// TODO .putExtra(VectorHomeActivity.EXTRA_CALL_SESSION_ID, matrixId)
// TODO .putExtra(VectorHomeActivity.EXTRA_CALL_ID, callId)
// Recreate the back stack
// val stackBuilder = TaskStackBuilder.create(context)
// .addParentStack(HomeActivity::class.java)
// .addNextIntent(intent)
// android 4.3 issue
// use a generator for the private requestCode.
// When using 0, the intent is not created/launched when the user taps on the notification.
//
val requestId = Random.nextInt(1000)
// val pendingIntent = stackBuilder.getPendingIntent(requestId, PendingIntent.FLAG_UPDATE_CURRENT)
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
.addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, false, VectorCallActivity.INCOMING_RINGING))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
val contentIntent = VectorCallActivity.newIntent(
context, callId, roomId, otherUserId, true, isVideo,
false, VectorCallActivity.INCOMING_RINGING
).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
data = Uri.parse("foobar://$callId")
}
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
// val contentPendingIntent = TaskStackBuilder.create(context)
// .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
// .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId = roomId))
// .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, false, VectorCallActivity.INCOMING_RINGING))
// .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
val answerCallPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
@ -321,7 +319,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
// putExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, CallHeadsUpService.CALL_ACTION_REJECT)
}
// val answerCallPendingIntent = PendingIntent.getBroadcast(context, requestId, answerCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
val rejectCallPendingIntent = PendingIntent.getBroadcast(context, requestId + 1, rejectCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
val rejectCallPendingIntent = PendingIntent.getBroadcast(
context,
requestId + 1,
rejectCallActionReceiver,
PendingIntent.FLAG_UPDATE_CURRENT
)
builder.addAction(
NotificationCompat.Action(
@ -340,6 +343,54 @@ class NotificationUtils @Inject constructor(private val context: Context,
rejectCallPendingIntent)
)
builder.setFullScreenIntent(contentPendingIntent, true)
return builder.build()
}
fun buildOutgoingRingingCallNotification(isVideo: Boolean,
otherUserId: String,
roomId: String,
callId: String): Notification {
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
.setContentTitle(ensureTitleNotEmpty(otherUserId))
.apply {
setContentText(stringProvider.getString(R.string.call_ring))
}
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setLights(accentColor, 500, 500)
.setOngoing(true)
val requestId = Random.nextInt(1000)
val contentIntent = VectorCallActivity.newIntent(
context, callId, roomId, otherUserId, true, isVideo,
false, null).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
data = Uri.parse("foobar://$callId")
}
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
}
val rejectCallPendingIntent = PendingIntent.getBroadcast(
context,
requestId + 1,
rejectCallActionReceiver,
PendingIntent.FLAG_UPDATE_CURRENT
)
builder.addAction(
NotificationCompat.Action(
IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
context.getString(R.string.call_notification_hangup),
rejectCallPendingIntent)
)
builder.setContentIntent(contentPendingIntent)
return builder.build()
@ -360,8 +411,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
roomName: String,
roomId: String,
matrixId: String,
callId: String): Notification {
val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
callId: String, fromBg: Boolean = false): Notification {
val builder = NotificationCompat.Builder(context, if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
.setContentTitle(ensureTitleNotEmpty(roomName))
.apply {
if (isVideo) {
@ -373,7 +424,29 @@ class NotificationUtils @Inject constructor(private val context: Context,
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
.setCategory(NotificationCompat.CATEGORY_CALL)
builder.priority = NotificationCompat.PRIORITY_DEFAULT
if (fromBg) {
builder.priority = NotificationCompat.PRIORITY_LOW
builder.setOngoing(true)
}
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
data = Uri.parse("mxcall://end?$callId")
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
}
val rejectCallPendingIntent = PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
rejectCallActionReceiver,
PendingIntent.FLAG_UPDATE_CURRENT
)
builder.addAction(
NotificationCompat.Action(
IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
context.getString(R.string.call_notification_hangup),
rejectCallPendingIntent)
)
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
@ -381,32 +454,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
.addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, false, null))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
// android 4.3 issue
// use a generator for the private requestCode.
// When using 0, the intent is not created/launched when the user taps on the notification.
builder.setContentIntent(contentPendingIntent)
/* TODO
// Build the pending intent for when the notification is clicked
val roomIntent = Intent(context, VectorRoomActivity::class.java)
.putExtra(VectorRoomActivity.EXTRA_ROOM_ID, roomId)
.putExtra(VectorRoomActivity.EXTRA_MATRIX_ID, matrixId)
.putExtra(VectorRoomActivity.EXTRA_START_CALL_ID, callId)
// Recreate the back stack
val stackBuilder = TaskStackBuilder.create(context)
.addParentStack(VectorRoomActivity::class.java)
.addNextIntent(roomIntent)
// android 4.3 issue
// use a generator for the private requestCode.
// When using 0, the intent is not created/launched when the user taps on the notification.
//
val pendingIntent = stackBuilder.getPendingIntent(Random().nextInt(1000), PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(pendingIntent)
*/
return builder.build()
}