Check native widget permissions

This commit is contained in:
Valere 2020-08-13 17:46:58 +02:00
parent 3ce1e3e5d9
commit c911d9c7ff
9 changed files with 129 additions and 25 deletions

View file

@ -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<JitsiCallViewState, JitsiCallViewActions, JitsiCallViewEvents>(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),

View file

@ -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"

View file

@ -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()
}

View file

@ -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)

View file

@ -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) {

View file

@ -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()

View file

@ -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) }

View file

@ -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()
}

View file

@ -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<Unit> {
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<Unit> {
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}")