VoIP: continue working on call tiles in timeline (add actions)

This commit is contained in:
ganfra 2020-12-04 16:27:41 +01:00
parent 24de6c0101
commit 139c3fdd19
11 changed files with 65 additions and 22 deletions

View file

@ -79,7 +79,7 @@ fun TextView.setLeftDrawable(@DrawableRes iconRes: Int, @ColorRes tintColor: Int
val icon = if(tintColor != null){
val tint = ContextCompat.getColor(context, tintColor)
ContextCompat.getDrawable(context, iconRes)?.also {
DrawableCompat.setTint(it, tint)
DrawableCompat.setTint(it.mutate(), tint)
}
}else {
ContextCompat.getDrawable(context, iconRes)

View file

@ -339,12 +339,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
const val INCOMING_RINGING = "INCOMING_RINGING"
const val INCOMING_ACCEPT = "INCOMING_ACCEPT"
fun newIntent(context: Context, mxCall: MxCallDetail): Intent {
fun newIntent(context: Context, mxCall: MxCallDetail, mode: String?): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
// what could be the best flags?
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.opponentUserId, !mxCall.isOutgoing, mxCall.isVideoCall))
putExtra(EXTRA_MODE, OUTGOING_CREATED)
putExtra(EXTRA_MODE, mode)
}
}

View file

@ -203,7 +203,7 @@ class WebRtcCallManager @Inject constructor(
callId = mxCall.callId)
// start the activity now
context.startActivity(VectorCallActivity.newIntent(context, mxCall))
context.startActivity(VectorCallActivity.newIntent(context, mxCall, VectorCallActivity.OUTGOING_CREATED))
}
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {

View file

@ -19,6 +19,8 @@ package im.vector.app.features.home.room.detail
import android.net.Uri
import android.view.View
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@ -73,6 +75,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
object ResendAll : RoomDetailAction()
data class StartCall(val isVideo: Boolean) : RoomDetailAction()
data class AcceptCall(val callId: String): RoomDetailAction()
object EndCall : RoomDetailAction()
data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction()

View file

@ -368,6 +368,7 @@ class RoomDetailFragment @Inject constructor(
is RoomDetailViewEvents.DisplayEnableIntegrationsWarning -> displayDisabledIntegrationDialog()
is RoomDetailViewEvents.OpenIntegrationManager -> openIntegrationManager()
is RoomDetailViewEvents.OpenFile -> startOpenFileIntent(it)
is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it)
RoomDetailViewEvents.OpenActiveWidgetBottomSheet -> onViewWidgetsClicked()
is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
@ -381,6 +382,7 @@ class RoomDetailFragment @Inject constructor(
is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
navigator.openBigImageViewer(requireActivity(), it.view, item)
}
}.exhaustive
}
@ -389,6 +391,15 @@ class RoomDetailFragment @Inject constructor(
}
}
private fun acceptIncomingCall(event: RoomDetailViewEvents.DisplayAndAcceptCall) {
val intent = VectorCallActivity.newIntent(
context = vectorBaseActivity,
mxCall = event.call.mxCall,
mode = VectorCallActivity.INCOMING_ACCEPT
)
startActivity(intent)
}
override fun onImageReady(uri: Uri?) {
uri ?: return
roomDetailViewModel.handle(
@ -836,6 +847,8 @@ class RoomDetailFragment @Inject constructor(
}
}
private fun safeStartCall2(isVideoCall: Boolean) {
val startCallAction = RoomDetailAction.StartCall(isVideoCall)
roomDetailViewModel.pendingAction = startCallAction

View file

@ -20,6 +20,7 @@ import android.net.Uri
import android.view.View
import androidx.annotation.StringRes
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.command.Command
import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.api.util.MatrixItem
@ -73,6 +74,8 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
abstract class SendMessageResult : RoomDetailViewEvents()
data class DisplayAndAcceptCall(val call: WebRtcCall): RoomDetailViewEvents()
object DisplayPromptForIntegrationManager : RoomDetailViewEvents()
object DisplayEnableIntegrationsWarning : RoomDetailViewEvents()

View file

@ -269,6 +269,7 @@ class RoomDetailViewModel @AssistedInject constructor(
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
is RoomDetailAction.StartCall -> handleStartCall(action)
is RoomDetailAction.AcceptCall -> handleAcceptCall(action)
is RoomDetailAction.EndCall -> handleEndCall()
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
@ -289,6 +290,12 @@ class RoomDetailViewModel @AssistedInject constructor(
}.exhaustive
}
private fun handleAcceptCall(action: RoomDetailAction.AcceptCall) {
callManager.getCallById(action.callId)?.also {
_viewEvents.post(RoomDetailViewEvents.DisplayAndAcceptCall(it))
}
}
private fun handleSetNewAvatar(action: RoomDetailAction.SetAvatarAction) {
viewModelScope.launch(Dispatchers.IO) {
try {

View file

@ -30,10 +30,12 @@ import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.LoadingItem_
import im.vector.app.core.extensions.localDateTime
import im.vector.app.core.extensions.nextOrNull
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.RoomDetailViewState
import im.vector.app.features.home.room.detail.UnreadState
import im.vector.app.features.home.room.detail.timeline.factory.MergedHeaderItemFactory
import im.vector.app.features.home.room.detail.timeline.factory.NoticeItemFactory
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
@ -47,6 +49,7 @@ import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineIte
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.app.features.home.room.detail.timeline.item.NoticeItemBuilder
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
import im.vector.app.features.media.ImageContentRenderer
@ -73,6 +76,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
private val session: Session,
private val callManager: WebRtcCallManager,
private val noticeItemFactory: NoticeItemFactory,
@TimelineEventControllerHandler
private val backgroundHandler: Handler
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
@ -187,12 +192,17 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
adapterPositionMapping.clear()
val callIds = mutableSetOf<String>()
val modelsIterator = models.listIterator()
val showHiddenEvents = vectorPreferences.shouldShowHiddenEvents()
modelsIterator.withIndex().forEach {
val index = it.index
val epoxyModel = it.value
if (epoxyModel is CallTileTimelineItem) {
val callId = epoxyModel.attributes.callId
if (callIds.contains(callId)) {
val call = callManager.getCallById(callId)
// We should remove the call tile if we already have one for this call or
// if this is an active call tile without an actual call (which can happen with permalink)
val shouldRemoveCallItem = callIds.contains(callId) || (call == null && epoxyModel.attributes.callStatus.isActive())
if (shouldRemoveCallItem && !showHiddenEvents) {
modelsIterator.remove()
return@forEach
}

View file

@ -63,25 +63,25 @@ class CallItemFactory @Inject constructor(
}
return when (event.root.getClearType()) {
EventType.CALL_ANSWER -> {
if (call == null) return null
createCallTileTimelineItem(
callId = callId,
callStatus = CallTileTimelineItem.CallStatus.IN_CALL,
callKind = callKind,
callback = callback,
highlight = highlight,
informationData = informationData
informationData = informationData,
isStillActive = call != null
)
}
EventType.CALL_INVITE -> {
if (call == null) return null
createCallTileTimelineItem(
callId = callId,
callStatus = CallTileTimelineItem.CallStatus.INVITED,
callKind = callKind,
callback = callback,
highlight = highlight,
informationData = informationData
informationData = informationData,
isStillActive = call != null
)
}
EventType.CALL_REJECT -> {
@ -91,7 +91,8 @@ class CallItemFactory @Inject constructor(
callKind = callKind,
callback = callback,
highlight = highlight,
informationData = informationData
informationData = informationData,
isStillActive = false
)
}
EventType.CALL_HANGUP -> {
@ -101,7 +102,8 @@ class CallItemFactory @Inject constructor(
callKind = callKind,
callback = callback,
highlight = highlight,
informationData = informationData
informationData = informationData,
isStillActive = false
)
}
else -> null
@ -124,6 +126,7 @@ class CallItemFactory @Inject constructor(
callStatus: CallTileTimelineItem.CallStatus,
informationData: MessageInformationData,
highlight: Boolean,
isStillActive: Boolean,
callback: TimelineEventController.Callback?
): CallTileTimelineItem? {
@ -140,7 +143,9 @@ class CallItemFactory @Inject constructor(
itemLongClickListener = it.itemLongClickListener,
reactionPillCallback = it.reactionPillCallback,
readReceiptsCallback = it.readReceiptsCallback,
userOfInterest = userOfInterest
userOfInterest = userOfInterest,
callback = callback,
isStillActive = isStillActive
)
}
return CallTileTimelineItem_()

View file

@ -15,31 +15,26 @@
*/
package im.vector.app.features.home.room.detail.timeline.item
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.extensions.setLeftDrawable
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import org.matrix.android.sdk.api.util.MatrixItem
import timber.log.Timber
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_state)
abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Holder>() {
@ -62,14 +57,14 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
attributes.avatarRenderer.render(attributes.userOfInterest, holder.creatorAvatarView)
holder.callKindView.setText(attributes.callKind.title)
holder.callKindView.setLeftDrawable(attributes.callKind.icon)
if (attributes.callStatus == CallStatus.INVITED && !attributes.informationData.sentByMe) {
if (attributes.callStatus == CallStatus.INVITED && !attributes.informationData.sentByMe && attributes.isStillActive) {
holder.acceptRejectViewGroup.isVisible = true
holder.acceptView.setOnClickListener {
Timber.v("On accept call: $attributes.callId ")
attributes.callback?.onTimelineItemAction(RoomDetailAction.AcceptCall(callId = attributes.callId))
}
holder.rejectView.setLeftDrawable(R.drawable.ic_call_hangup, R.color.riotx_notice)
holder.rejectView.setOnClickListener {
Timber.v("On reject call: $attributes.callId")
attributes.callback?.onTimelineItemAction(RoomDetailAction.EndCall)
}
holder.statusView.isVisible = false
when (attributes.callKind) {
@ -101,6 +96,8 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
when (attributes.callStatus) {
CallStatus.INVITED -> if (attributes.informationData.sentByMe) {
setText(R.string.call_tile_you_started_call)
}else {
text = context.getString(R.string.call_tile_other_started_call, attributes.userOfInterest.getBestName())
}
CallStatus.IN_CALL -> setText(R.string.call_tile_in_call)
CallStatus.REJECTED -> if (attributes.informationData.sentByMe) {
@ -133,6 +130,8 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
val callKind: CallKind,
val callStatus: CallStatus,
val userOfInterest: MatrixItem,
val isStillActive: Boolean,
val callback: TimelineEventController.Callback? = null,
override val informationData: MessageInformationData,
override val avatarRenderer: AvatarRenderer,
override val messageColorProvider: MessageColorProvider,
@ -152,6 +151,8 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
INVITED,
IN_CALL,
REJECTED,
ENDED
ENDED;
fun isActive() = this == INVITED || this == IN_CALL
}
}

View file

@ -2749,6 +2749,7 @@
<string name="matrix_to_card_title">Matrix Link</string>
<string name="call_tile_you_started_call">You started a call</string>
<string name="call_tile_other_started_call">%1$s started a call</string>
<string name="call_tile_in_call">You\'re currently in this call</string>
<string name="call_tile_you_declined">You declined this call %1$s</string>
<string name="call_tile_other_declined">%1$s declined this call</string>