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