From c911d9c7ffe283f227035f3cc4ad64065d4b8269 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 13 Aug 2020 17:46:58 +0200 Subject: [PATCH] Check native widget permissions --- .../call/conference/JitsiCallViewModel.kt | 6 +- .../call/conference/JitsiWidgetProperties.kt | 7 ++- .../home/room/detail/RoomDetailAction.kt | 2 + .../home/room/detail/RoomDetailActivity.kt | 11 +++- .../home/room/detail/RoomDetailFragment.kt | 41 ++++++++++++- .../home/room/detail/RoomDetailViewEvents.kt | 1 + .../home/room/detail/RoomDetailViewModel.kt | 20 ++++++ .../RoomWidgetPermissionBottomSheet.kt | 5 ++ .../RoomWidgetPermissionViewModel.kt | 61 ++++++++++++++----- 9 files changed, 129 insertions(+), 25 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index e5f031fbef..49e888d68e 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -26,6 +26,7 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction +import im.vector.app.core.resources.StringProvider import im.vector.app.features.call.WebRtcPeerConnectionManager import org.jitsi.meet.sdk.JitsiMeetUserInfo import org.matrix.android.sdk.api.query.QueryStringValue @@ -43,7 +44,8 @@ class JitsiCallViewModel @AssistedInject constructor( @Assisted initialState: JitsiCallViewState, @Assisted val args: VectorJitsiActivity.Args, val session: Session, - val webRtcPeerConnectionManager: WebRtcPeerConnectionManager + val webRtcPeerConnectionManager: WebRtcPeerConnectionManager, + val stringProvider: StringProvider ) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -71,7 +73,7 @@ class JitsiCallViewModel @AssistedInject constructor( if (jitsiWidget != null) { val uri = Uri.parse(jitsiWidget.computedUrl) val confId = uri.getQueryParameter("confId") - val ppt = jitsiWidget.computedUrl?.let { JitsiWidgetProperties(it) } + val ppt = jitsiWidget.computedUrl?.let { JitsiWidgetProperties(it, stringProvider) } setState { copy( widget = Success(jitsiWidget), diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiWidgetProperties.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiWidgetProperties.kt index c8d8f1f27f..1ee1f67133 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiWidgetProperties.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiWidgetProperties.kt @@ -17,9 +17,11 @@ package im.vector.app.features.call.conference import android.net.Uri +import im.vector.app.R +import im.vector.app.core.resources.StringProvider -class JitsiWidgetProperties(private val uriString: String) { - val domain: String by lazy { configs["conferenceDomain"] ?: DEFAULT_JITSI_DOMAIN } +class JitsiWidgetProperties(private val uriString: String, val stringProvider: StringProvider) { + val domain: String by lazy { configs["conferenceDomain"] ?: stringProvider.getString(R.string.preferred_jitsi_domain) } val displayName: String? by lazy { configs["displayName"] } val avatarUrl: String? by lazy { configs["avatarUrl"] } @@ -34,4 +36,3 @@ class JitsiWidgetProperties(private val uriString: String) { } } -private const val DEFAULT_JITSI_DOMAIN = "jitsi.riot.im" 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 3e29a399f0..5f3488437f 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 @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageStickerConte import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.widgets.model.Widget sealed class RoomDetailAction : VectorViewModelAction { data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction() @@ -83,4 +84,5 @@ sealed class RoomDetailAction : VectorViewModelAction { object ManageIntegrations: RoomDetailAction() data class AddJitsiWidget(val video: Boolean): RoomDetailAction() data class RemoveWidget(val widgetId: String): RoomDetailAction() + data class EnsureNativeWidgetAllowed(val widget: Widget, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 58eb77b454..aa608ef4ef 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -35,6 +35,8 @@ import im.vector.app.features.room.RequireActiveMembershipAction import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewState +import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel +import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState import kotlinx.android.synthetic.main.activity_room_detail.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import javax.inject.Inject @@ -42,7 +44,8 @@ import javax.inject.Inject class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable, - RequireActiveMembershipViewModel.Factory { + RequireActiveMembershipViewModel.Factory, + RoomWidgetPermissionViewModel.Factory { override fun getLayoutRes() = R.layout.activity_room_detail @@ -57,6 +60,12 @@ class RoomDetailActivity : return requireActiveMembershipViewModelFactory.create(initialState.copy(roomId = currentRoomId ?: "")) } + @Inject + lateinit var permissionsViewModelFactory: RoomWidgetPermissionViewModel.Factory + override fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel { + return permissionsViewModelFactory.create(initialState) + } + override fun injectWith(injector: ScreenComponent) { super.injectWith(injector) injector.inject(this) 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 55bfcd28a6..44d461d9f1 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 @@ -151,6 +151,9 @@ import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.widgets.WidgetActivity +import im.vector.app.features.widgets.WidgetArgs +import im.vector.app.features.widgets.WidgetKind +import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.permalinks.PermalinkFactory import org.matrix.android.sdk.api.session.Session @@ -223,7 +226,7 @@ class RoomDetailFragment @Inject constructor( AttachmentTypeSelectorView.Callback, AttachmentsHelper.Callback, // RoomWidgetsBannerView.Callback, - ActiveCallView.Callback { + ActiveCallView.Callback{ companion object { @@ -360,10 +363,35 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo) RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView() RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView() + is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) }.exhaustive } } + private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) { + val tag = RoomWidgetPermissionBottomSheet::class.java.name + val dFrag = childFragmentManager + .findFragmentByTag(tag) as? RoomWidgetPermissionBottomSheet + if (dFrag != null && dFrag.dialog?.isShowing == true && !dFrag.isRemoving) { + return + } else { + RoomWidgetPermissionBottomSheet + .newInstance(WidgetArgs( + baseUrl = it.domain, + kind = WidgetKind.ROOM, + roomId = roomDetailArgs.roomId, + widgetId = it.widget.widgetId + )).apply { + directListener = { granted -> + if (granted) { + roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(it.widget, it.grantedEvents)) + } + } + } + .show(childFragmentManager, tag) + } + } + private fun openIntegrationManager(screen: String? = null) { navigator.openIntegrationManager( fragment = this, @@ -376,11 +404,18 @@ class RoomDetailFragment @Inject constructor( private fun setupConfBannerView() { activeConferenceView.callback = object : ActiveConferenceView.Callback { override fun onTapJoinAudio(jitsiWidget: Widget) { - joinJitsiRoom(jitsiWidget, false) + // need to check if allowed first + roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed( + jitsiWidget, + RoomDetailViewEvents.JoinJitsiConference(jitsiWidget, false)) + ) } override fun onTapJoinVideo(jitsiWidget: Widget) { - joinJitsiRoom(jitsiWidget, true) + roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed( + jitsiWidget, + RoomDetailViewEvents.JoinJitsiConference(jitsiWidget, true)) + ) } override fun onDelete(jitsiWidget: Widget) { 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 7d0df5288c..7f7ec833f7 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 @@ -73,6 +73,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { object OpenIntegrationManager: RoomDetailViewEvents() object OpenActiveWidgetBottomSheet: RoomDetailViewEvents() + data class RequestNativeWidgetPermission(val widget: Widget, val domain: String, val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents() object MessageSent : SendMessageResult() data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult() 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 8f631a8a81..65ef0db0b9 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 @@ -281,6 +281,7 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) + is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) }.exhaustive } @@ -411,6 +412,25 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun handleCheckWidgetAllowed(action: RoomDetailAction.EnsureNativeWidgetAllowed) { + val widget = action.widget + val domain = action.widget.widgetContent.data["domain"] as? String ?: "" + val isAllowed = if (widget.type == WidgetType.Jitsi) { + widget.senderInfo?.userId == session.myUserId + || session.integrationManagerService().isNativeWidgetDomainAllowed( + action.widget.type.preferred, + domain + ) + } else false + + if (isAllowed) { + _viewEvents.post(action.grantedEvents) + } else { + // we need to request permission + _viewEvents.post(RoomDetailViewEvents.RequestNativeWidgetPermission(widget, domain, action.grantedEvents)) + } + } + private fun startTrackingUnreadMessages() { trackUnreadMessages.set(true) setState { copy(canShowJumpToReadMarker = false) } diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index 84ddf1b6e9..13470daaf7 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -48,6 +48,9 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { injector.inject(this) } + // Use this if you don't need to full activity view model + var directListener: ((Boolean) -> Unit)? = null + override fun invalidate() = withState(viewModel) { state -> super.invalidate() val permissionData = state.permissionData() ?: return@withState @@ -89,6 +92,7 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { fun doDecline() { viewModel.handle(RoomWidgetPermissionActions.BlockWidget) // optimistic dismiss + directListener?.invoke(false) dismiss() } @@ -96,6 +100,7 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { fun doAccept() { viewModel.handle(RoomWidgetPermissionActions.AllowWidget) // optimistic dismiss + directListener?.invoke(true) dismiss() } diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 60098d4e1c..ea55e04103 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -28,6 +28,8 @@ import org.matrix.android.sdk.api.session.Session import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.widgets.model.WidgetType +import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.net.URL @@ -57,20 +59,33 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in } // TODO check from widget urls the perms that should be shown? // For now put all - val infoShared = listOf( - R.string.room_widget_permission_display_name, - R.string.room_widget_permission_avatar_url, - R.string.room_widget_permission_user_id, - R.string.room_widget_permission_theme, - R.string.room_widget_permission_widget_id, - R.string.room_widget_permission_room_id - ) - RoomWidgetPermissionViewState.WidgetPermissionData( - widget = widget, - isWebviewWidget = true, - permissionsList = infoShared, - widgetDomain = domain - ) + if (widget.type == WidgetType.Jitsi) { + val infoShared = listOf( + R.string.room_widget_permission_display_name, + R.string.room_widget_permission_avatar_url + ) + RoomWidgetPermissionViewState.WidgetPermissionData( + widget = widget, + isWebviewWidget = false, + permissionsList = infoShared, + widgetDomain = widget.widgetContent.data["domain"] as? String + ) + } else { + val infoShared = listOf( + R.string.room_widget_permission_display_name, + R.string.room_widget_permission_avatar_url, + R.string.room_widget_permission_user_id, + R.string.room_widget_permission_theme, + R.string.room_widget_permission_widget_id, + R.string.room_widget_permission_room_id + ) + RoomWidgetPermissionViewState.WidgetPermissionData( + widget = widget, + isWebviewWidget = true, + permissionsList = infoShared, + widgetDomain = domain + ) + } } .execute { copy(permissionData = it) @@ -91,7 +106,14 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in if (state.permissionData()?.isWebviewWidget.orFalse()) { WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, false) } else { - // TODO JITSI + awaitCallback { + session.integrationManagerService().setNativeWidgetDomainAllowed( + state.permissionData.invoke()?.widget?.type?.preferred ?: "", + state.permissionData.invoke()?.widgetDomain ?: "", + false, + it + ) + } } } catch (failure: Throwable) { Timber.v("Failure revoking widget: ${state.widgetId}") @@ -109,7 +131,14 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in if (state.permissionData()?.isWebviewWidget.orFalse()) { WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, true) } else { - // TODO JITSI + awaitCallback { + session.integrationManagerService().setNativeWidgetDomainAllowed( + state.permissionData.invoke()?.widget?.type?.preferred ?: "", + state.permissionData.invoke()?.widgetDomain ?: "", + true, + it + ) + } } } catch (failure: Throwable) { Timber.v("Failure allowing widget: ${state.widgetId}")