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