diff --git a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt index 28524f6a91..4294358ae0 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index c6a5af5843..355bd64380 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -339,12 +339,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis const val INCOMING_RINGING = "INCOMING_RINGING" const val INCOMING_ACCEPT = "INCOMING_ACCEPT" - fun newIntent(context: Context, mxCall: MxCallDetail): Intent { + fun newIntent(context: Context, mxCall: MxCallDetail, mode: String?): Intent { return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.opponentUserId, !mxCall.isOutgoing, mxCall.isVideoCall)) - putExtra(EXTRA_MODE, OUTGOING_CREATED) + putExtra(EXTRA_MODE, mode) } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index c67abb3d72..7a7a6110a1 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -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) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 8891218a11..cf2683e68f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -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() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 8dc85bd8af..a9b309a218 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index d5d94a0ca5..a22f315082 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -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() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 27cd1b24ae..cbd14093c5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -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 { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 20fbe52731..4221bd5015 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -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() 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 } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/CallItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/CallItemFactory.kt index 36acf5d766..42f1542bf6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/CallItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/CallItemFactory.kt @@ -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_() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt index 85f093bfec..06fbb37c55 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt @@ -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() { @@ -62,14 +57,14 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem 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 : AbsBaseMessageItemMatrix Link You started a call + %1$s started a call You\'re currently in this call You declined this call %1$s %1$s declined this call