mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Merge pull request #2515 from vector-im/feature/fga/voip_notif
Feature/fga/voip notif
This commit is contained in:
commit
afc3c1462e
24 changed files with 534 additions and 371 deletions
|
@ -160,7 +160,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
|||
val content = event.getClearContent().toModel<CallInviteContent>() ?: return
|
||||
val incomingCall = mxCallFactory.createIncomingCall(
|
||||
roomId = event.roomId,
|
||||
senderId = event.senderId,
|
||||
opponentUserId = event.senderId,
|
||||
content = content
|
||||
) ?: return
|
||||
activeCallHandler.addCall(incomingCall)
|
||||
|
|
|
@ -35,7 +35,7 @@ internal class MxCallFactory @Inject constructor(
|
|||
@UserId private val userId: String
|
||||
) {
|
||||
|
||||
fun createIncomingCall(roomId: String, senderId: String, content: CallInviteContent): MxCall? {
|
||||
fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? {
|
||||
if (content.callId == null) return null
|
||||
return MxCallImpl(
|
||||
callId = content.callId,
|
||||
|
@ -43,7 +43,7 @@ internal class MxCallFactory @Inject constructor(
|
|||
roomId = roomId,
|
||||
userId = userId,
|
||||
ourPartyId = deviceId ?: "",
|
||||
opponentUserId = senderId,
|
||||
opponentUserId = opponentUserId,
|
||||
isVideoCall = content.isVideo(),
|
||||
localEchoEventFactory = localEchoEventFactory,
|
||||
eventSenderProcessor = eventSenderProcessor
|
||||
|
@ -53,14 +53,14 @@ 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(
|
||||
callId = UUID.randomUUID().toString(),
|
||||
isOutgoing = true,
|
||||
roomId = roomId,
|
||||
userId = userId,
|
||||
ourPartyId = deviceId ?: "",
|
||||
opponentUserId = otherUserId,
|
||||
opponentUserId = opponentUserId,
|
||||
isVideoCall = isVideoCall,
|
||||
localEchoEventFactory = localEchoEventFactory,
|
||||
eventSenderProcessor = eventSenderProcessor
|
||||
|
|
|
@ -239,7 +239,7 @@ android {
|
|||
productFlavors {
|
||||
gplay {
|
||||
dimension "store"
|
||||
|
||||
isDefault = true
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}"
|
||||
|
||||
resValue "bool", "isGplay", "true"
|
||||
|
|
|
@ -24,10 +24,20 @@ import android.support.v4.media.session.MediaSessionCompat
|
|||
import android.view.KeyEvent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.media.session.MediaButtonReceiver
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import im.vector.app.core.extensions.vectorComponent
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.call.CallArgs
|
||||
import im.vector.app.features.call.VectorCallActivity
|
||||
import im.vector.app.features.call.telecom.CallConnection
|
||||
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.popup.IncomingCallAlert
|
||||
import im.vector.app.features.popup.PopupAlertManager
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
|
@ -39,6 +49,8 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
|
||||
private lateinit var notificationUtils: NotificationUtils
|
||||
private lateinit var callManager: WebRtcCallManager
|
||||
private lateinit var avatarRenderer: AvatarRenderer
|
||||
private lateinit var alertManager: PopupAlertManager
|
||||
|
||||
private var callRingPlayerIncoming: CallRingPlayerIncoming? = null
|
||||
private var callRingPlayerOutgoing: CallRingPlayerOutgoing? = null
|
||||
|
@ -64,6 +76,8 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
super.onCreate()
|
||||
notificationUtils = vectorComponent().notificationUtils()
|
||||
callManager = vectorComponent().webRtcCallManager()
|
||||
avatarRenderer = vectorComponent().avatarRenderer()
|
||||
alertManager = vectorComponent().alertManager()
|
||||
callRingPlayerIncoming = CallRingPlayerIncoming(applicationContext)
|
||||
callRingPlayerOutgoing = CallRingPlayerOutgoing(applicationContext)
|
||||
wiredHeadsetStateReceiver = WiredHeadsetStateReceiver.createAndRegister(this, this)
|
||||
|
@ -111,20 +125,20 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
callRingPlayerOutgoing?.start()
|
||||
displayOutgoingRingingCallNotification(intent)
|
||||
}
|
||||
ACTION_ONGOING_CALL -> {
|
||||
ACTION_ONGOING_CALL -> {
|
||||
callRingPlayerIncoming?.stop()
|
||||
callRingPlayerOutgoing?.stop()
|
||||
displayCallInProgressNotification(intent)
|
||||
}
|
||||
ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
|
||||
ACTION_CALL_CONNECTING -> {
|
||||
ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
|
||||
ACTION_CALL_CONNECTING -> {
|
||||
// lower notification priority
|
||||
displayCallInProgressNotification(intent)
|
||||
// stop ringing
|
||||
callRingPlayerIncoming?.stop()
|
||||
callRingPlayerOutgoing?.stop()
|
||||
}
|
||||
ACTION_ONGOING_CALL_BG -> {
|
||||
ACTION_ONGOING_CALL_BG -> {
|
||||
// there is an ongoing call but call activity is in background
|
||||
displayCallOnGoingInBackground(intent)
|
||||
}
|
||||
|
@ -147,63 +161,61 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
/**
|
||||
* Display a permanent notification when there is an incoming call.
|
||||
*
|
||||
* @param session the session
|
||||
* @param isVideo true if this is a video call, false for voice call
|
||||
* @param room the room
|
||||
* @param callId the callId
|
||||
*/
|
||||
private fun displayIncomingCallNotification(intent: Intent) {
|
||||
Timber.v("## VOIP displayIncomingCallNotification $intent")
|
||||
|
||||
// the incoming call in progress is already displayed
|
||||
// if (!TextUtils.isEmpty(mIncomingCallId)) {
|
||||
// Timber.v("displayIncomingCallNotification : the incoming call in progress is already displayed")
|
||||
// } else if (!TextUtils.isEmpty(mCallIdInProgress)) {
|
||||
// Timber.v("displayIncomingCallNotification : a 'call in progress' notification is displayed")
|
||||
// } else
|
||||
// // if (null == webRtcPeerConnectionManager.currentCall)
|
||||
// {
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID)
|
||||
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||
val call = callManager.getCallById(callId) ?: return
|
||||
val isVideoCall = call.mxCall.isVideoCall
|
||||
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
|
||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||
Timber.v("displayIncomingCallNotification : display the dedicated notification")
|
||||
val incomingCallAlert = IncomingCallAlert(INCOMING_CALL_ALERT_UID,
|
||||
shouldBeDisplayedIn = { activity ->
|
||||
if (activity is RoomDetailActivity) {
|
||||
call.roomId != activity.currentRoomId
|
||||
} else if (activity is VectorCallActivity) {
|
||||
activity.intent.getParcelableExtra<CallArgs>(MvRx.KEY_ARG)?.callId != call.callId
|
||||
} else true
|
||||
}
|
||||
).apply {
|
||||
viewBinder = IncomingCallAlert.ViewBinder(
|
||||
matrixItem = opponentMatrixItem,
|
||||
avatarRenderer = avatarRenderer,
|
||||
isVideoCall = isVideoCall,
|
||||
onAccept = { showCallScreen(call, VectorCallActivity.INCOMING_ACCEPT) },
|
||||
onReject = { call.endCall() }
|
||||
)
|
||||
dismissedAction = Runnable { call.endCall() }
|
||||
contentAction = Runnable { showCallScreen(call, VectorCallActivity.INCOMING_RINGING) }
|
||||
}
|
||||
alertManager.postVectorAlert(incomingCallAlert)
|
||||
val notification = notificationUtils.buildIncomingCallNotification(
|
||||
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
|
||||
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
|
||||
intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
|
||||
callId ?: "")
|
||||
mxCall = call.mxCall,
|
||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
|
||||
fromBg = fromBg
|
||||
)
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
// mIncomingCallId = callId
|
||||
|
||||
// turn the screen on for 3 seconds
|
||||
// if (Matrix.getInstance(VectorApp.getInstance())!!.pushManager.isScreenTurnedOn) {
|
||||
// try {
|
||||
// val pm = getSystemService<PowerManager>()!!
|
||||
// val wl = pm.newWakeLock(
|
||||
// WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or PowerManager.ACQUIRE_CAUSES_WAKEUP,
|
||||
// CallService::class.java.simpleName)
|
||||
// wl.acquire(3000)
|
||||
// wl.release()
|
||||
// } catch (re: RuntimeException) {
|
||||
// Timber.e(re, "displayIncomingCallNotification : failed to turn screen on ")
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// Timber.i("displayIncomingCallNotification : do not display the incoming call notification because there is a pending call")
|
||||
// }
|
||||
private fun showCallScreen(call: WebRtcCall, mode: String) {
|
||||
val intent = VectorCallActivity.newIntent(
|
||||
context = this,
|
||||
mxCall = call.mxCall,
|
||||
mode = mode
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun displayOutgoingRingingCallNotification(intent: Intent) {
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID)
|
||||
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
|
||||
val call = callManager.getCallById(callId) ?: return
|
||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
|
||||
val notification = notificationUtils.buildOutgoingRingingCallNotification(
|
||||
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
|
||||
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
|
||||
intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
|
||||
callId ?: "")
|
||||
mxCall = call.mxCall,
|
||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
||||
)
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
|
@ -213,16 +225,14 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
private fun displayCallInProgressNotification(intent: Intent) {
|
||||
Timber.v("## VOIP displayCallInProgressNotification")
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||
|
||||
val call = callManager.getCallById(callId) ?: return
|
||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||
alertManager.cancelAlert(INCOMING_CALL_ALERT_UID)
|
||||
val notification = notificationUtils.buildPendingCallNotification(
|
||||
intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
|
||||
intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
|
||||
intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
|
||||
intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
|
||||
callId)
|
||||
|
||||
mxCall = call.mxCall,
|
||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId
|
||||
)
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
||||
// mCallIdInProgress = callId
|
||||
}
|
||||
|
||||
|
@ -231,18 +241,15 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
*/
|
||||
private fun displayCallOnGoingInBackground(intent: Intent) {
|
||||
Timber.v("## VOIP displayCallInProgressNotification")
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
|
||||
val call = callManager.getCallById(callId) ?: return
|
||||
val opponentMatrixItem = getOpponentMatrixItem(call)
|
||||
|
||||
val notification = notificationUtils.buildPendingCallNotification(
|
||||
isVideo = intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
|
||||
roomName = intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
|
||||
roomId = intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
|
||||
matrixId = intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
|
||||
callId = callId,
|
||||
mxCall = call.mxCall,
|
||||
title = opponentMatrixItem?.getBestName() ?: call.mxCall.opponentUserId,
|
||||
fromBg = true)
|
||||
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
||||
// mCallIdInProgress = callId
|
||||
}
|
||||
|
||||
|
@ -251,7 +258,7 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
*/
|
||||
private fun hideCallNotifications() {
|
||||
val notification = notificationUtils.buildCallEndedNotification()
|
||||
|
||||
alertManager.cancelAlert(INCOMING_CALL_ALERT_UID)
|
||||
mediaSession?.isActive = false
|
||||
// It's mandatory to startForeground to avoid crash
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
@ -263,9 +270,14 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
connections[callConnection.callId] = callConnection
|
||||
}
|
||||
|
||||
private fun getOpponentMatrixItem(call: WebRtcCall): MatrixItem? {
|
||||
return vectorComponent().currentSession().getUser(call.mxCall.opponentUserId)?.toMatrixItem()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NOTIFICATION_ID = 6480
|
||||
|
||||
private const val INCOMING_CALL_ALERT_UID = "INCOMING_CALL_ALERT_UID"
|
||||
private const val ACTION_INCOMING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_INCOMING_RINGING_CALL"
|
||||
private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.app.core.services.CallService.ACTION_OUTGOING_RINGING_CALL"
|
||||
private const val ACTION_CALL_CONNECTING = "im.vector.app.core.services.CallService.ACTION_CALL_CONNECTING"
|
||||
|
@ -275,44 +287,26 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
// private const val ACTION_ACTIVITY_VISIBLE = "im.vector.app.core.services.CallService.ACTION_ACTIVITY_VISIBLE"
|
||||
// private const val ACTION_STOP_RINGING = "im.vector.app.core.services.CallService.ACTION_STOP_RINGING"
|
||||
|
||||
private const val EXTRA_IS_VIDEO = "EXTRA_IS_VIDEO"
|
||||
private const val EXTRA_ROOM_NAME = "EXTRA_ROOM_NAME"
|
||||
private const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
|
||||
private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
|
||||
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
|
||||
private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG"
|
||||
|
||||
fun onIncomingCallRinging(context: Context,
|
||||
isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String) {
|
||||
callId: String,
|
||||
isInBackground: Boolean) {
|
||||
val intent = Intent(context, CallService::class.java)
|
||||
.apply {
|
||||
action = ACTION_INCOMING_RINGING_CALL
|
||||
putExtra(EXTRA_IS_VIDEO, isVideo)
|
||||
putExtra(EXTRA_ROOM_NAME, roomName)
|
||||
putExtra(EXTRA_ROOM_ID, roomId)
|
||||
putExtra(EXTRA_MATRIX_ID, matrixId)
|
||||
putExtra(EXTRA_CALL_ID, callId)
|
||||
putExtra(EXTRA_IS_IN_BG, isInBackground)
|
||||
}
|
||||
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
fun onOnGoingCallBackground(context: Context,
|
||||
isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String) {
|
||||
val intent = Intent(context, CallService::class.java)
|
||||
.apply {
|
||||
action = ACTION_ONGOING_CALL_BG
|
||||
putExtra(EXTRA_IS_VIDEO, isVideo)
|
||||
putExtra(EXTRA_ROOM_NAME, roomName)
|
||||
putExtra(EXTRA_ROOM_ID, roomId)
|
||||
putExtra(EXTRA_MATRIX_ID, matrixId)
|
||||
putExtra(EXTRA_CALL_ID, callId)
|
||||
}
|
||||
|
||||
|
@ -320,18 +314,10 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
}
|
||||
|
||||
fun onOutgoingCallRinging(context: Context,
|
||||
isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String) {
|
||||
val intent = Intent(context, CallService::class.java)
|
||||
.apply {
|
||||
action = ACTION_OUTGOING_RINGING_CALL
|
||||
putExtra(EXTRA_IS_VIDEO, isVideo)
|
||||
putExtra(EXTRA_ROOM_NAME, roomName)
|
||||
putExtra(EXTRA_ROOM_ID, roomId)
|
||||
putExtra(EXTRA_MATRIX_ID, matrixId)
|
||||
putExtra(EXTRA_CALL_ID, callId)
|
||||
}
|
||||
|
||||
|
@ -339,18 +325,10 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
}
|
||||
|
||||
fun onPendingCall(context: Context,
|
||||
isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String) {
|
||||
val intent = Intent(context, CallService::class.java)
|
||||
.apply {
|
||||
action = ACTION_ONGOING_CALL
|
||||
putExtra(EXTRA_IS_VIDEO, isVideo)
|
||||
putExtra(EXTRA_ROOM_NAME, roomName)
|
||||
putExtra(EXTRA_ROOM_ID, roomId)
|
||||
putExtra(EXTRA_MATRIX_ID, matrixId)
|
||||
putExtra(EXTRA_CALL_ID, callId)
|
||||
}
|
||||
|
||||
|
@ -362,7 +340,6 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
|||
.apply {
|
||||
action = ACTION_NO_ACTIVE_CALL
|
||||
}
|
||||
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ package im.vector.app.features.call
|
|||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
|
@ -33,7 +33,7 @@ import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
|||
|
||||
class CallControlsView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
|
@ -56,8 +56,7 @@ class CallControlsView @JvmOverloads constructor(
|
|||
lateinit var videoToggleIcon: ImageView
|
||||
|
||||
init {
|
||||
ConstraintLayout.inflate(context, R.layout.view_call_controls, this)
|
||||
// layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
View.inflate(context, R.layout.view_call_controls, this)
|
||||
ButterKnife.bind(this)
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ class WebRtcCall(val mxCall: MxCall,
|
|||
}
|
||||
|
||||
val callId = mxCall.callId
|
||||
val roomId = mxCall.roomId
|
||||
|
||||
private var peerConnection: PeerConnection? = null
|
||||
private var localAudioSource: AudioSource? = null
|
||||
|
@ -237,16 +238,9 @@ class WebRtcCall(val mxCall: MxCall,
|
|||
mxCall
|
||||
.takeIf { it.state is CallState.Connected }
|
||||
?.let { mxCall ->
|
||||
val session = sessionProvider.get()
|
||||
val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.roomId
|
||||
// Start background service with notification
|
||||
CallService.onPendingCall(
|
||||
context = context,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = session?.myUserId ?: "",
|
||||
callId = mxCall.callId)
|
||||
}
|
||||
|
||||
|
@ -307,15 +301,8 @@ class WebRtcCall(val mxCall: MxCall,
|
|||
.takeIf { it.state is CallState.Connected }
|
||||
?.let { mxCall ->
|
||||
// Start background service with notification
|
||||
val session = sessionProvider.get()
|
||||
val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.opponentUserId
|
||||
CallService.onOnGoingCallBackground(
|
||||
context = context,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = session?.myUserId ?: "",
|
||||
callId = mxCall.callId
|
||||
)
|
||||
}
|
||||
|
@ -344,15 +331,8 @@ class WebRtcCall(val mxCall: MxCall,
|
|||
val turnServerResponse = getTurnServer()
|
||||
// Update service state
|
||||
withContext(Dispatchers.Main) {
|
||||
val session = sessionProvider.get()
|
||||
val name = session?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.roomId
|
||||
CallService.onPendingCall(
|
||||
context = context,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = session?.myUserId ?: "",
|
||||
callId = mxCall.callId
|
||||
)
|
||||
}
|
||||
|
|
|
@ -189,14 +189,8 @@ class WebRtcCallManager @Inject constructor(
|
|||
createWebRtcCall(mxCall)
|
||||
callAudioManager.startForCall(mxCall)
|
||||
|
||||
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.opponentUserId
|
||||
CallService.onOutgoingCallRinging(
|
||||
context = context.applicationContext,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = currentSession?.myUserId ?: "",
|
||||
callId = mxCall.callId)
|
||||
|
||||
// start the activity now
|
||||
|
@ -264,15 +258,10 @@ class WebRtcCallManager @Inject constructor(
|
|||
}
|
||||
callAudioManager.startForCall(mxCall)
|
||||
// Start background service with notification
|
||||
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.opponentUserId
|
||||
CallService.onIncomingCallRinging(
|
||||
context = context,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = currentSession?.myUserId ?: "",
|
||||
callId = mxCall.callId
|
||||
callId = mxCall.callId,
|
||||
isInBackground = isInBackground
|
||||
)
|
||||
// If this is received while in background, the app will not sync,
|
||||
// and thus won't be able to received events. For example if the call is
|
||||
|
@ -294,14 +283,8 @@ class WebRtcCallManager @Inject constructor(
|
|||
}
|
||||
val mxCall = call.mxCall
|
||||
// Update service state
|
||||
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
|
||||
?: mxCall.opponentUserId
|
||||
CallService.onPendingCall(
|
||||
context = context,
|
||||
isVideo = mxCall.isVideoCall,
|
||||
roomName = name,
|
||||
roomId = mxCall.roomId,
|
||||
matrixId = currentSession?.myUserId ?: "",
|
||||
callId = mxCall.callId
|
||||
)
|
||||
call.onCallAnswerReceived(callAnswerContent)
|
||||
|
|
|
@ -18,11 +18,11 @@ package im.vector.app.features.crypto.verification
|
|||
import android.content.Context
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.app.features.popup.PopupAlertManager
|
||||
import im.vector.app.features.popup.VerificationVectorAlert
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
|
@ -31,6 +31,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
|
|||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
|
@ -39,6 +40,7 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class IncomingVerificationRequestHandler @Inject constructor(
|
||||
private val context: Context,
|
||||
private var avatarRenderer: Provider<AvatarRenderer>,
|
||||
private val popupAlertManager: PopupAlertManager) : VerificationService.Listener {
|
||||
|
||||
private var session: Session? = null
|
||||
|
@ -60,9 +62,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
when (tx.state) {
|
||||
is VerificationTxState.OnStarted -> {
|
||||
// Add a notification for every incoming request
|
||||
val name = session?.getUser(tx.otherUserId)?.displayName
|
||||
?: tx.otherUserId
|
||||
|
||||
val user = session?.getUser(tx.otherUserId)
|
||||
val name = user?.getBestName() ?: tx.otherUserId
|
||||
val alert = VerificationVectorAlert(
|
||||
uid,
|
||||
context.getString(R.string.sas_incoming_request_notif_title),
|
||||
|
@ -77,10 +78,10 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
}
|
||||
} ?: true
|
||||
} else true
|
||||
},
|
||||
matrixItem = session?.getUser(tx.otherUserId)?.toMatrixItem()
|
||||
}
|
||||
)
|
||||
.apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user?.toMatrixItem(), avatarRenderer.get())
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId)
|
||||
|
@ -120,8 +121,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
Timber.v("## SAS verificationRequestCreated ${pr.transactionId}")
|
||||
// For incoming request we should prompt (if not in activity where this request apply)
|
||||
if (pr.isIncoming) {
|
||||
val name = session?.getUser(pr.otherUserId)?.displayName
|
||||
?: pr.otherUserId
|
||||
val user = session?.getUser(pr.otherUserId)
|
||||
val name = user?.getBestName() ?: pr.otherUserId
|
||||
|
||||
val alert = VerificationVectorAlert(
|
||||
uniqueIdForVerificationRequest(pr),
|
||||
|
@ -134,10 +135,10 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
it.roomId != pr.roomId
|
||||
} ?: true
|
||||
} else true
|
||||
},
|
||||
matrixItem = session?.getUser(pr.otherUserId)?.toMatrixItem()
|
||||
}
|
||||
)
|
||||
.apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user?.toMatrixItem(), avatarRenderer.get())
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
val roomId = pr.roomId
|
||||
|
@ -154,7 +155,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||
pr.roomId ?: ""
|
||||
)
|
||||
}
|
||||
colorInt = ThemeUtils.getColor(context, R.attr.vctr_notice_secondary)
|
||||
colorAttribute = R.attr.vctr_notice_secondary
|
||||
// 5mn expiration
|
||||
expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ import im.vector.app.features.popup.VerificationVectorAlert
|
|||
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
|
||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewState
|
||||
import im.vector.app.push.fcm.FcmHelper
|
||||
|
@ -82,6 +81,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
|
||||
@Inject lateinit var serverBackupviewModelFactory: ServerBackupStatusViewModel.Factory
|
||||
|
||||
@Inject lateinit var avatarRenderer: AvatarRenderer
|
||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||
@Inject lateinit var pushManager: PushersManager
|
||||
|
@ -126,9 +126,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
.observe()
|
||||
.subscribe { sharedAction ->
|
||||
when (sharedAction) {
|
||||
is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
|
||||
is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
|
||||
is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START)
|
||||
is HomeActivitySharedAction.OpenGroup -> {
|
||||
is HomeActivitySharedAction.OpenGroup -> {
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
|
||||
}
|
||||
|
@ -145,9 +145,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
homeActivityViewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
|
||||
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
|
||||
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
||||
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
||||
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
|
||||
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
||||
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
||||
}.exhaustive
|
||||
}
|
||||
homeActivityViewModel.subscribe(this) { renderState(it) }
|
||||
|
@ -180,7 +180,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
|
||||
private fun renderState(state: HomeActivityViewState) {
|
||||
when (val status = state.initialSyncProgressServiceStatus) {
|
||||
is InitialSyncProgressService.Status.Idle -> {
|
||||
is InitialSyncProgressService.Status.Idle -> {
|
||||
waiting_view.isVisible = false
|
||||
}
|
||||
is InitialSyncProgressService.Status.Progressing -> {
|
||||
|
@ -251,7 +251,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
it is HomeActivity
|
||||
}
|
||||
).apply {
|
||||
colorInt = ThemeUtils.getColor(this@HomeActivity, R.attr.vctr_notice_secondary)
|
||||
colorAttribute = R.attr.vctr_notice_secondary
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
// action(it)
|
||||
|
@ -282,9 +282,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
uid = "upgradeSecurity",
|
||||
title = getString(titleRes),
|
||||
description = getString(descRes),
|
||||
iconId = R.drawable.ic_shield_warning,
|
||||
matrixItem = userItem
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(userItem, avatarRenderer)
|
||||
colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
|
|
|
@ -151,9 +151,9 @@ class HomeDetailFragment @Inject constructor(
|
|||
uid = uid,
|
||||
title = getString(R.string.new_session),
|
||||
description = getString(R.string.verify_this_session, newest.displayName ?: newest.deviceId ?: ""),
|
||||
iconId = R.drawable.ic_shield_warning,
|
||||
matrixItem = user
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
||||
colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)
|
||||
|
@ -179,9 +179,9 @@ class HomeDetailFragment @Inject constructor(
|
|||
uid = uid,
|
||||
title = getString(R.string.review_logins),
|
||||
description = getString(R.string.verify_other_sessions),
|
||||
iconId = R.drawable.ic_shield_warning,
|
||||
matrixItem = user
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
||||
colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
|
|
|
@ -72,7 +72,7 @@ class RoomDetailActivity :
|
|||
}
|
||||
|
||||
// Simple filter
|
||||
private var currentRoomId: String? = null
|
||||
var currentRoomId: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -118,8 +118,8 @@ import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs
|
|||
import im.vector.app.features.attachments.toGroupedContentAttachmentData
|
||||
import im.vector.app.features.call.SharedActiveCallViewModel
|
||||
import im.vector.app.features.call.VectorCallActivity
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.call.conference.JitsiCallViewModel
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.command.Command
|
||||
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
||||
import im.vector.app.features.crypto.util.toImageRes
|
||||
|
@ -311,7 +311,6 @@ class RoomDetailFragment @Inject constructor(
|
|||
setupActiveCallView()
|
||||
setupJumpToBottomView()
|
||||
setupConfBannerView()
|
||||
|
||||
roomToolbarContentView.debouncedClicks {
|
||||
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
|
||||
}
|
||||
|
@ -535,14 +534,14 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun handleShareData() {
|
||||
when (val sharedData = roomDetailArgs.sharedData) {
|
||||
is SharedData.Text -> {
|
||||
is SharedData.Text -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true))
|
||||
}
|
||||
is SharedData.Attachments -> {
|
||||
// open share edition
|
||||
onContentAttachmentsReady(sharedData.attachmentData)
|
||||
}
|
||||
null -> Timber.v("No share data to process")
|
||||
null -> Timber.v("No share data to process")
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -667,8 +666,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
withState(roomDetailViewModel) { state ->
|
||||
// Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions
|
||||
val callButtonsEnabled = when (state.asyncRoomSummary.invoke()?.joinedMembersCount) {
|
||||
1 -> false
|
||||
2 -> state.isAllowedToStartWebRTCCall
|
||||
1 -> false
|
||||
2 -> state.isAllowedToStartWebRTCCall
|
||||
else -> state.isAllowedToManageWidgets
|
||||
}
|
||||
setOf(R.id.voice_call, R.id.video_call).forEach {
|
||||
|
@ -698,36 +697,36 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.invite -> {
|
||||
R.id.invite -> {
|
||||
navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
|
||||
true
|
||||
}
|
||||
R.id.timeline_setting -> {
|
||||
R.id.timeline_setting -> {
|
||||
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
|
||||
true
|
||||
}
|
||||
R.id.resend_all -> {
|
||||
R.id.resend_all -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
||||
true
|
||||
}
|
||||
R.id.open_matrix_apps -> {
|
||||
R.id.open_matrix_apps -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
|
||||
true
|
||||
}
|
||||
R.id.voice_call,
|
||||
R.id.video_call -> {
|
||||
R.id.video_call -> {
|
||||
handleCallRequest(item)
|
||||
true
|
||||
}
|
||||
R.id.hangup_call -> {
|
||||
R.id.hangup_call -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EndCall)
|
||||
true
|
||||
}
|
||||
R.id.search -> {
|
||||
R.id.search -> {
|
||||
handleSearchAction()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -743,7 +742,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState
|
||||
val isVideoCall = item.itemId == R.id.video_call
|
||||
when (roomSummary.joinedMembersCount) {
|
||||
1 -> {
|
||||
1 -> {
|
||||
val pendingInvite = roomSummary.invitedMembersCount ?: 0 > 0
|
||||
if (pendingInvite) {
|
||||
// wait for other to join
|
||||
|
@ -753,7 +752,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
showDialogWithMessage(getString(R.string.cannot_call_yourself))
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
2 -> {
|
||||
val activeCall = sharedCallActionViewModel.activeCall.value
|
||||
if (activeCall != null) {
|
||||
// resume existing if same room, if not prompt to kill and then restart new call?
|
||||
|
@ -934,9 +933,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
when (roomDetailPendingAction) {
|
||||
is RoomDetailPendingAction.JumpToReadReceipt ->
|
||||
roomDetailViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
|
||||
is RoomDetailPendingAction.MentionUser ->
|
||||
is RoomDetailPendingAction.MentionUser ->
|
||||
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
|
||||
is RoomDetailPendingAction.OpenOrCreateDm ->
|
||||
is RoomDetailPendingAction.OpenOrCreateDm ->
|
||||
roomDetailViewModel.handle(RoomDetailAction.OpenOrCreateDm(roomDetailPendingAction.userId))
|
||||
}.exhaustive
|
||||
}
|
||||
|
@ -1079,9 +1078,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
withState(roomDetailViewModel) {
|
||||
val showJumpToUnreadBanner = when (it.unreadState) {
|
||||
UnreadState.Unknown,
|
||||
UnreadState.HasNoUnread -> false
|
||||
UnreadState.HasNoUnread -> false
|
||||
is UnreadState.ReadMarkerNotLoaded -> true
|
||||
is UnreadState.HasUnread -> {
|
||||
is UnreadState.HasUnread -> {
|
||||
if (it.canShowJumpToReadMarker) {
|
||||
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
||||
val positionOfReadMarker = timelineEventController.getPositionOfReadMarker()
|
||||
|
@ -1290,7 +1289,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
navigator.openRoom(vectorBaseActivity, async())
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
is Fail -> {
|
||||
is Fail -> {
|
||||
vectorBaseActivity.hideWaitingView()
|
||||
vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error))
|
||||
}
|
||||
|
@ -1299,19 +1298,19 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) {
|
||||
when (sendMessageResult) {
|
||||
is RoomDetailViewEvents.SlashCommandHandled -> {
|
||||
is RoomDetailViewEvents.SlashCommandHandled -> {
|
||||
sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
|
||||
}
|
||||
is RoomDetailViewEvents.SlashCommandError -> {
|
||||
is RoomDetailViewEvents.SlashCommandError -> {
|
||||
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
|
||||
}
|
||||
is RoomDetailViewEvents.SlashCommandUnknown -> {
|
||||
is RoomDetailViewEvents.SlashCommandUnknown -> {
|
||||
displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command))
|
||||
}
|
||||
is RoomDetailViewEvents.SlashCommandResultOk -> {
|
||||
is RoomDetailViewEvents.SlashCommandResultOk -> {
|
||||
updateComposerText("")
|
||||
}
|
||||
is RoomDetailViewEvents.SlashCommandResultError -> {
|
||||
is RoomDetailViewEvents.SlashCommandResultError -> {
|
||||
displayCommandError(errorFormatter.toHumanReadable(sendMessageResult.throwable))
|
||||
}
|
||||
is RoomDetailViewEvents.SlashCommandNotImplemented -> {
|
||||
|
@ -1333,7 +1332,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
private fun displayE2eError(withHeldCode: WithHeldCode?) {
|
||||
val msgId = when (withHeldCode) {
|
||||
WithHeldCode.BLACKLISTED -> R.string.crypto_error_withheld_blacklisted
|
||||
WithHeldCode.UNVERIFIED -> R.string.crypto_error_withheld_unverified
|
||||
WithHeldCode.UNVERIFIED -> R.string.crypto_error_withheld_unverified
|
||||
WithHeldCode.UNAUTHORISED,
|
||||
WithHeldCode.UNAVAILABLE -> R.string.crypto_error_withheld_generic
|
||||
else -> R.string.notice_crypto_unable_to_decrypt_friendly_desc
|
||||
|
@ -1385,7 +1384,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun displayRoomDetailActionSuccess(result: RoomDetailViewEvents.ActionSuccess) {
|
||||
when (val data = result.action) {
|
||||
is RoomDetailAction.ReportContent -> {
|
||||
is RoomDetailAction.ReportContent -> {
|
||||
when {
|
||||
data.spam -> {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
|
@ -1422,7 +1421,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
is RoomDetailAction.RequestVerification -> {
|
||||
is RoomDetailAction.RequestVerification -> {
|
||||
Timber.v("## SAS RequestVerification action")
|
||||
VerificationBottomSheet.withArgs(
|
||||
roomDetailArgs.roomId,
|
||||
|
@ -1437,7 +1436,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
data.transactionId
|
||||
).show(parentFragmentManager, "REQ")
|
||||
}
|
||||
is RoomDetailAction.ResumeVerification -> {
|
||||
is RoomDetailAction.ResumeVerification -> {
|
||||
val otherUserId = data.otherUserId ?: return
|
||||
VerificationBottomSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
|
@ -1581,11 +1580,11 @@ class RoomDetailFragment @Inject constructor(
|
|||
is MessageVerificationRequestContent -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
|
||||
}
|
||||
is MessageWithAttachmentContent -> {
|
||||
is MessageWithAttachmentContent -> {
|
||||
val action = RoomDetailAction.DownloadOrOpen(informationData.eventId, informationData.senderId, messageContent)
|
||||
roomDetailViewModel.handle(action)
|
||||
}
|
||||
is EncryptedEventContent -> {
|
||||
is EncryptedEventContent -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
|
||||
}
|
||||
}
|
||||
|
@ -1726,75 +1725,75 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun handleActions(action: EventSharedAction) {
|
||||
when (action) {
|
||||
is EventSharedAction.OpenUserProfile -> {
|
||||
is EventSharedAction.OpenUserProfile -> {
|
||||
openRoomMemberProfile(action.userId)
|
||||
}
|
||||
is EventSharedAction.AddReaction -> {
|
||||
is EventSharedAction.AddReaction -> {
|
||||
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
|
||||
}
|
||||
is EventSharedAction.ViewReactions -> {
|
||||
is EventSharedAction.ViewReactions -> {
|
||||
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
|
||||
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
|
||||
}
|
||||
is EventSharedAction.Copy -> {
|
||||
is EventSharedAction.Copy -> {
|
||||
// I need info about the current selected message :/
|
||||
copyToClipboard(requireContext(), action.content, false)
|
||||
showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
is EventSharedAction.Redact -> {
|
||||
is EventSharedAction.Redact -> {
|
||||
promptConfirmationToRedactEvent(action)
|
||||
}
|
||||
is EventSharedAction.Share -> {
|
||||
is EventSharedAction.Share -> {
|
||||
onShareActionClicked(action)
|
||||
}
|
||||
is EventSharedAction.Save -> {
|
||||
is EventSharedAction.Save -> {
|
||||
onSaveActionClicked(action)
|
||||
}
|
||||
is EventSharedAction.ViewEditHistory -> {
|
||||
is EventSharedAction.ViewEditHistory -> {
|
||||
onEditedDecorationClicked(action.messageInformationData)
|
||||
}
|
||||
is EventSharedAction.ViewSource -> {
|
||||
is EventSharedAction.ViewSource -> {
|
||||
JSonViewerDialog.newInstance(
|
||||
action.content,
|
||||
-1,
|
||||
createJSonViewerStyleProvider(colorProvider)
|
||||
).show(childFragmentManager, "JSON_VIEWER")
|
||||
}
|
||||
is EventSharedAction.ViewDecryptedSource -> {
|
||||
is EventSharedAction.ViewDecryptedSource -> {
|
||||
JSonViewerDialog.newInstance(
|
||||
action.content,
|
||||
-1,
|
||||
createJSonViewerStyleProvider(colorProvider)
|
||||
).show(childFragmentManager, "JSON_VIEWER")
|
||||
}
|
||||
is EventSharedAction.QuickReact -> {
|
||||
is EventSharedAction.QuickReact -> {
|
||||
// eventId,ClickedOn,Add
|
||||
roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
|
||||
}
|
||||
is EventSharedAction.Edit -> {
|
||||
is EventSharedAction.Edit -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString()))
|
||||
}
|
||||
is EventSharedAction.Quote -> {
|
||||
is EventSharedAction.Quote -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString()))
|
||||
}
|
||||
is EventSharedAction.Reply -> {
|
||||
is EventSharedAction.Reply -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString()))
|
||||
}
|
||||
is EventSharedAction.CopyPermalink -> {
|
||||
is EventSharedAction.CopyPermalink -> {
|
||||
val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
|
||||
copyToClipboard(requireContext(), permalink, false)
|
||||
showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
is EventSharedAction.Resend -> {
|
||||
is EventSharedAction.Resend -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ResendMessage(action.eventId))
|
||||
}
|
||||
is EventSharedAction.Remove -> {
|
||||
is EventSharedAction.Remove -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId))
|
||||
}
|
||||
is EventSharedAction.Cancel -> {
|
||||
is EventSharedAction.Cancel -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId))
|
||||
}
|
||||
is EventSharedAction.ReportContentSpam -> {
|
||||
is EventSharedAction.ReportContentSpam -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ReportContent(
|
||||
action.eventId, action.senderId, "This message is spam", spam = true))
|
||||
}
|
||||
|
@ -1802,22 +1801,22 @@ class RoomDetailFragment @Inject constructor(
|
|||
roomDetailViewModel.handle(RoomDetailAction.ReportContent(
|
||||
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
|
||||
}
|
||||
is EventSharedAction.ReportContentCustom -> {
|
||||
is EventSharedAction.ReportContentCustom -> {
|
||||
promptReasonToReportContent(action)
|
||||
}
|
||||
is EventSharedAction.IgnoreUser -> {
|
||||
is EventSharedAction.IgnoreUser -> {
|
||||
action.senderId?.let { askConfirmationToIgnoreUser(it) }
|
||||
}
|
||||
is EventSharedAction.OnUrlClicked -> {
|
||||
is EventSharedAction.OnUrlClicked -> {
|
||||
onUrlClicked(action.url, action.title)
|
||||
}
|
||||
is EventSharedAction.OnUrlLongClicked -> {
|
||||
is EventSharedAction.OnUrlLongClicked -> {
|
||||
onUrlLongClicked(action.url)
|
||||
}
|
||||
is EventSharedAction.ReRequestKey -> {
|
||||
is EventSharedAction.ReRequestKey -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ReRequestKeys(action.eventId))
|
||||
}
|
||||
is EventSharedAction.UseKeyBackup -> {
|
||||
is EventSharedAction.UseKeyBackup -> {
|
||||
context?.let {
|
||||
startActivity(KeysBackupRestoreActivity.intent(it))
|
||||
}
|
||||
|
@ -1957,10 +1956,10 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
|
||||
when (type) {
|
||||
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentImageActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher)
|
||||
AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment)
|
||||
}.exhaustive
|
||||
|
|
|
@ -30,6 +30,10 @@ import android.graphics.Bitmap
|
|||
import android.graphics.Canvas
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.NotificationCompat
|
||||
|
@ -52,6 +56,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
|
|||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
|
||||
import org.matrix.android.sdk.api.session.call.MxCall
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -269,19 +274,19 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
* @param roomName the room name in which the call is pending.
|
||||
* @param matrixId the matrix id
|
||||
* @param callId the call id.
|
||||
* @param fromBg true if the app is in background when posting the notification
|
||||
* @return the call notification.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun buildIncomingCallNotification(isVideo: Boolean,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
callId: String): Notification {
|
||||
fun buildIncomingCallNotification(mxCall: MxCall,
|
||||
title: String,
|
||||
fromBg: Boolean): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
|
||||
val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(ensureTitleNotEmpty(otherUserId))
|
||||
val notificationChannel = if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
||||
val builder = NotificationCompat.Builder(context, notificationChannel)
|
||||
.setContentTitle(ensureTitleNotEmpty(title))
|
||||
.apply {
|
||||
if (isVideo) {
|
||||
if (mxCall.isVideoCall) {
|
||||
setContentText(stringProvider.getString(R.string.incoming_video_call))
|
||||
} else {
|
||||
setContentText(stringProvider.getString(R.string.incoming_voice_call))
|
||||
|
@ -300,15 +305,11 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
|
||||
val contentIntent = VectorCallActivity.newIntent(
|
||||
context = context,
|
||||
callId = callId,
|
||||
roomId = roomId,
|
||||
otherUserId = otherUserId,
|
||||
isIncomingCall = true,
|
||||
isVideoCall = isVideo,
|
||||
mxCall = mxCall,
|
||||
mode = VectorCallActivity.INCOMING_RINGING
|
||||
).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
data = Uri.parse("foobar://$callId")
|
||||
data = Uri.parse("foobar://${mxCall.callId}")
|
||||
}
|
||||
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
||||
|
||||
|
@ -316,32 +317,28 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
||||
.addNextIntent(VectorCallActivity.newIntent(
|
||||
context = context,
|
||||
callId = callId,
|
||||
roomId = roomId,
|
||||
otherUserId = otherUserId,
|
||||
isIncomingCall = true,
|
||||
isVideoCall = isVideo,
|
||||
mxCall = mxCall,
|
||||
mode = VectorCallActivity.INCOMING_ACCEPT)
|
||||
)
|
||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
||||
|
||||
builder.addAction(
|
||||
NotificationCompat.Action(
|
||||
R.drawable.ic_call,
|
||||
// IconCompat.createWithResource(applicationContext, R.drawable.ic_call)
|
||||
// .setTint(ContextCompat.getColor(applicationContext, R.color.riotx_positive_accent)),
|
||||
context.getString(R.string.call_notification_answer),
|
||||
answerCallPendingIntent
|
||||
)
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
getActionText(R.string.call_notification_reject, R.color.riotx_notice),
|
||||
rejectCallPendingIntent)
|
||||
)
|
||||
|
||||
builder.addAction(
|
||||
NotificationCompat.Action(
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
context.getString(R.string.call_notification_reject),
|
||||
rejectCallPendingIntent)
|
||||
R.drawable.ic_call_answer,
|
||||
// IconCompat.createWithResource(applicationContext, R.drawable.ic_call)
|
||||
// .setTint(ContextCompat.getColor(applicationContext, R.color.riotx_positive_accent)),
|
||||
getActionText(R.string.call_notification_answer, R.color.riotx_positive_accent),
|
||||
answerCallPendingIntent
|
||||
)
|
||||
)
|
||||
|
||||
builder.setFullScreenIntent(contentPendingIntent, true)
|
||||
|
@ -349,14 +346,11 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
return builder.build()
|
||||
}
|
||||
|
||||
fun buildOutgoingRingingCallNotification(isVideo: Boolean,
|
||||
otherUserId: String,
|
||||
roomId: String,
|
||||
callId: String): Notification {
|
||||
fun buildOutgoingRingingCallNotification(mxCall: MxCall,
|
||||
title: String): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
|
||||
val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(ensureTitleNotEmpty(otherUserId))
|
||||
.setContentTitle(ensureTitleNotEmpty(title))
|
||||
.apply {
|
||||
setContentText(stringProvider.getString(R.string.call_ring))
|
||||
}
|
||||
|
@ -367,23 +361,19 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
|
||||
val contentIntent = VectorCallActivity.newIntent(
|
||||
context = context,
|
||||
callId = callId,
|
||||
roomId = roomId,
|
||||
otherUserId = otherUserId,
|
||||
isIncomingCall = true,
|
||||
isVideoCall = isVideo,
|
||||
mxCall = mxCall,
|
||||
mode = null).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
data = Uri.parse("foobar://$callId")
|
||||
data = Uri.parse("foobar://$mxCall.callId")
|
||||
}
|
||||
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
||||
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
||||
|
||||
builder.addAction(
|
||||
NotificationCompat.Action(
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
context.getString(R.string.call_notification_hangup),
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
getActionText(R.string.call_notification_hangup, R.color.riotx_notice),
|
||||
rejectCallPendingIntent)
|
||||
)
|
||||
builder.setContentIntent(contentPendingIntent)
|
||||
|
@ -402,16 +392,13 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
* @return the call notification.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun buildPendingCallNotification(isVideo: Boolean,
|
||||
roomName: String,
|
||||
roomId: String,
|
||||
matrixId: String,
|
||||
callId: String,
|
||||
fun buildPendingCallNotification(mxCall: MxCall,
|
||||
title: String,
|
||||
fromBg: Boolean = false): Notification {
|
||||
val builder = NotificationCompat.Builder(context, if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(ensureTitleNotEmpty(roomName))
|
||||
.setContentTitle(ensureTitleNotEmpty(title))
|
||||
.apply {
|
||||
if (isVideo) {
|
||||
if (mxCall.isVideoCall) {
|
||||
setContentText(stringProvider.getString(R.string.video_call_in_progress))
|
||||
} else {
|
||||
setContentText(stringProvider.getString(R.string.call_in_progress))
|
||||
|
@ -425,19 +412,18 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
builder.setOngoing(true)
|
||||
}
|
||||
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||
val rejectCallPendingIntent = buildRejectCallPendingIntent(mxCall.callId)
|
||||
|
||||
builder.addAction(
|
||||
NotificationCompat.Action(
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
context.getString(R.string.call_notification_hangup),
|
||||
IconCompat.createWithResource(context, R.drawable.ic_call_hangup).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
|
||||
getActionText(R.string.call_notification_hangup, R.color.riotx_notice),
|
||||
rejectCallPendingIntent)
|
||||
)
|
||||
|
||||
val contentPendingIntent = TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
|
||||
// TODO other userId
|
||||
.addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, null))
|
||||
.addNextIntent(VectorCallActivity.newIntent(context, mxCall, null))
|
||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
builder.setContentIntent(contentPendingIntent)
|
||||
|
@ -462,7 +448,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
* Build a temporary (because service will be stopped just after) notification for the CallService, when a call is ended
|
||||
*/
|
||||
fun buildCallEndedNotification(): Notification {
|
||||
return NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
|
||||
return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(stringProvider.getString(R.string.call_ended))
|
||||
.setSmallIcon(R.drawable.ic_material_call_end_grey)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
|
@ -883,6 +869,13 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||
|| setting == NotificationManager.INTERRUPTION_FILTER_ALARMS
|
||||
}
|
||||
|
||||
private fun getActionText(@StringRes stringRes: Int, @ColorRes colorRes: Int): Spannable {
|
||||
return SpannableString(context.getText(stringRes)).apply {
|
||||
val foregroundColorSpan = ForegroundColorSpan(ContextCompat.getColor(context, colorRes))
|
||||
setSpan(foregroundColorSpan, 0, length, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureTitleNotEmpty(title: String?): CharSequence {
|
||||
if (title.isNullOrBlank()) {
|
||||
return stringProvider.getString(R.string.app_name)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 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 priority = PopupAlertManager.INCOMING_CALL_PRIORITY
|
||||
override val layoutRes = R.layout.alerter_incoming_call_layout
|
||||
override var colorAttribute: Int? = R.attr.riotx_alerter_background
|
||||
override val dismissOnClick: Boolean = false
|
||||
override val isLight: Boolean = true
|
||||
|
||||
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,10 @@ import android.os.Build
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import com.tapadoo.alerter.Alerter
|
||||
import com.tapadoo.alerter.OnHideAlertListener
|
||||
import dagger.Lazy
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.isAnimationDisabled
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.pin.PinActivity
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import timber.log.Timber
|
||||
|
@ -38,19 +34,25 @@ import javax.inject.Singleton
|
|||
|
||||
/**
|
||||
* Responsible of displaying important popup alerts on top of the screen.
|
||||
* Alerts are stacked and will be displayed sequentially
|
||||
* Alerts are stacked and will be displayed sequentially but sorted by priority.
|
||||
* So if a new alert is posted with a higher priority than the current one it will show it instead and the current one
|
||||
* will be back in the queue in first position.
|
||||
*/
|
||||
@Singleton
|
||||
class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<AvatarRenderer>) {
|
||||
class PopupAlertManager @Inject constructor() {
|
||||
|
||||
companion object {
|
||||
const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE
|
||||
}
|
||||
|
||||
private var weakCurrentActivity: WeakReference<Activity>? = null
|
||||
private var currentAlerter: VectorAlert? = null
|
||||
|
||||
private val alertFiFo = mutableListOf<VectorAlert>()
|
||||
private val alertQueue = mutableListOf<VectorAlert>()
|
||||
|
||||
fun postVectorAlert(alert: VectorAlert) {
|
||||
synchronized(alertFiFo) {
|
||||
alertFiFo.add(alert)
|
||||
synchronized(alertQueue) {
|
||||
alertQueue.add(alert)
|
||||
}
|
||||
weakCurrentActivity?.get()?.runOnUiThread {
|
||||
displayNextIfPossible()
|
||||
|
@ -58,8 +60,8 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
}
|
||||
|
||||
fun cancelAlert(uid: String) {
|
||||
synchronized(alertFiFo) {
|
||||
alertFiFo.listIterator().apply {
|
||||
synchronized(alertQueue) {
|
||||
alertQueue.listIterator().apply {
|
||||
while (this.hasNext()) {
|
||||
val next = this.next()
|
||||
if (next.uid == uid) {
|
||||
|
@ -82,8 +84,8 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
* Cancel all alerts, after a sign out for instance
|
||||
*/
|
||||
fun cancelAll() {
|
||||
synchronized(alertFiFo) {
|
||||
alertFiFo.clear()
|
||||
synchronized(alertQueue) {
|
||||
alertQueue.clear()
|
||||
}
|
||||
|
||||
// Cancel any displayed alert
|
||||
|
@ -98,7 +100,9 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
if (currentAlerter != null) {
|
||||
weakCurrentActivity?.get()?.let {
|
||||
Alerter.clearCurrent(it)
|
||||
setLightStatusBar()
|
||||
if (currentAlerter?.isLight == false) {
|
||||
setLightStatusBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
weakCurrentActivity = WeakReference(activity)
|
||||
|
@ -129,15 +133,21 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
}
|
||||
|
||||
private fun displayNextIfPossible() {
|
||||
val currentActivity = weakCurrentActivity?.get()
|
||||
if (Alerter.isShowing || currentActivity == null) {
|
||||
// will retry later
|
||||
return
|
||||
}
|
||||
val currentActivity = weakCurrentActivity?.get() ?: return
|
||||
val next: VectorAlert?
|
||||
synchronized(alertFiFo) {
|
||||
next = alertFiFo.firstOrNull()
|
||||
if (next != null) alertFiFo.remove(next)
|
||||
synchronized(alertQueue) {
|
||||
next = alertQueue.maxByOrNull { it.priority }
|
||||
// If next alert with highest priority is higher than the current one, we should display it
|
||||
// and add the current one to queue again.
|
||||
if (next != null && next.priority > currentAlerter?.priority ?: Int.MIN_VALUE) {
|
||||
alertQueue.remove(next)
|
||||
currentAlerter?.also {
|
||||
alertQueue.add(0, it)
|
||||
}
|
||||
} else {
|
||||
// otherwise, we don't do anything
|
||||
return
|
||||
}
|
||||
}
|
||||
currentAlerter = next
|
||||
next?.let {
|
||||
|
@ -186,22 +196,19 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
}
|
||||
|
||||
private fun showAlert(alert: VectorAlert, activity: Activity, animate: Boolean = true) {
|
||||
clearLightStatusBar()
|
||||
|
||||
if (!alert.isLight) {
|
||||
clearLightStatusBar()
|
||||
}
|
||||
val noAnimation = !animate || isAnimationDisabled(activity)
|
||||
|
||||
alert.weakCurrentActivity = WeakReference(activity)
|
||||
val alerter = if (alert is VerificationVectorAlert) Alerter.create(activity, R.layout.alerter_verification_layout)
|
||||
else Alerter.create(activity)
|
||||
val alerter = Alerter.create(activity, alert.layoutRes)
|
||||
|
||||
alerter.setTitle(alert.title)
|
||||
.setText(alert.description)
|
||||
.also { al ->
|
||||
if (alert is VerificationVectorAlert) {
|
||||
val tvCustomView = al.getLayoutContainer()
|
||||
tvCustomView?.findViewById<ImageView>(R.id.ivUserAvatar)?.let { imageView ->
|
||||
alert.matrixItem?.let { avatarRenderer.get().render(it, imageView) }
|
||||
}
|
||||
al.getLayoutContainer()?.also {
|
||||
alert.viewBinder?.bind(it)
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
|
@ -213,7 +220,7 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
setIcon(it)
|
||||
}
|
||||
alert.actions.forEach { action ->
|
||||
addButton(action.title, R.style.AlerterButton, View.OnClickListener {
|
||||
addButton(action.title, R.style.AlerterButton) {
|
||||
if (action.autoClose) {
|
||||
currentIsDismissed()
|
||||
Alerter.hide()
|
||||
|
@ -223,21 +230,23 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
} catch (e: java.lang.Exception) {
|
||||
Timber.e("## failed to perform action")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
setOnClickListener(View.OnClickListener { _ ->
|
||||
setOnClickListener { _ ->
|
||||
alert.contentAction?.let {
|
||||
currentIsDismissed()
|
||||
Alerter.hide()
|
||||
if (alert.dismissOnClick) {
|
||||
currentIsDismissed()
|
||||
Alerter.hide()
|
||||
}
|
||||
try {
|
||||
it.run()
|
||||
} catch (e: java.lang.Exception) {
|
||||
Timber.e("## failed to perform action")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.setOnHideListener(OnHideAlertListener {
|
||||
.setOnHideListener {
|
||||
// called when dismissed on swipe
|
||||
try {
|
||||
alert.dismissedAction?.run()
|
||||
|
@ -245,12 +254,14 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
Timber.e("## failed to perform action")
|
||||
}
|
||||
currentIsDismissed()
|
||||
})
|
||||
}
|
||||
.enableSwipeToDismiss()
|
||||
.enableInfiniteDuration(true)
|
||||
.apply {
|
||||
if (alert.colorInt != null) {
|
||||
setBackgroundColorInt(alert.colorInt!!)
|
||||
} else if (alert.colorAttribute != null) {
|
||||
setBackgroundColorInt(ThemeUtils.getColor(activity, alert.colorAttribute!!))
|
||||
} else {
|
||||
setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
|
||||
}
|
||||
|
@ -261,8 +272,9 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<Ava
|
|||
|
||||
private fun currentIsDismissed() {
|
||||
// current alert has been hidden
|
||||
setLightStatusBar()
|
||||
|
||||
if (currentAlerter?.isLight == false) {
|
||||
setLightStatusBar()
|
||||
}
|
||||
currentAlerter = null
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
displayNextIfPossible()
|
||||
|
|
|
@ -17,10 +17,13 @@
|
|||
package im.vector.app.features.popup
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import androidx.annotation.LayoutRes
|
||||
import im.vector.app.R
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
interface VectorAlert {
|
||||
|
@ -28,6 +31,9 @@ interface VectorAlert {
|
|||
val title: String
|
||||
val description: String
|
||||
val iconId: Int?
|
||||
val priority: Int
|
||||
val dismissOnClick: Boolean
|
||||
val isLight: Boolean
|
||||
val shouldBeDisplayedIn: ((Activity) -> Boolean)
|
||||
|
||||
data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
|
||||
|
@ -47,22 +53,33 @@ interface VectorAlert {
|
|||
actions.add(Button(title, action, autoClose))
|
||||
}
|
||||
|
||||
var viewBinder: ViewBinder?
|
||||
|
||||
val layoutRes: Int
|
||||
|
||||
var colorRes: Int?
|
||||
|
||||
var colorInt: Int?
|
||||
|
||||
var colorAttribute: Int?
|
||||
|
||||
interface ViewBinder {
|
||||
fun bind(view: View)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataclass to describe an important alert with actions.
|
||||
*/
|
||||
open class DefaultVectorAlert(override val uid: String,
|
||||
override val title: String,
|
||||
override val description: String,
|
||||
@DrawableRes override val iconId: Int?,
|
||||
/**
|
||||
* Alert are displayed by default, but let this lambda return false to prevent displaying
|
||||
*/
|
||||
override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
|
||||
open class DefaultVectorAlert(
|
||||
override val uid: String,
|
||||
override val title: String,
|
||||
override val description: String,
|
||||
@DrawableRes override val iconId: Int?,
|
||||
/**
|
||||
* Alert are displayed by default, but let this lambda return false to prevent displaying
|
||||
*/
|
||||
override val shouldBeDisplayedIn: ((Activity) -> Boolean) = { true }
|
||||
) : VectorAlert {
|
||||
|
||||
// will be set by manager, and accessible by actions at runtime
|
||||
|
@ -76,26 +93,23 @@ open class DefaultVectorAlert(override val uid: String,
|
|||
/** If this timestamp is after current time, this alert will be skipped */
|
||||
override var expirationTimestamp: Long? = null
|
||||
|
||||
override fun addButton(title: String, action: Runnable, autoClose: Boolean) {
|
||||
actions.add(VectorAlert.Button(title, action, autoClose))
|
||||
}
|
||||
@LayoutRes
|
||||
override val layoutRes = R.layout.alerter_alert_default_layout
|
||||
|
||||
override val dismissOnClick: Boolean = true
|
||||
|
||||
override val priority: Int = 0
|
||||
|
||||
override val isLight: Boolean = false
|
||||
|
||||
@ColorRes
|
||||
override var colorRes: Int? = null
|
||||
|
||||
@ColorInt
|
||||
override var colorInt: Int? = 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
|
||||
)
|
||||
@AttrRes
|
||||
override var colorAttribute: Int? = null
|
||||
|
||||
override var viewBinder: VectorAlert.ViewBinder? = null
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
vector/src/main/res/layout/alerter_incoming_call_layout.xml
Normal file
83
vector/src/main/res/layout/alerter_incoming_call_layout.xml
Normal file
|
@ -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_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomToolbar"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/badgeBarrier"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:parentTag="android.widget.FrameLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
@ -200,4 +201,4 @@
|
|||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent" />-->
|
||||
|
||||
</FrameLayout>
|
||||
</merge>
|
|
@ -238,9 +238,16 @@
|
|||
<color name="riotx_reaction_background_on_dark">#4011BC8A</color>
|
||||
<color name="riotx_reaction_background_on_black">#4011BC8A</color>
|
||||
|
||||
<attr name="riotx_alerter_background" format="color" />
|
||||
<color name="riotx_alerter_background_light">#FFF3F8FD</color>
|
||||
<color name="riotx_alerter_background_dark">#FF282C35</color>
|
||||
<color name="riotx_alerter_background_black">#FF282C35</color>
|
||||
|
||||
<!-- (color from RiotWeb) -->
|
||||
<attr name="riotx_keys_backup_banner_accent_color" format="color" />
|
||||
<color name="riotx_keys_backup_banner_accent_color_light">#FFF8E3</color>
|
||||
<color name="riotx_keys_backup_banner_accent_color_dark">#22262E</color>
|
||||
|
||||
|
||||
|
||||
</resources>
|
|
@ -38,6 +38,7 @@
|
|||
<item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_black</item>
|
||||
<item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_black</item>
|
||||
<item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_black</item>
|
||||
<item name="riotx_alerter_background">@color/riotx_alerter_background_black</item>
|
||||
|
||||
<item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_black</item>
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_dark</item>
|
||||
<item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_dark</item>
|
||||
<item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_dark</item>
|
||||
|
||||
<item name="riotx_alerter_background">@color/riotx_alerter_background_dark</item>
|
||||
<item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_dark</item>
|
||||
|
||||
<item name="riotx_keys_backup_banner_accent_color">@color/riotx_keys_backup_banner_accent_color_dark</item>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<item name="riotx_room_active_widgets_banner_text">@color/riotx_room_active_widgets_banner_text_light</item>
|
||||
<item name="riotx_reaction_background_off">@color/riotx_reaction_background_off_light</item>
|
||||
<item name="riotx_reaction_background_on">@color/riotx_reaction_background_on_light</item>
|
||||
|
||||
<item name="riotx_alerter_background">@color/riotx_alerter_background_light</item>
|
||||
<item name="riotx_bottom_nav_icon_color">@color/riotx_bottom_nav_icon_color_light</item>
|
||||
|
||||
<!-- Material color: Note: this block should be the same in all theme because it references only common colors and ?riotx attributes -->
|
||||
|
|
Loading…
Reference in a new issue