From 76ed775f6f7d730f4aeb22f36b68c8314ab7bbd2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Dec 2020 11:17:49 +0100 Subject: [PATCH] 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() ?: 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()!! -// 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, 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(R.id.incomingCallKindView).setText(callKind) + view.findViewById(R.id.incomingCallNameView).text = matrixItem?.getBestName() + view.findViewById(R.id.incomingCallAvatar)?.let { imageView -> + matrixItem?.let { avatarRenderer.render(it, imageView) } + } + view.findViewById(R.id.incomingCallAcceptView).setOnClickListener { + onAccept() + } + view.findViewById(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) { +class PopupAlertManager @Inject constructor() { private var weakCurrentActivity: WeakReference? = null private var currentAlerter: VectorAlert? = null @@ -191,17 +188,13 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy - if (alert is VerificationVectorAlert) { - val tvCustomView = al.getLayoutContainer() - tvCustomView?.findViewById(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 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(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 @@ + + + + + + + + + + + + + + \ 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" /> - @@ -200,4 +201,4 @@ - \ No newline at end of file + \ 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 @@ #4011BC8A #4011BC8A + + #FFF3F8FD + #FF282C35 + #FF282C35 + #FFF8E3 #22262E + + \ 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 @@ @color/riotx_room_active_widgets_banner_text_black @color/riotx_reaction_background_off_black @color/riotx_reaction_background_on_black + @color/riotx_alerter_background_black @color/riotx_bottom_nav_icon_color_black 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 @@ @color/riotx_room_active_widgets_banner_text_dark @color/riotx_reaction_background_off_dark @color/riotx_reaction_background_on_dark - + @color/riotx_alerter_background_dark @color/riotx_bottom_nav_icon_color_dark @color/riotx_keys_backup_banner_accent_color_dark 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 @@ @color/riotx_room_active_widgets_banner_text_light @color/riotx_reaction_background_off_light @color/riotx_reaction_background_on_light - + @color/riotx_alerter_background_light @color/riotx_bottom_nav_icon_color_light