From 76ed775f6f7d730f4aeb22f36b68c8314ab7bbd2 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Wed, 9 Dec 2020 11:17:49 +0100
Subject: [PATCH 1/6] VoIP: start to show in-app notification

---
 .../android/sdk/api/session/call/MxCall.kt    |   1 +
 .../session/call/CallSignalingHandler.kt      |   2 +-
 .../internal/session/call/MxCallFactory.kt    |  12 +-
 .../internal/session/call/model/MxCallImpl.kt |   1 +
 vector/build.gradle                           |   2 +-
 .../vector/app/core/services/CallService.kt   | 176 ++++++++---------
 .../app/features/call/CallControlsView.kt     |   7 +-
 .../app/features/call/VectorCallActivity.kt   |   4 +-
 .../app/features/call/webrtc/WebRtcCall.kt    |  22 +--
 .../features/call/webrtc/WebRtcCallManager.kt |  23 +--
 .../IncomingVerificationRequestHandler.kt     |  22 ++-
 .../vector/app/features/home/HomeActivity.kt  |  17 +-
 .../app/features/home/HomeDetailFragment.kt   |   8 +-
 .../home/room/detail/RoomDetailActivity.kt    |   2 +-
 .../home/room/detail/RoomDetailFragment.kt    | 177 +++++++++---------
 .../notifications/NotificationUtils.kt        |  75 +++-----
 .../app/features/popup/IncomingCallAlert.kt   |  61 ++++++
 .../app/features/popup/PopupAlertManager.kt   |  17 +-
 .../vector/app/features/popup/VectorAlert.kt  |  57 +++---
 .../features/popup/VerificationVectorAlert.kt |  59 ++++++
 .../layout/alerter_incoming_call_layout.xml   |  83 ++++++++
 .../main/res/layout/fragment_room_detail.xml  |   2 +-
 .../main/res/layout/view_call_controls.xml    |   5 +-
 vector/src/main/res/values/colors_riotx.xml   |   7 +
 vector/src/main/res/values/theme_black.xml    |   1 +
 vector/src/main/res/values/theme_dark.xml     |   2 +-
 vector/src/main/res/values/theme_light.xml    |   2 +-
 27 files changed, 496 insertions(+), 351 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
 create mode 100644 vector/src/main/res/layout/alerter_incoming_call_layout.xml

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
index 75cff0e709..9ff07a21ad 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
@@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.call.SdpType
 import org.matrix.android.sdk.api.util.Optional
 
 interface MxCallDetail {
+    val sessionId: String
     val callId: String
     val isOutgoing: Boolean
     val roomId: String
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt
index 562809a826..a6de65f9f5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt
@@ -160,7 +160,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
         val content = event.getClearContent().toModel<CallInviteContent>() ?: return
         val incomingCall = mxCallFactory.createIncomingCall(
                 roomId = event.roomId,
-                senderId = event.senderId,
+                opponentUserId = event.senderId,
                 content = content
         ) ?: return
         activeCallHandler.addCall(incomingCall)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
index f970522cc9..355cb4830d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
@@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.session.call.MxCall
 import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.di.DeviceId
+import org.matrix.android.sdk.internal.di.SessionId
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.session.call.model.MxCallImpl
 import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
@@ -29,21 +30,23 @@ import java.util.UUID
 import javax.inject.Inject
 
 internal class MxCallFactory @Inject constructor(
+        @SessionId private val sessionId: String,
         @DeviceId private val deviceId: String?,
         private val localEchoEventFactory: LocalEchoEventFactory,
         private val eventSenderProcessor: EventSenderProcessor,
         @UserId private val userId: String
 ) {
 
-    fun createIncomingCall(roomId: String, senderId: String, content: CallInviteContent): MxCall? {
+    fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? {
         if (content.callId == null) return null
         return MxCallImpl(
+                sessionId = sessionId,
                 callId = content.callId,
                 isOutgoing = false,
                 roomId = roomId,
                 userId = userId,
                 ourPartyId = deviceId ?: "",
-                opponentUserId = senderId,
+                opponentUserId = opponentUserId,
                 isVideoCall = content.isVideo(),
                 localEchoEventFactory = localEchoEventFactory,
                 eventSenderProcessor = eventSenderProcessor
@@ -53,14 +56,15 @@ internal class MxCallFactory @Inject constructor(
         }
     }
 
-    fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
+    fun createOutgoingCall(roomId: String, opponentUserId: String, isVideoCall: Boolean): MxCall {
         return MxCallImpl(
+                sessionId = sessionId,
                 callId = UUID.randomUUID().toString(),
                 isOutgoing = true,
                 roomId = roomId,
                 userId = userId,
                 ourPartyId = deviceId ?: "",
-                opponentUserId = otherUserId,
+                opponentUserId = opponentUserId,
                 isVideoCall = isVideoCall,
                 localEchoEventFactory = localEchoEventFactory,
                 eventSenderProcessor = eventSenderProcessor
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
index 9368e94efc..38de28d4b8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
@@ -40,6 +40,7 @@ import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
 import timber.log.Timber
 
 internal class MxCallImpl(
+        override val sessionId: String,
         override val callId: String,
         override val isOutgoing: Boolean,
         override val roomId: String,
diff --git a/vector/build.gradle b/vector/build.gradle
index 6edaec1755..69f8daf47b 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -239,7 +239,7 @@ android {
     productFlavors {
         gplay {
             dimension "store"
-
+            isDefault = true
             versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}"
 
             resValue "bool", "isGplay", "true"
diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt
index 397394e4fe..a0adcca496 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt
@@ -25,9 +25,16 @@ import android.view.KeyEvent
 import androidx.core.content.ContextCompat
 import androidx.media.session.MediaButtonReceiver
 import im.vector.app.core.extensions.vectorComponent
-import im.vector.app.features.call.webrtc.WebRtcCallManager
+import im.vector.app.features.call.VectorCallActivity
 import im.vector.app.features.call.telecom.CallConnection
+import im.vector.app.features.call.webrtc.WebRtcCall
+import im.vector.app.features.call.webrtc.WebRtcCallManager
+import im.vector.app.features.home.AvatarRenderer
 import im.vector.app.features.notifications.NotificationUtils
+import im.vector.app.features.popup.IncomingCallAlert
+import im.vector.app.features.popup.PopupAlertManager
+import org.matrix.android.sdk.api.util.MatrixItem
+import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
 
 /**
@@ -39,6 +46,8 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
 
     private lateinit var notificationUtils: NotificationUtils
     private lateinit var callManager: WebRtcCallManager
+    private lateinit var avatarRenderer: AvatarRenderer
+    private lateinit var alertManager: PopupAlertManager
 
     private var callRingPlayerIncoming: CallRingPlayerIncoming? = null
     private var callRingPlayerOutgoing: CallRingPlayerOutgoing? = null
@@ -64,6 +73,8 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         super.onCreate()
         notificationUtils = vectorComponent().notificationUtils()
         callManager = vectorComponent().webRtcCallManager()
+        avatarRenderer = vectorComponent().avatarRenderer()
+        alertManager = vectorComponent().alertManager()
         callRingPlayerIncoming = CallRingPlayerIncoming(applicationContext)
         callRingPlayerOutgoing = CallRingPlayerOutgoing(applicationContext)
         wiredHeadsetStateReceiver = WiredHeadsetStateReceiver.createAndRegister(this, this)
@@ -111,20 +122,20 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
                 callRingPlayerOutgoing?.start()
                 displayOutgoingRingingCallNotification(intent)
             }
-            ACTION_ONGOING_CALL          -> {
+            ACTION_ONGOING_CALL -> {
                 callRingPlayerIncoming?.stop()
                 callRingPlayerOutgoing?.stop()
                 displayCallInProgressNotification(intent)
             }
-            ACTION_NO_ACTIVE_CALL        -> hideCallNotifications()
-            ACTION_CALL_CONNECTING       -> {
+            ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
+            ACTION_CALL_CONNECTING -> {
                 // lower notification priority
                 displayCallInProgressNotification(intent)
                 // stop ringing
                 callRingPlayerIncoming?.stop()
                 callRingPlayerOutgoing?.stop()
             }
-            ACTION_ONGOING_CALL_BG       -> {
+            ACTION_ONGOING_CALL_BG -> {
                 // there is an ongoing call but call activity is in background
                 displayCallOnGoingInBackground(intent)
             }
@@ -154,56 +165,52 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
      */
     private fun displayIncomingCallNotification(intent: Intent) {
         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)
-
+        val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
+        val call = callManager.getCallById(callId) ?: return
+        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")
+        if (!fromBg) {
+            // Show in-app notification if app is in foreground.
+            val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID).apply {
+                viewBinder = IncomingCallAlert.ViewBinder(
+                        matrixItem = opponentMatrixItem,
+                        avatarRenderer = avatarRenderer,
+                        isVideoCall = isVideoCall,
+                        onAccept = { acceptIncomingCall(call) },
+                        onReject = { call.endCall() }
+                )
+                dismissedAction = Runnable { call.endCall() }
+            }
+            alertManager.postVectorAlert(incomingCallAlert)
+        }
         val notification = notificationUtils.buildIncomingCallNotification(
-                intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
-                intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
-                intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
-                callId ?: "")
+                mxCall = call.mxCall,
+                title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
+                fromBg = fromBg
+        )
         startForeground(NOTIFICATION_ID, notification)
+    }
 
-//            mIncomingCallId = callId
-
-        // turn the screen on for 3 seconds
-//            if (Matrix.getInstance(VectorApp.getInstance())!!.pushManager.isScreenTurnedOn) {
-//                try {
-//                    val pm = getSystemService<PowerManager>()!!
-//                    val wl = pm.newWakeLock(
-//                            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or PowerManager.ACQUIRE_CAUSES_WAKEUP,
-//                            CallService::class.java.simpleName)
-//                    wl.acquire(3000)
-//                    wl.release()
-//                } catch (re: RuntimeException) {
-//                    Timber.e(re, "displayIncomingCallNotification : failed to turn screen on ")
-//                }
-//
-//            }
-//        }
-//        else {
-//            Timber.i("displayIncomingCallNotification : do not display the incoming call notification because there is a pending call")
-//        }
+    private fun acceptIncomingCall(call: WebRtcCall){
+        val intent = VectorCallActivity.newIntent(
+                context = this,
+                mxCall = call.mxCall,
+                mode = VectorCallActivity.INCOMING_ACCEPT
+        )
+        startActivity(intent)
     }
 
     private fun displayOutgoingRingingCallNotification(intent: Intent) {
-        val callId = intent.getStringExtra(EXTRA_CALL_ID)
-
+        val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
+        val call = callManager.getCallById(callId) ?: return
+        val opponentMatrixItem = getOpponentMatrixItem(call)
         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 ?: "")
+                mxCall = call.mxCall,
+                title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
+        )
         startForeground(NOTIFICATION_ID, notification)
     }
 
@@ -213,16 +220,14 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
     private fun displayCallInProgressNotification(intent: Intent) {
         Timber.v("## VOIP displayCallInProgressNotification")
         val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
-
+        val call = callManager.getCallById(callId) ?: return
+        val opponentMatrixItem = getOpponentMatrixItem(call)
+        alertManager.cancelAlert(INCOMING_CALL_ALERT_UID)
         val notification = notificationUtils.buildPendingCallNotification(
-                intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
-                intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
-                intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
-                intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
-                callId)
-
+                mxCall = call.mxCall,
+                title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
+        )
         startForeground(NOTIFICATION_ID, notification)
-
         // mCallIdInProgress = callId
     }
 
@@ -231,18 +236,15 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
      */
     private fun displayCallOnGoingInBackground(intent: Intent) {
         Timber.v("## VOIP displayCallInProgressNotification")
-        val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
+        val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
+        val call = callManager.getCallById(callId) ?: return
+        val opponentMatrixItem = getOpponentMatrixItem(call)
 
         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,
+                mxCall = call.mxCall,
+                title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
                 fromBg = true)
-
         startForeground(NOTIFICATION_ID, notification)
-
         // mCallIdInProgress = callId
     }
 
@@ -251,7 +253,7 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
      */
     private fun hideCallNotifications() {
         val notification = notificationUtils.buildCallEndedNotification()
-
+        alertManager.cancelAlert(INCOMING_CALL_ALERT_UID)
         mediaSession?.isActive = false
         // It's mandatory to startForeground to avoid crash
         startForeground(NOTIFICATION_ID, notification)
@@ -263,9 +265,14 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         connections[callConnection.callId] = callConnection
     }
 
+    private fun getOpponentMatrixItem(call: WebRtcCall): MatrixItem? {
+        return vectorComponent().currentSession().getUser(call.mxCall.opponentUserId)?.toMatrixItem()
+    }
+
     companion object {
         private const val NOTIFICATION_ID = 6480
 
+        private const val INCOMING_CALL_ALERT_UID = "INCOMING_CALL_ALERT_UID"
         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"
         private const val ACTION_CALL_CONNECTING = "im.vector.app.core.services.CallService.ACTION_CALL_CONNECTING"
@@ -275,44 +282,26 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
 //        private const val ACTION_ACTIVITY_VISIBLE = "im.vector.app.core.services.CallService.ACTION_ACTIVITY_VISIBLE"
 //        private const val ACTION_STOP_RINGING = "im.vector.app.core.services.CallService.ACTION_STOP_RINGING"
 
-        private const val EXTRA_IS_VIDEO = "EXTRA_IS_VIDEO"
-        private const val EXTRA_ROOM_NAME = "EXTRA_ROOM_NAME"
-        private const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
-        private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
         private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
+        private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG"
 
         fun onIncomingCallRinging(context: Context,
-                                  isVideo: Boolean,
-                                  roomName: String,
-                                  roomId: String,
-                                  matrixId: String,
-                                  callId: String) {
+                                  callId: String,
+                                  isInBackground: Boolean) {
             val intent = Intent(context, CallService::class.java)
                     .apply {
                         action = ACTION_INCOMING_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)
+                        putExtra(EXTRA_IS_IN_BG, isInBackground)
                     }
-
             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)
                     }
 
@@ -320,18 +309,10 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         }
 
         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)
                     }
 
@@ -339,18 +320,10 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         }
 
         fun onPendingCall(context: Context,
-                          isVideo: Boolean,
-                          roomName: String,
-                          roomId: String,
-                          matrixId: String,
                           callId: String) {
             val intent = Intent(context, CallService::class.java)
                     .apply {
                         action = ACTION_ONGOING_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)
                     }
 
@@ -362,7 +335,6 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
                     .apply {
                         action = ACTION_NO_ACTIVE_CALL
                     }
-
             ContextCompat.startForegroundService(context, intent)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
index 93fc132a8f..6e5678d3dc 100644
--- a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
+++ b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
@@ -18,7 +18,9 @@ package im.vector.app.features.call
 
 import android.content.Context
 import android.util.AttributeSet
+import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -33,7 +35,7 @@ import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
 
 class CallControlsView @JvmOverloads constructor(
         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
+) : FrameLayout(context, attrs, defStyleAttr) {
 
     var interactionListener: InteractionListener? = null
 
@@ -56,8 +58,7 @@ class CallControlsView @JvmOverloads constructor(
     lateinit var videoToggleIcon: ImageView
 
     init {
-        ConstraintLayout.inflate(context, R.layout.view_call_controls, this)
-        // layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+        View.inflate(context, R.layout.view_call_controls, this)
         ButterKnife.bind(this)
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
index c6a5af5843..355bd64380 100644
--- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
@@ -339,12 +339,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
         const val INCOMING_RINGING = "INCOMING_RINGING"
         const val INCOMING_ACCEPT = "INCOMING_ACCEPT"
 
-        fun newIntent(context: Context, mxCall: MxCallDetail): Intent {
+        fun newIntent(context: Context, mxCall: MxCallDetail, mode: String?): 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.opponentUserId, !mxCall.isOutgoing, mxCall.isVideoCall))
-                putExtra(EXTRA_MODE, OUTGOING_CREATED)
+                putExtra(EXTRA_MODE, mode)
             }
         }
 
diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
index 4e12934d12..10c7cb2e24 100644
--- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
+++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
@@ -107,6 +107,7 @@ class WebRtcCall(val mxCall: MxCall,
     }
 
     val callId = mxCall.callId
+    val roomId = mxCall.roomId
 
     private var peerConnection: PeerConnection? = null
     private var localAudioSource: AudioSource? = null
@@ -237,16 +238,9 @@ class WebRtcCall(val mxCall: MxCall,
         mxCall
                 .takeIf { it.state is CallState.Connected }
                 ?.let { mxCall ->
-                    val session = sessionProvider.get()
-                    val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
-                            ?: mxCall.roomId
                     // Start background service with notification
                     CallService.onPendingCall(
                             context = context,
-                            isVideo = mxCall.isVideoCall,
-                            roomName = name,
-                            roomId = mxCall.roomId,
-                            matrixId = session?.myUserId ?: "",
                             callId = mxCall.callId)
                 }
 
@@ -307,15 +301,8 @@ class WebRtcCall(val mxCall: MxCall,
                     .takeIf { it.state is CallState.Connected }
                     ?.let { mxCall ->
                         // Start background service with notification
-                        val session = sessionProvider.get()
-                        val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
-                                ?: mxCall.opponentUserId
                         CallService.onOnGoingCallBackground(
                                 context = context,
-                                isVideo = mxCall.isVideoCall,
-                                roomName = name,
-                                roomId = mxCall.roomId,
-                                matrixId = session?.myUserId ?: "",
                                 callId = mxCall.callId
                         )
                     }
@@ -344,15 +331,8 @@ class WebRtcCall(val mxCall: MxCall,
         val turnServerResponse = getTurnServer()
         // Update service state
         withContext(Dispatchers.Main) {
-            val session = sessionProvider.get()
-            val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
-                    ?: mxCall.roomId
             CallService.onPendingCall(
                     context = context,
-                    isVideo = mxCall.isVideoCall,
-                    roomName = name,
-                    roomId = mxCall.roomId,
-                    matrixId = session?.myUserId ?: "",
                     callId = mxCall.callId
             )
         }
diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
index 8e94604260..f6fcfd446e 100644
--- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
+++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
@@ -189,18 +189,12 @@ class WebRtcCallManager @Inject constructor(
         createWebRtcCall(mxCall)
         callAudioManager.startForCall(mxCall)
 
-        val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
-                ?: mxCall.opponentUserId
         CallService.onOutgoingCallRinging(
                 context = context.applicationContext,
-                isVideo = mxCall.isVideoCall,
-                roomName = name,
-                roomId = mxCall.roomId,
-                matrixId = currentSession?.myUserId ?: "",
                 callId = mxCall.callId)
 
         // start the activity now
-        context.startActivity(VectorCallActivity.newIntent(context, mxCall))
+        context.startActivity(VectorCallActivity.newIntent(context, mxCall, VectorCallActivity.OUTGOING_CREATED))
     }
 
     override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {
@@ -264,15 +258,10 @@ class WebRtcCallManager @Inject constructor(
         }
         callAudioManager.startForCall(mxCall)
         // Start background service with notification
-        val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
-                ?: mxCall.opponentUserId
         CallService.onIncomingCallRinging(
                 context = context,
-                isVideo = mxCall.isVideoCall,
-                roomName = name,
-                roomId = mxCall.roomId,
-                matrixId = currentSession?.myUserId ?: "",
-                callId = mxCall.callId
+                callId = mxCall.callId,
+                isInBackground = isInBackground
         )
         // If this is received while in background, the app will not sync,
         // and thus won't be able to received events. For example if the call is
@@ -294,14 +283,8 @@ class WebRtcCallManager @Inject constructor(
                 }
         val mxCall = call.mxCall
         // Update service state
-        val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
-                ?: mxCall.opponentUserId
         CallService.onPendingCall(
                 context = context,
-                isVideo = mxCall.isVideoCall,
-                roomName = name,
-                roomId = mxCall.roomId,
-                matrixId = currentSession?.myUserId ?: "",
                 callId = mxCall.callId
         )
         call.onCallAnswerReceived(callAnswerContent)
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 7d98b7c2a5..e38ab2f6e8 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification
 import android.content.Context
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.features.home.AvatarRenderer
 import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.home.room.detail.RoomDetailArgs
 import im.vector.app.features.popup.PopupAlertManager
@@ -31,6 +32,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
 import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
 import javax.inject.Inject
+import javax.inject.Provider
 import javax.inject.Singleton
 
 /**
@@ -39,6 +41,7 @@ import javax.inject.Singleton
 @Singleton
 class IncomingVerificationRequestHandler @Inject constructor(
         private val context: Context,
+        private var avatarRenderer: Provider<AvatarRenderer>,
         private val popupAlertManager: PopupAlertManager) : VerificationService.Listener {
 
     private var session: Session? = null
@@ -60,9 +63,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
         when (tx.state) {
             is VerificationTxState.OnStarted       -> {
                 // Add a notification for every incoming request
-                val name = session?.getUser(tx.otherUserId)?.displayName
-                        ?: tx.otherUserId
-
+                val user = session?.getUser(tx.otherUserId)
+                val name = user?.displayName ?: tx.otherUserId
                 val alert = VerificationVectorAlert(
                         uid,
                         context.getString(R.string.sas_incoming_request_notif_title),
@@ -77,10 +79,10 @@ class IncomingVerificationRequestHandler @Inject constructor(
                                     }
                                 } ?: true
                             } else true
-                        },
-                        matrixItem = session?.getUser(tx.otherUserId)?.toMatrixItem()
+                        }
                 )
                         .apply {
+                            viewBinder = VerificationVectorAlert.ViewBinder(user?.toMatrixItem(), avatarRenderer.get())
                             contentAction = Runnable {
                                 (weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
                                     it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId)
@@ -120,8 +122,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
         Timber.v("## SAS verificationRequestCreated ${pr.transactionId}")
         // For incoming request we should prompt (if not in activity where this request apply)
         if (pr.isIncoming) {
-            val name = session?.getUser(pr.otherUserId)?.displayName
-                    ?: pr.otherUserId
+            val user = session?.getUser(pr.otherUserId)
+            val name = user?.displayName ?: pr.otherUserId
 
             val alert = VerificationVectorAlert(
                     uniqueIdForVerificationRequest(pr),
@@ -134,10 +136,10 @@ class IncomingVerificationRequestHandler @Inject constructor(
                                 it.roomId != pr.roomId
                             } ?: true
                         } else true
-                    },
-                    matrixItem = session?.getUser(pr.otherUserId)?.toMatrixItem()
+                    }
             )
                     .apply {
+                        viewBinder = VerificationVectorAlert.ViewBinder(user?.toMatrixItem(), avatarRenderer.get())
                         contentAction = Runnable {
                             (weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
                                 val roomId = pr.roomId
@@ -154,7 +156,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
                                     pr.roomId ?: ""
                             )
                         }
-                        colorInt = ThemeUtils.getColor(context, R.attr.vctr_notice_secondary)
+                        colorAttribute = R.attr.vctr_notice_secondary
                         // 5mn expiration
                         expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L)
                     }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 7dde0edf32..9fb63c50d3 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -82,6 +82,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
     private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
     @Inject lateinit var serverBackupviewModelFactory: ServerBackupStatusViewModel.Factory
 
+    @Inject lateinit var avatarRenderer: AvatarRenderer
     @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
     @Inject lateinit var pushManager: PushersManager
@@ -126,9 +127,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
                 .observe()
                 .subscribe { sharedAction ->
                     when (sharedAction) {
-                        is HomeActivitySharedAction.OpenDrawer  -> drawerLayout.openDrawer(GravityCompat.START)
+                        is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
                         is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START)
-                        is HomeActivitySharedAction.OpenGroup   -> {
+                        is HomeActivitySharedAction.OpenGroup -> {
                             drawerLayout.closeDrawer(GravityCompat.START)
                             replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
                         }
@@ -145,9 +146,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
         homeActivityViewModel.observeViewEvents {
             when (it) {
                 is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
-                is HomeActivityViewEvents.OnNewSession                  -> handleOnNewSession(it)
-                HomeActivityViewEvents.PromptToEnableSessionPush        -> handlePromptToEnablePush()
-                is HomeActivityViewEvents.OnCrossSignedInvalidated      -> handleCrossSigningInvalidated(it)
+                is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
+                HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
+                is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
             }.exhaustive
         }
         homeActivityViewModel.subscribe(this) { renderState(it) }
@@ -180,7 +181,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
 
     private fun renderState(state: HomeActivityViewState) {
         when (val status = state.initialSyncProgressServiceStatus) {
-            is InitialSyncProgressService.Status.Idle        -> {
+            is InitialSyncProgressService.Status.Idle -> {
                 waiting_view.isVisible = false
             }
             is InitialSyncProgressService.Status.Progressing -> {
@@ -251,7 +252,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
                             it is HomeActivity
                         }
                 ).apply {
-                    colorInt = ThemeUtils.getColor(this@HomeActivity, R.attr.vctr_notice_secondary)
+                    colorAttribute = R.attr.vctr_notice_secondary
                     contentAction = Runnable {
                         (weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
                             // action(it)
@@ -283,8 +284,8 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
                         title = getString(titleRes),
                         description = getString(descRes),
                         iconId = R.drawable.ic_shield_warning,
-                        matrixItem = userItem
                 ).apply {
+                    viewBinder = VerificationVectorAlert.ViewBinder(userItem, avatarRenderer)
                     colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
                     contentAction = Runnable {
                         (weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt
index 853eb31274..6a48119a64 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt
@@ -151,9 +151,9 @@ class HomeDetailFragment @Inject constructor(
                         uid = uid,
                         title = getString(R.string.new_session),
                         description = getString(R.string.verify_this_session, newest.displayName ?: newest.deviceId ?: ""),
-                        iconId = R.drawable.ic_shield_warning,
-                        matrixItem = user
+                        iconId = R.drawable.ic_shield_warning
                 ).apply {
+                    viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
                     colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
                     contentAction = Runnable {
                         (weakCurrentActivity?.get() as? VectorBaseActivity)
@@ -179,9 +179,9 @@ class HomeDetailFragment @Inject constructor(
                         uid = uid,
                         title = getString(R.string.review_logins),
                         description = getString(R.string.verify_other_sessions),
-                        iconId = R.drawable.ic_shield_warning,
-                        matrixItem = user
+                        iconId = R.drawable.ic_shield_warning
                 ).apply {
+                    viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
                     colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
                     contentAction = Runnable {
                         (weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
index de82689303..08c1e6806c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
@@ -72,7 +72,7 @@ class RoomDetailActivity :
     }
 
     // Simple filter
-    private var currentRoomId: String? = null
+    var currentRoomId: String? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 8dc85bd8af..8102a69c68 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -118,8 +118,8 @@ import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs
 import im.vector.app.features.attachments.toGroupedContentAttachmentData
 import im.vector.app.features.call.SharedActiveCallViewModel
 import im.vector.app.features.call.VectorCallActivity
-import im.vector.app.features.call.webrtc.WebRtcCallManager
 import im.vector.app.features.call.conference.JitsiCallViewModel
+import im.vector.app.features.call.webrtc.WebRtcCallManager
 import im.vector.app.features.command.Command
 import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
 import im.vector.app.features.crypto.util.toImageRes
@@ -311,7 +311,6 @@ class RoomDetailFragment @Inject constructor(
         setupActiveCallView()
         setupJumpToBottomView()
         setupConfBannerView()
-
         roomToolbarContentView.debouncedClicks {
             navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
         }
@@ -340,9 +339,9 @@ class RoomDetailFragment @Inject constructor(
             }
             when (mode) {
                 is SendMode.REGULAR -> renderRegularMode(mode.text)
-                is SendMode.EDIT    -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
-                is SendMode.QUOTE   -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
-                is SendMode.REPLY   -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
+                is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
+                is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
+                is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
             }
         }
 
@@ -352,33 +351,33 @@ class RoomDetailFragment @Inject constructor(
 
         roomDetailViewModel.observeViewEvents {
             when (it) {
-                is RoomDetailViewEvents.Failure                          -> showErrorInSnackbar(it.throwable)
-                is RoomDetailViewEvents.OnNewTimelineEvents              -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
-                is RoomDetailViewEvents.ActionSuccess                    -> displayRoomDetailActionSuccess(it)
-                is RoomDetailViewEvents.ActionFailure                    -> displayRoomDetailActionFailure(it)
-                is RoomDetailViewEvents.ShowMessage                      -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
-                is RoomDetailViewEvents.NavigateToEvent                  -> navigateToEvent(it)
-                is RoomDetailViewEvents.FileTooBigError                  -> displayFileTooBigError(it)
-                is RoomDetailViewEvents.DownloadFileState                -> handleDownloadFileState(it)
-                is RoomDetailViewEvents.JoinRoomCommandSuccess           -> handleJoinedToAnotherRoom(it)
-                is RoomDetailViewEvents.SendMessageResult                -> renderSendMessageResult(it)
-                is RoomDetailViewEvents.ShowE2EErrorMessage              -> displayE2eError(it.withHeldCode)
-                RoomDetailViewEvents.DisplayPromptForIntegrationManager  -> displayPromptForIntegrationManager()
-                is RoomDetailViewEvents.OpenStickerPicker                -> openStickerPicker(it)
+                is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
+                is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
+                is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
+                is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
+                is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
+                is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
+                is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
+                is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
+                is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
+                is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
+                is RoomDetailViewEvents.ShowE2EErrorMessage -> displayE2eError(it.withHeldCode)
+                RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager()
+                is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it)
                 is RoomDetailViewEvents.DisplayEnableIntegrationsWarning -> displayDisabledIntegrationDialog()
-                is RoomDetailViewEvents.OpenIntegrationManager           -> openIntegrationManager()
-                is RoomDetailViewEvents.OpenFile                         -> startOpenFileIntent(it)
-                RoomDetailViewEvents.OpenActiveWidgetBottomSheet         -> onViewWidgetsClicked()
-                is RoomDetailViewEvents.ShowInfoOkDialog                 -> showDialogWithMessage(it.message)
-                is RoomDetailViewEvents.JoinJitsiConference              -> joinJitsiRoom(it.widget, it.withVideo)
-                RoomDetailViewEvents.ShowWaitingView                     -> vectorBaseActivity.showWaitingView()
-                RoomDetailViewEvents.HideWaitingView                     -> vectorBaseActivity.hideWaitingView()
-                is RoomDetailViewEvents.RequestNativeWidgetPermission    -> requestNativeWidgetPermission(it)
-                is RoomDetailViewEvents.OpenRoom                         -> handleOpenRoom(it)
-                RoomDetailViewEvents.OpenInvitePeople                    -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
-                RoomDetailViewEvents.OpenSetRoomAvatarDialog             -> galleryOrCameraDialogHelper.show()
-                RoomDetailViewEvents.OpenRoomSettings                    -> handleOpenRoomSettings()
-                is RoomDetailViewEvents.ShowRoomAvatarFullScreen         -> it.matrixItem?.let { item ->
+                is RoomDetailViewEvents.OpenIntegrationManager -> openIntegrationManager()
+                is RoomDetailViewEvents.OpenFile -> startOpenFileIntent(it)
+                RoomDetailViewEvents.OpenActiveWidgetBottomSheet -> onViewWidgetsClicked()
+                is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
+                is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
+                RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView()
+                RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
+                is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
+                is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
+                RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
+                RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
+                RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
+                is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
                     navigator.openBigImageViewer(requireActivity(), it.view, item)
                 }
             }.exhaustive
@@ -525,14 +524,14 @@ class RoomDetailFragment @Inject constructor(
 
     private fun handleShareData() {
         when (val sharedData = roomDetailArgs.sharedData) {
-            is SharedData.Text        -> {
+            is SharedData.Text -> {
                 roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true))
             }
             is SharedData.Attachments -> {
                 // open share edition
                 onContentAttachmentsReady(sharedData.attachmentData)
             }
-            null                      -> Timber.v("No share data to process")
+            null -> Timber.v("No share data to process")
         }.exhaustive
     }
 
@@ -657,8 +656,8 @@ class RoomDetailFragment @Inject constructor(
         withState(roomDetailViewModel) { state ->
             // Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions
             val callButtonsEnabled = when (state.asyncRoomSummary.invoke()?.joinedMembersCount) {
-                1    -> false
-                2    -> state.isAllowedToStartWebRTCCall
+                1 -> false
+                2 -> state.isAllowedToStartWebRTCCall
                 else -> state.isAllowedToManageWidgets
             }
             setOf(R.id.voice_call, R.id.video_call).forEach {
@@ -688,36 +687,36 @@ class RoomDetailFragment @Inject constructor(
 
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         return when (item.itemId) {
-            R.id.invite              -> {
+            R.id.invite -> {
                 navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
                 true
             }
-            R.id.timeline_setting    -> {
+            R.id.timeline_setting -> {
                 navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
                 true
             }
-            R.id.resend_all          -> {
+            R.id.resend_all -> {
                 roomDetailViewModel.handle(RoomDetailAction.ResendAll)
                 true
             }
-            R.id.open_matrix_apps    -> {
+            R.id.open_matrix_apps -> {
                 roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
                 true
             }
             R.id.voice_call,
-            R.id.video_call          -> {
+            R.id.video_call -> {
                 handleCallRequest(item)
                 true
             }
-            R.id.hangup_call         -> {
+            R.id.hangup_call -> {
                 roomDetailViewModel.handle(RoomDetailAction.EndCall)
                 true
             }
-            R.id.search              -> {
+            R.id.search -> {
                 handleSearchAction()
                 true
             }
-            else                     -> super.onOptionsItemSelected(item)
+            else                  -> super.onOptionsItemSelected(item)
         }
     }
 
@@ -733,7 +732,7 @@ class RoomDetailFragment @Inject constructor(
         val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState
         val isVideoCall = item.itemId == R.id.video_call
         when (roomSummary.joinedMembersCount) {
-            1    -> {
+            1 -> {
                 val pendingInvite = roomSummary.invitedMembersCount ?: 0 > 0
                 if (pendingInvite) {
                     // wait for other to join
@@ -743,7 +742,7 @@ class RoomDetailFragment @Inject constructor(
                     showDialogWithMessage(getString(R.string.cannot_call_yourself))
                 }
             }
-            2    -> {
+            2 -> {
                 val activeCall = sharedCallActionViewModel.activeCall.value
                 if (activeCall != null) {
                     // resume existing if same room, if not prompt to kill and then restart new call?
@@ -924,9 +923,9 @@ class RoomDetailFragment @Inject constructor(
         when (roomDetailPendingAction) {
             is RoomDetailPendingAction.JumpToReadReceipt ->
                 roomDetailViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
-            is RoomDetailPendingAction.MentionUser       ->
+            is RoomDetailPendingAction.MentionUser ->
                 insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
-            is RoomDetailPendingAction.OpenOrCreateDm    ->
+            is RoomDetailPendingAction.OpenOrCreateDm ->
                 roomDetailViewModel.handle(RoomDetailAction.OpenOrCreateDm(roomDetailPendingAction.userId))
         }.exhaustive
     }
@@ -1069,9 +1068,9 @@ class RoomDetailFragment @Inject constructor(
             withState(roomDetailViewModel) {
                 val showJumpToUnreadBanner = when (it.unreadState) {
                     UnreadState.Unknown,
-                    UnreadState.HasNoUnread            -> false
+                    UnreadState.HasNoUnread -> false
                     is UnreadState.ReadMarkerNotLoaded -> true
-                    is UnreadState.HasUnread           -> {
+                    is UnreadState.HasUnread -> {
                         if (it.canShowJumpToReadMarker) {
                             val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
                             val positionOfReadMarker = timelineEventController.getPositionOfReadMarker()
@@ -1280,7 +1279,7 @@ class RoomDetailFragment @Inject constructor(
                 navigator.openRoom(vectorBaseActivity, async())
                 vectorBaseActivity.finish()
             }
-            is Fail    -> {
+            is Fail -> {
                 vectorBaseActivity.hideWaitingView()
                 vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error))
             }
@@ -1289,19 +1288,19 @@ class RoomDetailFragment @Inject constructor(
 
     private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) {
         when (sendMessageResult) {
-            is RoomDetailViewEvents.SlashCommandHandled        -> {
+            is RoomDetailViewEvents.SlashCommandHandled -> {
                 sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
             }
-            is RoomDetailViewEvents.SlashCommandError          -> {
+            is RoomDetailViewEvents.SlashCommandError -> {
                 displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
             }
-            is RoomDetailViewEvents.SlashCommandUnknown        -> {
+            is RoomDetailViewEvents.SlashCommandUnknown -> {
                 displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command))
             }
-            is RoomDetailViewEvents.SlashCommandResultOk       -> {
+            is RoomDetailViewEvents.SlashCommandResultOk -> {
                 updateComposerText("")
             }
-            is RoomDetailViewEvents.SlashCommandResultError    -> {
+            is RoomDetailViewEvents.SlashCommandResultError -> {
                 displayCommandError(errorFormatter.toHumanReadable(sendMessageResult.throwable))
             }
             is RoomDetailViewEvents.SlashCommandNotImplemented -> {
@@ -1323,7 +1322,7 @@ class RoomDetailFragment @Inject constructor(
     private fun displayE2eError(withHeldCode: WithHeldCode?) {
         val msgId = when (withHeldCode) {
             WithHeldCode.BLACKLISTED -> R.string.crypto_error_withheld_blacklisted
-            WithHeldCode.UNVERIFIED  -> R.string.crypto_error_withheld_unverified
+            WithHeldCode.UNVERIFIED -> R.string.crypto_error_withheld_unverified
             WithHeldCode.UNAUTHORISED,
             WithHeldCode.UNAVAILABLE -> R.string.crypto_error_withheld_generic
             else                     -> R.string.notice_crypto_unable_to_decrypt_friendly_desc
@@ -1375,7 +1374,7 @@ class RoomDetailFragment @Inject constructor(
 
     private fun displayRoomDetailActionSuccess(result: RoomDetailViewEvents.ActionSuccess) {
         when (val data = result.action) {
-            is RoomDetailAction.ReportContent             -> {
+            is RoomDetailAction.ReportContent -> {
                 when {
                     data.spam          -> {
                         AlertDialog.Builder(requireActivity())
@@ -1412,7 +1411,7 @@ class RoomDetailFragment @Inject constructor(
                     }
                 }
             }
-            is RoomDetailAction.RequestVerification       -> {
+            is RoomDetailAction.RequestVerification -> {
                 Timber.v("## SAS RequestVerification action")
                 VerificationBottomSheet.withArgs(
                         roomDetailArgs.roomId,
@@ -1427,7 +1426,7 @@ class RoomDetailFragment @Inject constructor(
                         data.transactionId
                 ).show(parentFragmentManager, "REQ")
             }
-            is RoomDetailAction.ResumeVerification        -> {
+            is RoomDetailAction.ResumeVerification -> {
                 val otherUserId = data.otherUserId ?: return
                 VerificationBottomSheet().apply {
                     arguments = Bundle().apply {
@@ -1571,11 +1570,11 @@ class RoomDetailFragment @Inject constructor(
             is MessageVerificationRequestContent -> {
                 roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
             }
-            is MessageWithAttachmentContent      -> {
+            is MessageWithAttachmentContent -> {
                 val action = RoomDetailAction.DownloadOrOpen(informationData.eventId, informationData.senderId, messageContent)
                 roomDetailViewModel.handle(action)
             }
-            is EncryptedEventContent             -> {
+            is EncryptedEventContent -> {
                 roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
             }
         }
@@ -1716,75 +1715,75 @@ class RoomDetailFragment @Inject constructor(
 
     private fun handleActions(action: EventSharedAction) {
         when (action) {
-            is EventSharedAction.OpenUserProfile            -> {
+            is EventSharedAction.OpenUserProfile -> {
                 openRoomMemberProfile(action.userId)
             }
-            is EventSharedAction.AddReaction                -> {
+            is EventSharedAction.AddReaction -> {
                 emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
             }
-            is EventSharedAction.ViewReactions              -> {
+            is EventSharedAction.ViewReactions -> {
                 ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
                         .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
             }
-            is EventSharedAction.Copy                       -> {
+            is EventSharedAction.Copy -> {
                 // I need info about the current selected message :/
                 copyToClipboard(requireContext(), action.content, false)
                 showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
             }
-            is EventSharedAction.Redact                     -> {
+            is EventSharedAction.Redact -> {
                 promptConfirmationToRedactEvent(action)
             }
-            is EventSharedAction.Share                      -> {
+            is EventSharedAction.Share -> {
                 onShareActionClicked(action)
             }
-            is EventSharedAction.Save                       -> {
+            is EventSharedAction.Save -> {
                 onSaveActionClicked(action)
             }
-            is EventSharedAction.ViewEditHistory            -> {
+            is EventSharedAction.ViewEditHistory -> {
                 onEditedDecorationClicked(action.messageInformationData)
             }
-            is EventSharedAction.ViewSource                 -> {
+            is EventSharedAction.ViewSource -> {
                 JSonViewerDialog.newInstance(
                         action.content,
                         -1,
                         createJSonViewerStyleProvider(colorProvider)
                 ).show(childFragmentManager, "JSON_VIEWER")
             }
-            is EventSharedAction.ViewDecryptedSource        -> {
+            is EventSharedAction.ViewDecryptedSource -> {
                 JSonViewerDialog.newInstance(
                         action.content,
                         -1,
                         createJSonViewerStyleProvider(colorProvider)
                 ).show(childFragmentManager, "JSON_VIEWER")
             }
-            is EventSharedAction.QuickReact                 -> {
+            is EventSharedAction.QuickReact -> {
                 // eventId,ClickedOn,Add
                 roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
             }
-            is EventSharedAction.Edit                       -> {
+            is EventSharedAction.Edit -> {
                 roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString()))
             }
-            is EventSharedAction.Quote                      -> {
+            is EventSharedAction.Quote -> {
                 roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString()))
             }
-            is EventSharedAction.Reply                      -> {
+            is EventSharedAction.Reply -> {
                 roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString()))
             }
-            is EventSharedAction.CopyPermalink              -> {
+            is EventSharedAction.CopyPermalink -> {
                 val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
                 copyToClipboard(requireContext(), permalink, false)
                 showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
             }
-            is EventSharedAction.Resend                     -> {
+            is EventSharedAction.Resend -> {
                 roomDetailViewModel.handle(RoomDetailAction.ResendMessage(action.eventId))
             }
-            is EventSharedAction.Remove                     -> {
+            is EventSharedAction.Remove -> {
                 roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId))
             }
-            is EventSharedAction.Cancel                     -> {
+            is EventSharedAction.Cancel -> {
                 roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId))
             }
-            is EventSharedAction.ReportContentSpam          -> {
+            is EventSharedAction.ReportContentSpam -> {
                 roomDetailViewModel.handle(RoomDetailAction.ReportContent(
                         action.eventId, action.senderId, "This message is spam", spam = true))
             }
@@ -1792,22 +1791,22 @@ class RoomDetailFragment @Inject constructor(
                 roomDetailViewModel.handle(RoomDetailAction.ReportContent(
                         action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
             }
-            is EventSharedAction.ReportContentCustom        -> {
+            is EventSharedAction.ReportContentCustom -> {
                 promptReasonToReportContent(action)
             }
-            is EventSharedAction.IgnoreUser                 -> {
+            is EventSharedAction.IgnoreUser -> {
                 action.senderId?.let { askConfirmationToIgnoreUser(it) }
             }
-            is EventSharedAction.OnUrlClicked               -> {
+            is EventSharedAction.OnUrlClicked -> {
                 onUrlClicked(action.url, action.title)
             }
-            is EventSharedAction.OnUrlLongClicked           -> {
+            is EventSharedAction.OnUrlLongClicked -> {
                 onUrlLongClicked(action.url)
             }
-            is EventSharedAction.ReRequestKey               -> {
+            is EventSharedAction.ReRequestKey -> {
                 roomDetailViewModel.handle(RoomDetailAction.ReRequestKeys(action.eventId))
             }
-            is EventSharedAction.UseKeyBackup               -> {
+            is EventSharedAction.UseKeyBackup -> {
                 context?.let {
                     startActivity(KeysBackupRestoreActivity.intent(it))
                 }
@@ -1947,10 +1946,10 @@ class RoomDetailFragment @Inject constructor(
 
     private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
         when (type) {
-            AttachmentTypeSelectorView.Type.CAMERA  -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher)
-            AttachmentTypeSelectorView.Type.FILE    -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher)
+            AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher)
+            AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher)
             AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentImageActivityResultLauncher)
-            AttachmentTypeSelectorView.Type.AUDIO   -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher)
+            AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher)
             AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher)
             AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment)
         }.exhaustive
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index fbf0ed9085..ecac5b742c 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -52,6 +52,8 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.home.room.detail.RoomDetailArgs
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
+import org.matrix.android.sdk.api.session.call.MxCall
+import org.matrix.android.sdk.api.util.MatrixItem
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -269,19 +271,19 @@ class NotificationUtils @Inject constructor(private val context: Context,
      * @param roomName the room name in which the call is pending.
      * @param matrixId the matrix id
      * @param callId   the call id.
+     * @param fromBg true if the app is in background when posting the notification
      * @return the call notification.
      */
     @SuppressLint("NewApi")
-    fun buildIncomingCallNotification(isVideo: Boolean,
-                                      otherUserId: String,
-                                      roomId: String,
-                                      callId: String): Notification {
+    fun buildIncomingCallNotification(mxCall: MxCall,
+                                      title: String,
+                                      fromBg: Boolean): Notification {
         val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
-
-        val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
-                .setContentTitle(ensureTitleNotEmpty(otherUserId))
+        val notificationChannel = if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
+        val builder = NotificationCompat.Builder(context, notificationChannel)
+                .setContentTitle(ensureTitleNotEmpty(title))
                 .apply {
-                    if (isVideo) {
+                    if (mxCall.isVideoCall) {
                         setContentText(stringProvider.getString(R.string.incoming_video_call))
                     } else {
                         setContentText(stringProvider.getString(R.string.incoming_voice_call))
@@ -300,15 +302,11 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         val contentIntent = VectorCallActivity.newIntent(
                 context = context,
-                callId = callId,
-                roomId = roomId,
-                otherUserId = otherUserId,
-                isIncomingCall = true,
-                isVideoCall = isVideo,
+                mxCall = mxCall,
                 mode = VectorCallActivity.INCOMING_RINGING
         ).apply {
             flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
-            data = Uri.parse("foobar://$callId")
+            data = Uri.parse("foobar://${mxCall.callId}")
         }
         val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
 
@@ -316,20 +314,16 @@ class NotificationUtils @Inject constructor(private val context: Context,
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
                 .addNextIntent(VectorCallActivity.newIntent(
                         context = context,
-                        callId = callId,
-                        roomId = roomId,
-                        otherUserId = otherUserId,
-                        isIncomingCall = true,
-                        isVideoCall = isVideo,
+                        mxCall = mxCall,
                         mode = VectorCallActivity.INCOMING_ACCEPT)
                 )
                 .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
 
-        val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
+        val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
 
         builder.addAction(
                 NotificationCompat.Action(
-                        R.drawable.ic_call,
+                        R.drawable.ic_call_answer,
                         // IconCompat.createWithResource(applicationContext, R.drawable.ic_call)
                         // .setTint(ContextCompat.getColor(applicationContext, R.color.riotx_positive_accent)),
                         context.getString(R.string.call_notification_answer),
@@ -339,7 +333,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         builder.addAction(
                 NotificationCompat.Action(
-                        IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+                        IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
                         context.getString(R.string.call_notification_reject),
                         rejectCallPendingIntent)
         )
@@ -349,14 +343,11 @@ class NotificationUtils @Inject constructor(private val context: Context,
         return builder.build()
     }
 
-    fun buildOutgoingRingingCallNotification(isVideo: Boolean,
-                                             otherUserId: String,
-                                             roomId: String,
-                                             callId: String): Notification {
+    fun buildOutgoingRingingCallNotification(mxCall: MxCall,
+                                             title: String): Notification {
         val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
-
         val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
-                .setContentTitle(ensureTitleNotEmpty(otherUserId))
+                .setContentTitle(ensureTitleNotEmpty(title))
                 .apply {
                     setContentText(stringProvider.getString(R.string.call_ring))
                 }
@@ -367,18 +358,14 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         val contentIntent = VectorCallActivity.newIntent(
                 context = context,
-                callId = callId,
-                roomId = roomId,
-                otherUserId = otherUserId,
-                isIncomingCall = true,
-                isVideoCall = isVideo,
+                mxCall = mxCall,
                 mode = null).apply {
             flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
-            data = Uri.parse("foobar://$callId")
+            data = Uri.parse("foobar://$mxCall.callId")
         }
         val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
 
-        val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
+        val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
 
         builder.addAction(
                 NotificationCompat.Action(
@@ -402,16 +389,13 @@ class NotificationUtils @Inject constructor(private val context: Context,
      * @return the call notification.
      */
     @SuppressLint("NewApi")
-    fun buildPendingCallNotification(isVideo: Boolean,
-                                     roomName: String,
-                                     roomId: String,
-                                     matrixId: String,
-                                     callId: String,
+    fun buildPendingCallNotification(mxCall: MxCall,
+                                     title: String,
                                      fromBg: Boolean = false): Notification {
         val builder = NotificationCompat.Builder(context, if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
-                .setContentTitle(ensureTitleNotEmpty(roomName))
+                .setContentTitle(ensureTitleNotEmpty(title))
                 .apply {
-                    if (isVideo) {
+                    if (mxCall.isVideoCall) {
                         setContentText(stringProvider.getString(R.string.video_call_in_progress))
                     } else {
                         setContentText(stringProvider.getString(R.string.call_in_progress))
@@ -425,7 +409,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
             builder.setOngoing(true)
         }
 
-        val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
+        val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
 
         builder.addAction(
                 NotificationCompat.Action(
@@ -436,8 +420,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         val contentPendingIntent = TaskStackBuilder.create(context)
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
-                // TODO other userId
-                .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, null))
+                .addNextIntent(VectorCallActivity.newIntent(context, mxCall, null))
                 .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
 
         builder.setContentIntent(contentPendingIntent)
@@ -462,7 +445,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
      * Build a temporary (because service will be stopped just after) notification for the CallService, when a call is ended
      */
     fun buildCallEndedNotification(): Notification {
-        return NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
+        return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
                 .setContentTitle(stringProvider.getString(R.string.call_ended))
                 .setSmallIcon(R.drawable.ic_material_call_end_grey)
                 .setCategory(NotificationCompat.CATEGORY_CALL)
diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
new file mode 100644
index 0000000000..add7e9c083
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.app.features.popup
+
+import android.app.Activity
+import android.view.View
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import im.vector.app.R
+import im.vector.app.features.home.AvatarRenderer
+import org.matrix.android.sdk.api.util.MatrixItem
+
+class IncomingCallAlert(uid: String,
+                        override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
+) : DefaultVectorAlert(uid, "", "", 0, shouldBeDisplayedIn) {
+
+    override val layoutRes = R.layout.alerter_incoming_call_layout
+    override var colorAttribute: Int? = R.attr.riotx_alerter_background
+
+    class ViewBinder(private val matrixItem: MatrixItem?,
+                     private val avatarRenderer: AvatarRenderer,
+                     private val isVideoCall: Boolean,
+                     private val onAccept: () -> Unit,
+                     private val onReject: () -> Unit)
+        : VectorAlert.ViewBinder {
+
+        override fun bind(view: View) {
+            val callKind = if (isVideoCall) {
+                R.string.action_video_call
+            } else {
+                R.string.action_voice_call
+            }
+            view.findViewById<TextView>(R.id.incomingCallKindView).setText(callKind)
+            view.findViewById<TextView>(R.id.incomingCallNameView).text = matrixItem?.getBestName()
+            view.findViewById<ImageView>(R.id.incomingCallAvatar)?.let { imageView ->
+                matrixItem?.let { avatarRenderer.render(it, imageView) }
+            }
+            view.findViewById<ImageView>(R.id.incomingCallAcceptView).setOnClickListener {
+                onAccept()
+            }
+            view.findViewById<ImageView>(R.id.incomingCallRejectView).setOnClickListener {
+                onReject()
+            }
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
index b2257b250a..34cee15707 100644
--- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
@@ -21,14 +21,11 @@ import android.os.Build
 import android.os.Handler
 import android.os.Looper
 import android.view.View
-import android.widget.ImageView
 import com.tapadoo.alerter.Alerter
 import com.tapadoo.alerter.OnHideAlertListener
-import dagger.Lazy
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.utils.isAnimationDisabled
-import im.vector.app.features.home.AvatarRenderer
 import im.vector.app.features.pin.PinActivity
 import im.vector.app.features.themes.ThemeUtils
 import timber.log.Timber
@@ -41,7 +38,7 @@ import javax.inject.Singleton
  * Alerts are stacked and will be displayed sequentially
  */
 @Singleton
-class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<AvatarRenderer>) {
+class PopupAlertManager @Inject constructor() {
 
     private var weakCurrentActivity: WeakReference<Activity>? = null
     private var currentAlerter: VectorAlert? = null
@@ -191,17 +188,13 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
         val noAnimation = !animate || isAnimationDisabled(activity)
 
         alert.weakCurrentActivity = WeakReference(activity)
-        val alerter = if (alert is VerificationVectorAlert) Alerter.create(activity, R.layout.alerter_verification_layout)
-        else Alerter.create(activity)
+        val alerter = Alerter.create(activity, alert.layoutRes)
 
         alerter.setTitle(alert.title)
                 .setText(alert.description)
                 .also { al ->
-                    if (alert is VerificationVectorAlert) {
-                        val tvCustomView = al.getLayoutContainer()
-                        tvCustomView?.findViewById<ImageView>(R.id.ivUserAvatar)?.let { imageView ->
-                            alert.matrixItem?.let { avatarRenderer.get().render(it, imageView) }
-                        }
+                    al.getLayoutContainer()?.also {
+                        alert.viewBinder?.bind(it)
                     }
                 }
                 .apply {
@@ -251,6 +244,8 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
                 .apply {
                     if (alert.colorInt != null) {
                         setBackgroundColorInt(alert.colorInt!!)
+                    } else if (alert.colorAttribute != null) {
+                        setBackgroundColorInt(ThemeUtils.getColor(activity, alert.colorAttribute!!))
                     } else {
                         setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
                     }
diff --git a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
index 00f80240c5..eadc09aa7c 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
@@ -17,12 +17,18 @@
 package im.vector.app.features.popup
 
 import android.app.Activity
+import android.view.View
+import androidx.annotation.AttrRes
 import androidx.annotation.ColorInt
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
+import androidx.annotation.LayoutRes
+import im.vector.app.R
+import im.vector.app.features.home.AvatarRenderer
 import org.matrix.android.sdk.api.util.MatrixItem
 import java.lang.ref.WeakReference
 
+
 interface VectorAlert {
     val uid: String
     val title: String
@@ -47,22 +53,34 @@ interface VectorAlert {
         actions.add(Button(title, action, autoClose))
     }
 
+    var viewBinder: ViewBinder?
+
+    val layoutRes: Int
+
     var colorRes: Int?
 
     var colorInt: Int?
+
+    var colorAttribute: Int?
+
+    interface ViewBinder {
+        fun bind(view: View)
+    }
+
 }
 
 /**
  * Dataclass to describe an important alert with actions.
  */
-open class DefaultVectorAlert(override val uid: String,
-                              override val title: String,
-                              override val description: String,
-                              @DrawableRes override val iconId: Int?,
-                              /**
-                               * Alert are displayed by default, but let this lambda return false to prevent displaying
-                               */
-                              override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
+open class DefaultVectorAlert(
+        override val uid: String,
+        override val title: String,
+        override val description: String,
+        @DrawableRes override val iconId: Int?,
+        /**
+         * Alert are displayed by default, but let this lambda return false to prevent displaying
+         */
+        override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true },
 ) : VectorAlert {
 
     // will be set by manager, and accessible by actions at runtime
@@ -76,26 +94,19 @@ open class DefaultVectorAlert(override val uid: String,
     /** If this timestamp is after current time, this alert will be skipped */
     override var expirationTimestamp: Long? = null
 
-    override fun addButton(title: String, action: Runnable, autoClose: Boolean) {
-        actions.add(VectorAlert.Button(title, action, autoClose))
-    }
+    @LayoutRes
+    override val layoutRes = R.layout.alerter_alert_default_layout
 
     @ColorRes
     override var colorRes: Int? = null
 
     @ColorInt
     override var colorInt: Int? = null
+
+    @AttrRes
+    override var colorAttribute: Int? = null
+
+    override var viewBinder: VectorAlert.ViewBinder? = null
+
 }
 
-class VerificationVectorAlert(uid: String,
-                              title: String,
-                              override val description: String,
-                              @DrawableRes override val iconId: Int?,
-                              /**
-                               * Alert are displayed by default, but let this lambda return false to prevent displaying
-                               */
-                              override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true },
-                              val matrixItem: MatrixItem?
-) : DefaultVectorAlert(
-        uid, title, description, iconId, shouldBeDisplayedIn
-)
diff --git a/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
new file mode 100644
index 0000000000..3c02548725
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.app.features.popup
+
+import android.app.Activity
+import android.view.View
+import android.widget.ImageView
+import androidx.annotation.DrawableRes
+import im.vector.app.R
+import im.vector.app.features.home.AvatarRenderer
+import org.matrix.android.sdk.api.util.MatrixItem
+
+class VerificationVectorAlert(uid: String,
+                              title: String,
+                              override val description: String,
+                              @DrawableRes override val iconId: Int?,
+                              /**
+                               * Alert are displayed by default, but let this lambda return false to prevent displaying
+                               */
+                              override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
+) : DefaultVectorAlert(
+        uid, title, description, iconId, shouldBeDisplayedIn
+) {
+    override val layoutRes = R.layout.alerter_verification_layout
+
+    class ViewBinder(private val matrixItem: MatrixItem?,
+                     private val avatarRenderer: AvatarRenderer)
+        : VectorAlert.ViewBinder {
+
+        override fun bind(view: View) {
+            view.findViewById<ImageView>(R.id.ivUserAvatar)?.let { imageView ->
+                matrixItem?.let { avatarRenderer.render(it, imageView) }
+            }
+        }
+    }
+
+
+
+}
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/alerter_incoming_call_layout.xml b/vector/src/main/res/layout/alerter_incoming_call_layout.xml
new file mode 100644
index 0000000000..6cbcfd10d7
--- /dev/null
+++ b/vector/src/main/res/layout/alerter_incoming_call_layout.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipToPadding="false"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:style="@style/AlertStyle"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <ImageView
+        android:id="@+id/incomingCallAvatar"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:contentDescription="@string/call_notification_answer"
+        android:layout_margin="12dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:src="@tools:sample/avatars" />
+
+    <TextView
+        android:id="@+id/incomingCallNameView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textColor="?riotx_text_primary"
+        android:textSize="15sp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toStartOf="@+id/incomingCallRejectView"
+        app:layout_constraintStart_toEndOf="@id/incomingCallAvatar"
+        app:layout_constraintTop_toTopOf="@id/incomingCallAvatar"
+        tools:text="@sample/matrix.json/data/displayName" />
+
+    <TextView
+        android:id="@+id/incomingCallKindView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="3dp"
+        android:layout_marginEnd="8dp"
+        android:ellipsize="end"
+        android:textColor="?riotx_text_secondary"
+        android:textSize="15sp"
+        android:maxLines="1"
+        app:layout_constraintEnd_toStartOf="@+id/incomingCallRejectView"
+        app:layout_constraintStart_toStartOf="@id/incomingCallNameView"
+        app:layout_constraintTop_toBottomOf="@id/incomingCallNameView"
+        tools:text="@string/action_voice_call" />
+
+    <ImageView
+        android:id="@+id/incomingCallAcceptView"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:background="@drawable/oval_positive"
+        android:clickable="true"
+        android:contentDescription="@string/call_notification_answer"
+        android:focusable="true"
+        android:layout_marginEnd="12dp"
+        android:padding="8dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:src="@drawable/ic_call_answer" />
+
+    <ImageView
+        android:id="@+id/incomingCallRejectView"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:background="@drawable/oval_destructive"
+        android:clickable="true"
+        android:contentDescription="@string/call_notification_reject"
+        android:focusable="true"
+        android:padding="8dp"
+        android:layout_marginEnd="12dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/incomingCallAcceptView"
+        android:src="@drawable/ic_call_hangup" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml
index 33f462c0d1..97cebcc0da 100644
--- a/vector/src/main/res/layout/fragment_room_detail.xml
+++ b/vector/src/main/res/layout/fragment_room_detail.xml
@@ -179,7 +179,7 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/roomToolbar"
-        tools:visibility="visible" />
+        tools:visibility="gone" />
 
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/badgeBarrier"
diff --git a/vector/src/main/res/layout/view_call_controls.xml b/vector/src/main/res/layout/view_call_controls.xml
index 36de855437..35b46e9b9f 100644
--- a/vector/src/main/res/layout/view_call_controls.xml
+++ b/vector/src/main/res/layout/view_call_controls.xml
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    tools:parentTag="android.widget.FrameLayout"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
@@ -200,4 +201,4 @@
     <!--        app:layout_constraintEnd_toEndOf="parent"-->
     <!--        app:layout_constraintTop_toTopOf="parent" />-->
 
-</FrameLayout>
\ No newline at end of file
+</merge>
\ No newline at end of file
diff --git a/vector/src/main/res/values/colors_riotx.xml b/vector/src/main/res/values/colors_riotx.xml
index c7183c3071..493e2dfd5e 100644
--- a/vector/src/main/res/values/colors_riotx.xml
+++ b/vector/src/main/res/values/colors_riotx.xml
@@ -238,9 +238,16 @@
     <color name="riotx_reaction_background_on_dark">#4011BC8A</color>
     <color name="riotx_reaction_background_on_black">#4011BC8A</color>
 
+    <attr name="riotx_alerter_background" format="color" />
+    <color name="riotx_alerter_background_light">#FFF3F8FD</color>
+    <color name="riotx_alerter_background_dark">#FF282C35</color>
+    <color name="riotx_alerter_background_black">#FF282C35</color>
+
     <!-- (color from RiotWeb) -->
     <attr name="riotx_keys_backup_banner_accent_color" format="color" />
     <color name="riotx_keys_backup_banner_accent_color_light">#FFF8E3</color>
     <color name="riotx_keys_backup_banner_accent_color_dark">#22262E</color>
 
+
+
 </resources>
\ No newline at end of file
diff --git a/vector/src/main/res/values/theme_black.xml b/vector/src/main/res/values/theme_black.xml
index 18ced0a071..9221f13cc1 100644
--- a/vector/src/main/res/values/theme_black.xml
+++ b/vector/src/main/res/values/theme_black.xml
@@ -38,6 +38,7 @@
         <item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_black</item>
         <item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_black</item>
         <item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_black</item>
+        <item name="riotx_alerter_background">@color/riotx_alerter_background_black</item>
 
         <item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_black</item>
 
diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml
index cdd5cde488..421ba407a4 100644
--- a/vector/src/main/res/values/theme_dark.xml
+++ b/vector/src/main/res/values/theme_dark.xml
@@ -36,7 +36,7 @@
         <item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_dark</item>
         <item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_dark</item>
         <item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_dark</item>
-
+        <item name="riotx_alerter_background">@color/riotx_alerter_background_dark</item>
         <item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_dark</item>
 
         <item name="riotx_keys_backup_banner_accent_color">@color/riotx_keys_backup_banner_accent_color_dark</item>
diff --git a/vector/src/main/res/values/theme_light.xml b/vector/src/main/res/values/theme_light.xml
index 3c1505bb60..7dff0283d0 100644
--- a/vector/src/main/res/values/theme_light.xml
+++ b/vector/src/main/res/values/theme_light.xml
@@ -37,7 +37,7 @@
         <item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_light</item>
         <item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_light</item>
         <item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_light</item>
-
+        <item name="riotx_alerter_background">@color/riotx_alerter_background_light</item>
         <item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_light</item>
 
         <!-- Material color: Note: this block should be the same in all theme because it references only common colors and ?riotx attributes -->

From 40446c7a310a2c451356d29bd6fe20376bdf3819 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Wed, 9 Dec 2020 11:48:44 +0100
Subject: [PATCH 2/6] VoIP: introduce priority for in-app notification

---
 .../vector/app/core/services/CallService.kt   |  4 --
 .../app/features/popup/IncomingCallAlert.kt   |  1 +
 .../app/features/popup/PopupAlertManager.kt   | 44 ++++++++++++-------
 .../vector/app/features/popup/VectorAlert.kt  |  4 ++
 4 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt
index a0adcca496..1b8345491b 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt
@@ -158,10 +158,6 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
     /**
      * Display a permanent notification when there is an incoming call.
      *
-     * @param session  the session
-     * @param isVideo  true if this is a video call, false for voice call
-     * @param room     the room
-     * @param callId   the callId
      */
     private fun displayIncomingCallNotification(intent: Intent) {
         Timber.v("## VOIP displayIncomingCallNotification $intent")
diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
index add7e9c083..73f9f32ca4 100644
--- a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
@@ -29,6 +29,7 @@ class IncomingCallAlert(uid: String,
                         override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
 ) : DefaultVectorAlert(uid, "", "", 0, shouldBeDisplayedIn) {
 
+    override val priority = PopupAlertManager.INCOMING_CALL_PRIORITY
     override val layoutRes = R.layout.alerter_incoming_call_layout
     override var colorAttribute: Int? = R.attr.riotx_alerter_background
 
diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
index 34cee15707..0b7bf2e2c8 100644
--- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
@@ -35,19 +35,25 @@ import javax.inject.Singleton
 
 /**
  * Responsible of displaying important popup alerts on top of the screen.
- * Alerts are stacked and will be displayed sequentially
+ * Alerts are stacked and will be displayed sequentially but sorted by priority.
+ * So if a new alert is posted with a higher priority than the current one it will show it instead and the current one
+ * will be back in the queue in first position.
  */
 @Singleton
 class PopupAlertManager @Inject constructor() {
 
+    companion object {
+        const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE
+    }
+
     private var weakCurrentActivity: WeakReference<Activity>? = null
     private var currentAlerter: VectorAlert? = null
 
-    private val alertFiFo = mutableListOf<VectorAlert>()
+    private val alertQueue = mutableListOf<VectorAlert>()
 
     fun postVectorAlert(alert: VectorAlert) {
-        synchronized(alertFiFo) {
-            alertFiFo.add(alert)
+        synchronized(alertQueue) {
+            alertQueue.add(alert)
         }
         weakCurrentActivity?.get()?.runOnUiThread {
             displayNextIfPossible()
@@ -55,8 +61,8 @@ class PopupAlertManager @Inject constructor() {
     }
 
     fun cancelAlert(uid: String) {
-        synchronized(alertFiFo) {
-            alertFiFo.listIterator().apply {
+        synchronized(alertQueue) {
+            alertQueue.listIterator().apply {
                 while (this.hasNext()) {
                     val next = this.next()
                     if (next.uid == uid) {
@@ -79,8 +85,8 @@ class PopupAlertManager @Inject constructor() {
      * Cancel all alerts, after a sign out for instance
      */
     fun cancelAll() {
-        synchronized(alertFiFo) {
-            alertFiFo.clear()
+        synchronized(alertQueue) {
+            alertQueue.clear()
         }
 
         // Cancel any displayed alert
@@ -126,15 +132,21 @@ class PopupAlertManager @Inject constructor() {
     }
 
     private fun displayNextIfPossible() {
-        val currentActivity = weakCurrentActivity?.get()
-        if (Alerter.isShowing || currentActivity == null) {
-            // will retry later
-            return
-        }
+        val currentActivity = weakCurrentActivity?.get() ?: return
         val next: VectorAlert?
-        synchronized(alertFiFo) {
-            next = alertFiFo.firstOrNull()
-            if (next != null) alertFiFo.remove(next)
+        synchronized(alertQueue) {
+            next = alertQueue.maxByOrNull { it.priority }
+            // If next alert with highest priority is higher than the current one, we should display it
+            // and add the current one to queue again.
+            if (next != null && next.priority > currentAlerter?.priority ?: Int.MIN_VALUE) {
+                alertQueue.remove(next)
+                currentAlerter?.also {
+                    alertQueue.add(0, it)
+                }
+            } else {
+                // otherwise, we don't do anything
+                return
+            }
         }
         currentAlerter = next
         next?.let {
diff --git a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
index eadc09aa7c..d21755091e 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
@@ -34,6 +34,7 @@ interface VectorAlert {
     val title: String
     val description: String
     val iconId: Int?
+    val priority: Int
     val shouldBeDisplayedIn: ((Activity) -> Boolean)
 
     data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
@@ -83,6 +84,7 @@ open class DefaultVectorAlert(
         override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true },
 ) : VectorAlert {
 
+
     // will be set by manager, and accessible by actions at runtime
     override var weakCurrentActivity: WeakReference<Activity>? = null
 
@@ -97,6 +99,8 @@ open class DefaultVectorAlert(
     @LayoutRes
     override val layoutRes = R.layout.alerter_alert_default_layout
 
+    override val priority: Int = 0
+
     @ColorRes
     override var colorRes: Int? = null
 

From edf4841371579062b89967550c96c43b3bb16141 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Wed, 9 Dec 2020 11:52:48 +0100
Subject: [PATCH 3/6] VoIP: in-app notif add content action

---
 .../main/java/im/vector/app/core/services/CallService.kt   | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt
index 1b8345491b..9fb353736a 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt
@@ -174,10 +174,11 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
                         matrixItem = opponentMatrixItem,
                         avatarRenderer = avatarRenderer,
                         isVideoCall = isVideoCall,
-                        onAccept = { acceptIncomingCall(call) },
+                        onAccept = { showCallScreen(call, VectorCallActivity.INCOMING_ACCEPT) },
                         onReject = { call.endCall() }
                 )
                 dismissedAction = Runnable { call.endCall() }
+                contentAction = Runnable { showCallScreen(call, VectorCallActivity.INCOMING_RINGING) }
             }
             alertManager.postVectorAlert(incomingCallAlert)
         }
@@ -189,11 +190,11 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         startForeground(NOTIFICATION_ID, notification)
     }
 
-    private fun acceptIncomingCall(call: WebRtcCall){
+    private fun showCallScreen(call: WebRtcCall, mode: String) {
         val intent = VectorCallActivity.newIntent(
                 context = this,
                 mxCall = call.mxCall,
-                mode = VectorCallActivity.INCOMING_ACCEPT
+                mode = mode
         )
         startActivity(intent)
     }

From 92fe70c15cfe38053f785a57e9ab6aa492aefd83 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Wed, 9 Dec 2020 16:28:10 +0100
Subject: [PATCH 4/6] VoIP: show in-notif only when necessary

---
 .../java/im/vector/app/core/services/CallService.kt | 13 ++++++++++++-
 .../vector/app/features/popup/IncomingCallAlert.kt  |  1 +
 .../vector/app/features/popup/PopupAlertManager.kt  | 10 ++++++----
 .../im/vector/app/features/popup/VectorAlert.kt     |  3 +++
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt
index 9fb353736a..05875f3cf5 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt
@@ -24,12 +24,15 @@ import android.support.v4.media.session.MediaSessionCompat
 import android.view.KeyEvent
 import androidx.core.content.ContextCompat
 import androidx.media.session.MediaButtonReceiver
+import com.airbnb.mvrx.MvRx
 import im.vector.app.core.extensions.vectorComponent
+import im.vector.app.features.call.CallArgs
 import im.vector.app.features.call.VectorCallActivity
 import im.vector.app.features.call.telecom.CallConnection
 import im.vector.app.features.call.webrtc.WebRtcCall
 import im.vector.app.features.call.webrtc.WebRtcCallManager
 import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.popup.IncomingCallAlert
 import im.vector.app.features.popup.PopupAlertManager
@@ -169,7 +172,15 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         Timber.v("displayIncomingCallNotification : display the dedicated notification")
         if (!fromBg) {
             // Show in-app notification if app is in foreground.
-            val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID).apply {
+            val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID,
+                    shouldBeDisplayedIn = { activity ->
+                        if (activity is RoomDetailActivity) {
+                            call.roomId != activity.currentRoomId
+                        } else if(activity is VectorCallActivity) {
+                            activity.intent.getParcelableExtra<CallArgs>(MvRx.KEY_ARG)?.callId != call.callId
+                        } else true
+                    }
+            ).apply {
                 viewBinder = IncomingCallAlert.ViewBinder(
                         matrixItem = opponentMatrixItem,
                         avatarRenderer = avatarRenderer,
diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
index 73f9f32ca4..ec823bc21f 100644
--- a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
@@ -32,6 +32,7 @@ class IncomingCallAlert(uid: String,
     override val priority = PopupAlertManager.INCOMING_CALL_PRIORITY
     override val layoutRes = R.layout.alerter_incoming_call_layout
     override var colorAttribute: Int? = R.attr.riotx_alerter_background
+    override val dismissOnClick: Boolean = false
 
     class ViewBinder(private val matrixItem: MatrixItem?,
                      private val avatarRenderer: AvatarRenderer,
diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
index 0b7bf2e2c8..db95507436 100644
--- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
@@ -230,17 +230,19 @@ class PopupAlertManager @Inject constructor() {
                             }
                         })
                     }
-                    setOnClickListener(View.OnClickListener { _ ->
+                    setOnClickListener { _ ->
                         alert.contentAction?.let {
-                            currentIsDismissed()
-                            Alerter.hide()
+                            if (alert.dismissOnClick) {
+                                currentIsDismissed()
+                                Alerter.hide()
+                            }
                             try {
                                 it.run()
                             } catch (e: java.lang.Exception) {
                                 Timber.e("## failed to perform action")
                             }
                         }
-                    })
+                    }
                 }
                 .setOnHideListener(OnHideAlertListener {
                     // called when dismissed on swipe
diff --git a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
index d21755091e..4394ef42d2 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
@@ -35,6 +35,7 @@ interface VectorAlert {
     val description: String
     val iconId: Int?
     val priority: Int
+    val dismissOnClick: Boolean
     val shouldBeDisplayedIn: ((Activity) -> Boolean)
 
     data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
@@ -99,6 +100,8 @@ open class DefaultVectorAlert(
     @LayoutRes
     override val layoutRes = R.layout.alerter_alert_default_layout
 
+    override val dismissOnClick: Boolean = true
+
     override val priority: Int = 0
 
     @ColorRes

From fe3c080991699812dc7988d314fd4c38d0b320c8 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Wed, 9 Dec 2020 17:48:34 +0100
Subject: [PATCH 5/6] VoIP: polish notifs and clean code

---
 .../vector/app/core/services/CallService.kt   | 41 +++++++++----------
 .../app/features/call/CallControlsView.kt     |  2 -
 .../IncomingVerificationRequestHandler.kt     |  1 -
 .../vector/app/features/home/HomeActivity.kt  |  3 +-
 .../notifications/NotificationUtils.kt        | 38 ++++++++++-------
 .../app/features/popup/IncomingCallAlert.kt   |  1 -
 .../vector/app/features/popup/VectorAlert.kt  |  9 +---
 .../features/popup/VerificationVectorAlert.kt |  9 ----
 8 files changed, 45 insertions(+), 59 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt
index 05875f3cf5..a5a59dc0ba 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt
@@ -170,29 +170,26 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
         val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
         val opponentMatrixItem = getOpponentMatrixItem(call)
         Timber.v("displayIncomingCallNotification : display the dedicated notification")
-        if (!fromBg) {
-            // Show in-app notification if app is in foreground.
-            val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID,
-                    shouldBeDisplayedIn = { activity ->
-                        if (activity is RoomDetailActivity) {
-                            call.roomId != activity.currentRoomId
-                        } else if(activity is VectorCallActivity) {
-                            activity.intent.getParcelableExtra<CallArgs>(MvRx.KEY_ARG)?.callId != call.callId
-                        } else true
-                    }
-            ).apply {
-                viewBinder = IncomingCallAlert.ViewBinder(
-                        matrixItem = opponentMatrixItem,
-                        avatarRenderer = avatarRenderer,
-                        isVideoCall = isVideoCall,
-                        onAccept = { showCallScreen(call, VectorCallActivity.INCOMING_ACCEPT) },
-                        onReject = { call.endCall() }
-                )
-                dismissedAction = Runnable { call.endCall() }
-                contentAction = Runnable { showCallScreen(call, VectorCallActivity.INCOMING_RINGING) }
-            }
-            alertManager.postVectorAlert(incomingCallAlert)
+        val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID,
+                shouldBeDisplayedIn = { activity ->
+                    if (activity is RoomDetailActivity) {
+                        call.roomId != activity.currentRoomId
+                    } else if (activity is VectorCallActivity) {
+                        activity.intent.getParcelableExtra<CallArgs>(MvRx.KEY_ARG)?.callId != call.callId
+                    } else true
+                }
+        ).apply {
+            viewBinder = IncomingCallAlert.ViewBinder(
+                    matrixItem = opponentMatrixItem,
+                    avatarRenderer = avatarRenderer,
+                    isVideoCall = isVideoCall,
+                    onAccept = { showCallScreen(call, VectorCallActivity.INCOMING_ACCEPT) },
+                    onReject = { call.endCall() }
+            )
+            dismissedAction = Runnable { call.endCall() }
+            contentAction = Runnable { showCallScreen(call, VectorCallActivity.INCOMING_RINGING) }
         }
+        alertManager.postVectorAlert(incomingCallAlert)
         val notification = notificationUtils.buildIncomingCallNotification(
                 mxCall = call.mxCall,
                 title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
index 6e5678d3dc..d0b472b295 100644
--- a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
+++ b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
@@ -22,8 +22,6 @@ import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import android.widget.ImageView
-import android.widget.LinearLayout
-import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.isVisible
 import butterknife.BindView
 import butterknife.ButterKnife
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index e38ab2f6e8..9fca355158 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -23,7 +23,6 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.home.room.detail.RoomDetailArgs
 import im.vector.app.features.popup.PopupAlertManager
 import im.vector.app.features.popup.VerificationVectorAlert
-import im.vector.app.features.themes.ThemeUtils
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
 import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 9fb63c50d3..fac36ee3eb 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -51,7 +51,6 @@ import im.vector.app.features.popup.VerificationVectorAlert
 import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity
-import im.vector.app.features.themes.ThemeUtils
 import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
 import im.vector.app.features.workers.signout.ServerBackupStatusViewState
 import im.vector.app.push.fcm.FcmHelper
@@ -283,7 +282,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
                         uid = "upgradeSecurity",
                         title = getString(titleRes),
                         description = getString(descRes),
-                        iconId = R.drawable.ic_shield_warning,
+                        iconId = R.drawable.ic_shield_warning
                 ).apply {
                     viewBinder = VerificationVectorAlert.ViewBinder(userItem, avatarRenderer)
                     colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index ecac5b742c..74a93fceda 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -30,6 +30,10 @@ import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.net.Uri
 import android.os.Build
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
+import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import androidx.core.app.NotificationCompat
@@ -53,7 +57,6 @@ import im.vector.app.features.home.room.detail.RoomDetailArgs
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
 import org.matrix.android.sdk.api.session.call.MxCall
-import org.matrix.android.sdk.api.util.MatrixItem
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -323,19 +326,19 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         builder.addAction(
                 NotificationCompat.Action(
-                        R.drawable.ic_call_answer,
-                        // IconCompat.createWithResource(applicationContext, R.drawable.ic_call)
-                        // .setTint(ContextCompat.getColor(applicationContext, R.color.riotx_positive_accent)),
-                        context.getString(R.string.call_notification_answer),
-                        answerCallPendingIntent
-                )
+                        IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+                        getActionText(R.string.call_notification_reject, R.color.riotx_notice),
+                        rejectCallPendingIntent)
         )
 
         builder.addAction(
                 NotificationCompat.Action(
-                        IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
-                        context.getString(R.string.call_notification_reject),
-                        rejectCallPendingIntent)
+                        R.drawable.ic_call_answer,
+                        // IconCompat.createWithResource(applicationContext, R.drawable.ic_call)
+                        // .setTint(ContextCompat.getColor(applicationContext, R.color.riotx_positive_accent)),
+                        getActionText(R.string.call_notification_answer, R.color.riotx_positive_accent),
+                        answerCallPendingIntent
+                )
         )
 
         builder.setFullScreenIntent(contentPendingIntent, true)
@@ -369,8 +372,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         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),
+                        IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+                        getActionText(R.string.call_notification_hangup, R.color.riotx_notice),
                         rejectCallPendingIntent)
         )
         builder.setContentIntent(contentPendingIntent)
@@ -413,8 +416,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
 
         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),
+                        IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+                        getActionText(R.string.call_notification_hangup, R.color.riotx_notice),
                         rejectCallPendingIntent)
         )
 
@@ -866,6 +869,13 @@ class NotificationUtils @Inject constructor(private val context: Context,
                 || setting == NotificationManager.INTERRUPTION_FILTER_ALARMS
     }
 
+    private fun getActionText(@StringRes stringRes: Int, @ColorRes colorRes: Int): Spannable {
+        return SpannableString(context.getText(stringRes)).apply {
+            val foregroundColorSpan = ForegroundColorSpan(ContextCompat.getColor(context, colorRes))
+            setSpan(foregroundColorSpan, 0, length, 0)
+        }
+    }
+
     private fun ensureTitleNotEmpty(title: String?): CharSequence {
         if (title.isNullOrBlank()) {
             return stringProvider.getString(R.string.app_name)
diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
index ec823bc21f..e8189eec1d 100644
--- a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
@@ -18,7 +18,6 @@ package im.vector.app.features.popup
 
 import android.app.Activity
 import android.view.View
-import android.widget.Button
 import android.widget.ImageView
 import android.widget.TextView
 import im.vector.app.R
diff --git a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
index 4394ef42d2..d46622ef32 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
@@ -24,11 +24,8 @@ import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import androidx.annotation.LayoutRes
 import im.vector.app.R
-import im.vector.app.features.home.AvatarRenderer
-import org.matrix.android.sdk.api.util.MatrixItem
 import java.lang.ref.WeakReference
 
-
 interface VectorAlert {
     val uid: String
     val title: String
@@ -68,7 +65,6 @@ interface VectorAlert {
     interface ViewBinder {
         fun bind(view: View)
     }
-
 }
 
 /**
@@ -82,10 +78,9 @@ open class DefaultVectorAlert(
         /**
          * Alert are displayed by default, but let this lambda return false to prevent displaying
          */
-        override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true },
+        override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
 ) : VectorAlert {
 
-
     // will be set by manager, and accessible by actions at runtime
     override var weakCurrentActivity: WeakReference<Activity>? = null
 
@@ -114,6 +109,4 @@ open class DefaultVectorAlert(
     override var colorAttribute: Int? = null
 
     override var viewBinder: VectorAlert.ViewBinder? = null
-
 }
-
diff --git a/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
index 3c02548725..2cd9ab59ac 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
@@ -47,13 +47,4 @@ class VerificationVectorAlert(uid: String,
             }
         }
     }
-
-
-
 }
-
-
-
-
-
-

From e4cabe9aa82e37af6e080e5ed014515810a109e4 Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Thu, 10 Dec 2020 17:56:22 +0100
Subject: [PATCH 6/6] VoIP: clean after Benoit review

---
 .../android/sdk/api/session/call/MxCall.kt    |  1 -
 .../internal/session/call/MxCallFactory.kt    |  4 --
 .../internal/session/call/model/MxCallImpl.kt |  1 -
 .../IncomingVerificationRequestHandler.kt     |  4 +-
 .../home/room/detail/RoomDetailFragment.kt    | 58 +++++++++----------
 .../app/features/popup/IncomingCallAlert.kt   |  1 +
 .../app/features/popup/PopupAlertManager.kt   | 23 ++++----
 .../vector/app/features/popup/VectorAlert.kt  |  3 +
 8 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
index 9ff07a21ad..75cff0e709 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt
@@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.room.model.call.SdpType
 import org.matrix.android.sdk.api.util.Optional
 
 interface MxCallDetail {
-    val sessionId: String
     val callId: String
     val isOutgoing: Boolean
     val roomId: String
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
index 355cb4830d..3c258df31a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt
@@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.session.call.MxCall
 import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.di.DeviceId
-import org.matrix.android.sdk.internal.di.SessionId
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.session.call.model.MxCallImpl
 import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
@@ -30,7 +29,6 @@ import java.util.UUID
 import javax.inject.Inject
 
 internal class MxCallFactory @Inject constructor(
-        @SessionId private val sessionId: String,
         @DeviceId private val deviceId: String?,
         private val localEchoEventFactory: LocalEchoEventFactory,
         private val eventSenderProcessor: EventSenderProcessor,
@@ -40,7 +38,6 @@ internal class MxCallFactory @Inject constructor(
     fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? {
         if (content.callId == null) return null
         return MxCallImpl(
-                sessionId = sessionId,
                 callId = content.callId,
                 isOutgoing = false,
                 roomId = roomId,
@@ -58,7 +55,6 @@ internal class MxCallFactory @Inject constructor(
 
     fun createOutgoingCall(roomId: String, opponentUserId: String, isVideoCall: Boolean): MxCall {
         return MxCallImpl(
-                sessionId = sessionId,
                 callId = UUID.randomUUID().toString(),
                 isOutgoing = true,
                 roomId = roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
index 38de28d4b8..9368e94efc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
@@ -40,7 +40,6 @@ import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
 import timber.log.Timber
 
 internal class MxCallImpl(
-        override val sessionId: String,
         override val callId: String,
         override val isOutgoing: Boolean,
         override val roomId: String,
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 9fca355158..aca283a6ab 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -63,7 +63,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
             is VerificationTxState.OnStarted       -> {
                 // Add a notification for every incoming request
                 val user = session?.getUser(tx.otherUserId)
-                val name = user?.displayName ?: tx.otherUserId
+                val name = user?.getBestName() ?: tx.otherUserId
                 val alert = VerificationVectorAlert(
                         uid,
                         context.getString(R.string.sas_incoming_request_notif_title),
@@ -122,7 +122,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
         // For incoming request we should prompt (if not in activity where this request apply)
         if (pr.isIncoming) {
             val user = session?.getUser(pr.otherUserId)
-            val name = user?.displayName ?: pr.otherUserId
+            val name = user?.getBestName() ?: pr.otherUserId
 
             val alert = VerificationVectorAlert(
                     uniqueIdForVerificationRequest(pr),
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 8102a69c68..2964d02c6b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -339,9 +339,9 @@ class RoomDetailFragment @Inject constructor(
             }
             when (mode) {
                 is SendMode.REGULAR -> renderRegularMode(mode.text)
-                is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
-                is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
-                is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
+                is SendMode.EDIT    -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
+                is SendMode.QUOTE   -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
+                is SendMode.REPLY   -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
             }
         }
 
@@ -351,33 +351,33 @@ class RoomDetailFragment @Inject constructor(
 
         roomDetailViewModel.observeViewEvents {
             when (it) {
-                is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
-                is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
-                is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
-                is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
-                is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
-                is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
-                is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
-                is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
-                is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
-                is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
-                is RoomDetailViewEvents.ShowE2EErrorMessage -> displayE2eError(it.withHeldCode)
-                RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager()
-                is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it)
+                is RoomDetailViewEvents.Failure                          -> showErrorInSnackbar(it.throwable)
+                is RoomDetailViewEvents.OnNewTimelineEvents              -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
+                is RoomDetailViewEvents.ActionSuccess                    -> displayRoomDetailActionSuccess(it)
+                is RoomDetailViewEvents.ActionFailure                    -> displayRoomDetailActionFailure(it)
+                is RoomDetailViewEvents.ShowMessage                      -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
+                is RoomDetailViewEvents.NavigateToEvent                  -> navigateToEvent(it)
+                is RoomDetailViewEvents.FileTooBigError                  -> displayFileTooBigError(it)
+                is RoomDetailViewEvents.DownloadFileState                -> handleDownloadFileState(it)
+                is RoomDetailViewEvents.JoinRoomCommandSuccess           -> handleJoinedToAnotherRoom(it)
+                is RoomDetailViewEvents.SendMessageResult                -> renderSendMessageResult(it)
+                is RoomDetailViewEvents.ShowE2EErrorMessage              -> displayE2eError(it.withHeldCode)
+                RoomDetailViewEvents.DisplayPromptForIntegrationManager  -> displayPromptForIntegrationManager()
+                is RoomDetailViewEvents.OpenStickerPicker                -> openStickerPicker(it)
                 is RoomDetailViewEvents.DisplayEnableIntegrationsWarning -> displayDisabledIntegrationDialog()
-                is RoomDetailViewEvents.OpenIntegrationManager -> openIntegrationManager()
-                is RoomDetailViewEvents.OpenFile -> startOpenFileIntent(it)
-                RoomDetailViewEvents.OpenActiveWidgetBottomSheet -> onViewWidgetsClicked()
-                is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
-                is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
-                RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView()
-                RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
-                is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
-                is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
-                RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
-                RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
-                RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
-                is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
+                is RoomDetailViewEvents.OpenIntegrationManager           -> openIntegrationManager()
+                is RoomDetailViewEvents.OpenFile                         -> startOpenFileIntent(it)
+                RoomDetailViewEvents.OpenActiveWidgetBottomSheet         -> onViewWidgetsClicked()
+                is RoomDetailViewEvents.ShowInfoOkDialog                 -> showDialogWithMessage(it.message)
+                is RoomDetailViewEvents.JoinJitsiConference              -> joinJitsiRoom(it.widget, it.withVideo)
+                RoomDetailViewEvents.ShowWaitingView                     -> vectorBaseActivity.showWaitingView()
+                RoomDetailViewEvents.HideWaitingView                     -> vectorBaseActivity.hideWaitingView()
+                is RoomDetailViewEvents.RequestNativeWidgetPermission    -> requestNativeWidgetPermission(it)
+                is RoomDetailViewEvents.OpenRoom                         -> handleOpenRoom(it)
+                RoomDetailViewEvents.OpenInvitePeople                    -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
+                RoomDetailViewEvents.OpenSetRoomAvatarDialog             -> galleryOrCameraDialogHelper.show()
+                RoomDetailViewEvents.OpenRoomSettings                    -> handleOpenRoomSettings()
+                is RoomDetailViewEvents.ShowRoomAvatarFullScreen         -> it.matrixItem?.let { item ->
                     navigator.openBigImageViewer(requireActivity(), it.view, item)
                 }
             }.exhaustive
diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
index e8189eec1d..64e729e54d 100644
--- a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt
@@ -32,6 +32,7 @@ class IncomingCallAlert(uid: String,
     override val layoutRes = R.layout.alerter_incoming_call_layout
     override var colorAttribute: Int? = R.attr.riotx_alerter_background
     override val dismissOnClick: Boolean = false
+    override val isLight: Boolean = true
 
     class ViewBinder(private val matrixItem: MatrixItem?,
                      private val avatarRenderer: AvatarRenderer,
diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
index db95507436..3209fbdca7 100644
--- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt
@@ -22,7 +22,6 @@ import android.os.Handler
 import android.os.Looper
 import android.view.View
 import com.tapadoo.alerter.Alerter
-import com.tapadoo.alerter.OnHideAlertListener
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.utils.isAnimationDisabled
@@ -101,7 +100,9 @@ class PopupAlertManager @Inject constructor() {
         if (currentAlerter != null) {
             weakCurrentActivity?.get()?.let {
                 Alerter.clearCurrent(it)
-                setLightStatusBar()
+                if (currentAlerter?.isLight == false) {
+                    setLightStatusBar()
+                }
             }
         }
         weakCurrentActivity = WeakReference(activity)
@@ -195,8 +196,9 @@ class PopupAlertManager @Inject constructor() {
     }
 
     private fun showAlert(alert: VectorAlert, activity: Activity, animate: Boolean = true) {
-        clearLightStatusBar()
-
+        if (!alert.isLight) {
+            clearLightStatusBar()
+        }
         val noAnimation = !animate || isAnimationDisabled(activity)
 
         alert.weakCurrentActivity = WeakReference(activity)
@@ -218,7 +220,7 @@ class PopupAlertManager @Inject constructor() {
                         setIcon(it)
                     }
                     alert.actions.forEach { action ->
-                        addButton(action.title, R.style.AlerterButton, View.OnClickListener {
+                        addButton(action.title, R.style.AlerterButton) {
                             if (action.autoClose) {
                                 currentIsDismissed()
                                 Alerter.hide()
@@ -228,7 +230,7 @@ class PopupAlertManager @Inject constructor() {
                             } catch (e: java.lang.Exception) {
                                 Timber.e("## failed to perform action")
                             }
-                        })
+                        }
                     }
                     setOnClickListener { _ ->
                         alert.contentAction?.let {
@@ -244,7 +246,7 @@ class PopupAlertManager @Inject constructor() {
                         }
                     }
                 }
-                .setOnHideListener(OnHideAlertListener {
+                .setOnHideListener {
                     // called when dismissed on swipe
                     try {
                         alert.dismissedAction?.run()
@@ -252,7 +254,7 @@ class PopupAlertManager @Inject constructor() {
                         Timber.e("## failed to perform action")
                     }
                     currentIsDismissed()
-                })
+                }
                 .enableSwipeToDismiss()
                 .enableInfiniteDuration(true)
                 .apply {
@@ -270,8 +272,9 @@ class PopupAlertManager @Inject constructor() {
 
     private fun currentIsDismissed() {
         // current alert has been hidden
-        setLightStatusBar()
-
+        if (currentAlerter?.isLight == false) {
+            setLightStatusBar()
+        }
         currentAlerter = null
         Handler(Looper.getMainLooper()).postDelayed({
             displayNextIfPossible()
diff --git a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
index d46622ef32..8b855fa542 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VectorAlert.kt
@@ -33,6 +33,7 @@ interface VectorAlert {
     val iconId: Int?
     val priority: Int
     val dismissOnClick: Boolean
+    val isLight: Boolean
     val shouldBeDisplayedIn: ((Activity) -> Boolean)
 
     data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
@@ -99,6 +100,8 @@ open class DefaultVectorAlert(
 
     override val priority: Int = 0
 
+    override val isLight: Boolean = false
+
     @ColorRes
     override var colorRes: Int? = null