diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt index c9fb8fbccd..2cc1bd1a77 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt @@ -108,8 +108,7 @@ class JitsiService @Inject constructor( this.avatar = userAvatar?.let { URL(it) } } val roomName = session.getRoomSummary(roomId)?.displayName - val properties = session.widgetService().getWidgetComputedUrl(jitsiWidget, themeProvider.isLightTheme()) - ?.let { url -> jitsiWidgetPropertiesFactory.create(url) } ?: throw IllegalStateException() + val properties = extractProperties(jitsiWidget) ?: throw IllegalStateException() val token = if (jitsiWidget.isOpenIdJWTAuthenticationRequired()) { getOpenIdJWTToken(roomId, properties.domain, userDisplayName ?: session.myUserId, userAvatar ?: "") @@ -126,6 +125,11 @@ class JitsiService @Inject constructor( ) } + fun extractProperties(jitsiWidget: Widget): JitsiWidgetProperties? { + return session.widgetService().getWidgetComputedUrl(jitsiWidget, themeProvider.isLightTheme()) + ?.let { url -> jitsiWidgetPropertiesFactory.create(url) } + } + private fun Widget.isOpenIdJWTAuthenticationRequired(): Boolean { return widgetContent.data[JITSI_AUTH_KEY] == JITSI_OPEN_ID_TOKEN_JWT_AUTH } 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 c0e73823e4..6d13fa05cc 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,7 @@ package im.vector.app.features.home.room.detail import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewModelAction +import org.jitsi.meet.sdk.BroadcastEvent 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 @@ -92,6 +93,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class EnsureNativeWidgetAllowed(val widget: Widget, val userJustAccepted: Boolean, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() + data class UpdateJoinJitsiCallStatus(val broadcastEvent: BroadcastEvent): RoomDetailAction() data class OpenOrCreateDm(val userId: String) : RoomDetailAction() data class JumpToReadReceipt(val userId: String) : RoomDetailAction() @@ -101,6 +103,7 @@ sealed class RoomDetailAction : VectorViewModelAction { object QuickActionSetTopic : RoomDetailAction() data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val transitionView: View?) : RoomDetailAction() + // Preview URL data class DoNotShowPreviewUrlFor(val eventId: String, val url: 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 3e3964ba98..b320bb9291 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 @@ -120,7 +120,9 @@ import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs import im.vector.app.features.attachments.toGroupedContentAttachmentData import im.vector.app.features.call.SharedKnownCallsViewModel import im.vector.app.features.call.VectorCallActivity +import im.vector.app.features.call.conference.JitsiBroadcastEventObserver import im.vector.app.features.call.conference.JitsiCallViewModel +import im.vector.app.features.call.conference.extractConferenceUrl import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.Command import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity @@ -174,6 +176,7 @@ import nl.dionsegijn.konfetti.models.Shape import nl.dionsegijn.konfetti.models.Size import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.commonmark.parser.Parser +import org.jitsi.meet.sdk.BroadcastEvent import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Event @@ -301,6 +304,7 @@ class RoomDetailFragment @Inject constructor( private lateinit var emojiPopup: EmojiPopup override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + lifecycle.addObserver(JitsiBroadcastEventObserver(vectorBaseActivity, this::onBroadcastEvent)) super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java) @@ -412,6 +416,10 @@ class RoomDetailFragment @Inject constructor( } } + private fun onBroadcastEvent(event: BroadcastEvent) { + roomDetailViewModel.handle(RoomDetailAction.UpdateJoinJitsiCallStatus(event)) + } + private fun acceptIncomingCall(event: RoomDetailViewEvents.DisplayAndAcceptCall) { val intent = VectorCallActivity.newIntent( context = vectorBaseActivity, 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 9d7c6eb6aa..fe40ff09d8 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 @@ -39,6 +39,7 @@ import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.call.conference.JitsiService +import im.vector.app.features.call.conference.extractConferenceUrl import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.CommandParser @@ -63,8 +64,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer +import org.jitsi.meet.sdk.BroadcastEvent import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -236,8 +239,11 @@ class RoomDetailViewModel @AssistedInject constructor( .map { widgets -> widgets.filter { it.isActive } } - .execute { - copy(activeRoomWidgets = it) + .execute { widgets -> + val jitsiConfId = widgets()?.firstOrNull { it.type == WidgetType.Jitsi }?.let { jitsiWidget -> + jitsiService.extractProperties(jitsiWidget)?.confId + } + copy(activeRoomWidgets = widgets, jitsiConfId = jitsiConfId) } } @@ -303,6 +309,7 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.EndCall -> handleEndCall() is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) + is RoomDetailAction.UpdateJoinJitsiCallStatus -> handleJitsiCallJoinStatus(action) is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) is RoomDetailAction.CancelSend -> handleCancel(action) @@ -323,6 +330,25 @@ class RoomDetailViewModel @AssistedInject constructor( }.exhaustive } + private fun handleJitsiCallJoinStatus(action: RoomDetailAction.UpdateJoinJitsiCallStatus) = withState { state -> + if (state.jitsiConfId == null) { + // If jitsi widget is removed while on the call + if (state.hasJoinedActiveJitsiConference) { + setState { copy(hasJoinedActiveJitsiConference = false) } + } + return@withState + } + when (action.broadcastEvent.type) { + BroadcastEvent.Type.CONFERENCE_JOINED, + BroadcastEvent.Type.CONFERENCE_TERMINATED -> { + if (action.broadcastEvent.extractConferenceUrl()?.endsWith(state.jitsiConfId).orFalse()) { + setState { copy(hasJoinedActiveJitsiConference = action.broadcastEvent.type == BroadcastEvent.Type.CONFERENCE_JOINED) } + } + } + else -> Unit + } + } + private fun handleAcceptCall(action: RoomDetailAction.AcceptCall) { callManager.getCallById(action.callId)?.also { _viewEvents.post(RoomDetailViewEvents.DisplayAndAcceptCall(it)) @@ -607,7 +633,7 @@ class RoomDetailViewModel @AssistedInject constructor( R.id.invite -> state.canInvite R.id.open_matrix_apps -> true R.id.voice_call -> state.isWebRTCCallOptionAvailable() - R.id.video_call -> state.isWebRTCCallOptionAvailable() || !state.hasActiveJitsiWidget() + R.id.video_call -> true R.id.search -> true R.id.dev_tools -> vectorPreferences.developerMode() else -> false diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 0b9d18db9b..9304ddcba3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -19,7 +19,6 @@ package im.vector.app.features.home.room.detail import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary @@ -27,7 +26,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.widgets.model.Widget -import org.matrix.android.sdk.api.session.widgets.model.WidgetType /** * Describes the current send mode: @@ -77,7 +75,10 @@ data class RoomDetailViewState( val canInvite: Boolean = true, val isAllowedToManageWidgets: Boolean = false, val isAllowedToStartWebRTCCall: Boolean = true, - val hasFailedSending: Boolean = false + val hasFailedSending: Boolean = false, + val hasJoinedActiveJitsiConference: Boolean = false, + // Not null if we have an active jitsi widget on the room + val jitsiConfId: String? = null ) : MvRxState { constructor(args: RoomDetailArgs) : this( @@ -89,7 +90,7 @@ data class RoomDetailViewState( fun isWebRTCCallOptionAvailable() = (asyncRoomSummary.invoke()?.joinedMembersCount ?: 0) <= 2 - fun hasActiveJitsiWidget() = activeRoomWidgets()?.any { it.type == WidgetType.Jitsi }.orFalse() + fun hasActiveJitsiWidget() =jitsiConfId != null fun isDm() = asyncRoomSummary()?.isDirect == true }