mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-03 12:37:31 +01:00
Jitsi widget: refact a bit so we use data instead of url when possible
This commit is contained in:
parent
dae035aa76
commit
84e6a67a51
@ -52,7 +52,7 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage
|
||||
return widgetManager.getWidgetComputedUrl(widget, isLightTheme)
|
||||
}
|
||||
|
||||
override fun getRoomWidgetsLive(
|
||||
override fun getRoomWidgetsLive(
|
||||
roomId: String,
|
||||
widgetId: QueryStringValue,
|
||||
widgetTypes: Set<String>?,
|
||||
|
@ -44,12 +44,16 @@ class JitsiService @Inject constructor(
|
||||
private val rawService: RawService,
|
||||
private val stringProvider: StringProvider,
|
||||
private val themeProvider: ThemeProvider,
|
||||
private val jitsiWidgetPropertiesFactory: JitsiWidgetPropertiesFactory,
|
||||
private val jitsiJWTFactory: JitsiJWTFactory) {
|
||||
|
||||
companion object {
|
||||
const val JITSI_OPEN_ID_TOKEN_JWT_AUTH = "openidtoken-jwt"
|
||||
private const val JITSI_AUTH_KEY = "auth"
|
||||
}
|
||||
|
||||
private val jitsiWidgetDataFactory by lazy {
|
||||
JitsiWidgetDataFactory(stringProvider.getString(R.string.preferred_jitsi_domain)) { widget ->
|
||||
session.widgetService().getWidgetComputedUrl(widget, themeProvider.isLightTheme())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun createJitsiWidget(roomId: String, withVideo: Boolean): Widget {
|
||||
@ -85,17 +89,11 @@ class JitsiService @Inject constructor(
|
||||
val widgetEventContent = mapOf(
|
||||
"url" to url,
|
||||
"type" to WidgetType.Jitsi.legacy,
|
||||
"data" to mapOf(
|
||||
"conferenceId" to confId,
|
||||
"domain" to jitsiDomain,
|
||||
"isAudioOnly" to !withVideo,
|
||||
JITSI_AUTH_KEY to jitsiAuth
|
||||
),
|
||||
"data" to JitsiWidgetData(jitsiDomain, confId, !withVideo, jitsiAuth),
|
||||
"creatorUserId" to session.myUserId,
|
||||
"id" to widgetId,
|
||||
"name" to "jitsi"
|
||||
)
|
||||
|
||||
return session.widgetService().createRoomWidget(roomId, widgetId, widgetEventContent)
|
||||
}
|
||||
|
||||
@ -108,26 +106,24 @@ 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 token = if (jitsiWidget.isOpenIdJWTAuthenticationRequired()) {
|
||||
getOpenIdJWTToken(roomId, properties.domain, userDisplayName ?: session.myUserId, userAvatar ?: "")
|
||||
val widgetData = jitsiWidgetDataFactory.create(jitsiWidget)
|
||||
val token = if (widgetData.isOpenIdJWTAuthenticationRequired()) {
|
||||
getOpenIdJWTToken(roomId, widgetData.domain, userDisplayName ?: session.myUserId, userAvatar ?: "")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return JitsiCallViewEvents.JoinConference(
|
||||
enableVideo = enableVideo,
|
||||
jitsiUrl = properties.domain.ensureProtocol(),
|
||||
jitsiUrl = widgetData.domain.ensureProtocol(),
|
||||
subject = roomName ?: "",
|
||||
confId = properties.confId ?: "",
|
||||
confId = widgetData.confId,
|
||||
userInfo = userInfo,
|
||||
token = token
|
||||
)
|
||||
}
|
||||
|
||||
private fun Widget.isOpenIdJWTAuthenticationRequired(): Boolean {
|
||||
return widgetContent.data[JITSI_AUTH_KEY] == JITSI_OPEN_ID_TOKEN_JWT_AUTH
|
||||
private fun JitsiWidgetData.isOpenIdJWTAuthenticationRequired(): Boolean {
|
||||
return auth == JITSI_OPEN_ID_TOKEN_JWT_AUTH
|
||||
}
|
||||
|
||||
private suspend fun getOpenIdJWTToken(roomId: String, domain: String, userDisplayName: String, userAvatar: String): String {
|
||||
|
@ -16,9 +16,17 @@
|
||||
|
||||
package im.vector.app.features.call.conference
|
||||
|
||||
data class JitsiWidgetProperties(
|
||||
val domain: String,
|
||||
val confId: String?,
|
||||
val displayName: String?,
|
||||
val avatarUrl: String?
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* This is jitsi widget data
|
||||
* https://github.com/matrix-org/matrix-doc/blob/b910b8966524febe7ffe78f723127a5037defe64/api/widgets/definitions/jitsi_data.yaml
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class JitsiWidgetData(
|
||||
@Json(name = "domain") val domain: String,
|
||||
@Json(name = "conferenceId") val confId: String,
|
||||
@Json(name = "isAudioOnly") val isAudioOnly: Boolean = false,
|
||||
@Json(name = "auth") val auth: String? = null
|
||||
)
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.call.conference
|
||||
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
|
||||
class JitsiWidgetDataFactory(private val fallbackJitsiDomain: String, private val urlComputer: (Widget) -> String?) {
|
||||
|
||||
/**
|
||||
* Extract JitsiWidgetData from a widget.
|
||||
* For Widget V2, it will extract data from content.data
|
||||
* For Widget V1, it will extract data from url.
|
||||
*/
|
||||
fun create(widget: Widget): JitsiWidgetData {
|
||||
return widget.widgetContent.data.toModel<JitsiWidgetData>() ?: widget.createFromUrl()
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a JitsiWidgetData from the url.
|
||||
* It's a fallback for Widget V1.
|
||||
* It first get the computed url and then tries to extract JitsiWidgetData from it.
|
||||
*/
|
||||
private fun Widget.createFromUrl(): JitsiWidgetData {
|
||||
return urlComputer(this)?.let { url -> createFromUrl(url) } ?: throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun createFromUrl(url: String): JitsiWidgetData {
|
||||
val configString = tryOrNull { URL(url) }?.query
|
||||
val configs = configString?.split("&")
|
||||
?.map { it.split("=") }
|
||||
?.filter { it.size == 2 }
|
||||
?.map { (key, value) -> key to URLDecoder.decode(value, "UTF-8") }
|
||||
?.toMap()
|
||||
.orEmpty()
|
||||
|
||||
return JitsiWidgetData(
|
||||
domain = configs["conferenceDomain"] ?: fallbackJitsiDomain,
|
||||
confId = configs["conferenceId"] ?: configs["confId"] ?: throw IllegalStateException(),
|
||||
isAudioOnly = configs["isAudioOnly"].toBoolean(),
|
||||
auth = configs["auth"]
|
||||
)
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.call.conference
|
||||
|
||||
import android.net.Uri
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import java.net.URLDecoder
|
||||
import javax.inject.Inject
|
||||
|
||||
class JitsiWidgetPropertiesFactory @Inject constructor(
|
||||
private val stringProvider: StringProvider
|
||||
) {
|
||||
fun create(url: String): JitsiWidgetProperties {
|
||||
val configString = tryOrNull { Uri.parse(url) }?.encodedQuery
|
||||
val configs = configString?.split("&")
|
||||
?.map { it.split("=") }
|
||||
?.filter { it.size == 2 }
|
||||
?.map { (key, value) -> key to URLDecoder.decode(value, "UTF-8") }
|
||||
?.toMap()
|
||||
.orEmpty()
|
||||
|
||||
return JitsiWidgetProperties(
|
||||
domain = configs["conferenceDomain"] ?: stringProvider.getString(R.string.preferred_jitsi_domain),
|
||||
confId = configs["conferenceId"] ?: configs["confId"],
|
||||
displayName = configs["displayName"],
|
||||
avatarUrl = configs["avatarUrl"]
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.call.conference
|
||||
|
||||
import org.amshove.kluent.internal.assertFails
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||
import org.matrix.android.sdk.api.util.appendParamToUrl
|
||||
|
||||
private const val DOMAIN = "DOMAIN"
|
||||
private const val CONF_ID = "CONF_ID"
|
||||
private const val USER_ID = "USER_ID"
|
||||
private const val WIDGET_ID = "WIDGET_ID"
|
||||
|
||||
class JitsiWidgetDataFactoryTest {
|
||||
|
||||
private val jitsiWidgetDataFactory = JitsiWidgetDataFactory(DOMAIN) { widget ->
|
||||
// we don't need to compute here.
|
||||
widget.widgetContent.url
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jitsiWidget_V2_success() {
|
||||
val widget = createWidgetV2()
|
||||
val widgetData = jitsiWidgetDataFactory.create(widget)
|
||||
assertEquals(widgetData.confId, CONF_ID)
|
||||
assertEquals(widgetData.domain, DOMAIN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jitsiWidget_V1_success() {
|
||||
val widget = createWidgetV1(true)
|
||||
val widgetData = jitsiWidgetDataFactory.create(widget)
|
||||
assertEquals(widgetData.confId, CONF_ID)
|
||||
assertEquals(widgetData.domain, DOMAIN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jitsiWidget_V1_failure() {
|
||||
val widget = createWidgetV1(false)
|
||||
assertFails {
|
||||
jitsiWidgetDataFactory.create(widget)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createWidgetV1(successful: Boolean): Widget {
|
||||
val url = buildString {
|
||||
append("https://app.element.io/jitsi.html")
|
||||
if (successful) {
|
||||
appendParamToUrl("confId", CONF_ID)
|
||||
}
|
||||
append("#conferenceDomain=\$domain")
|
||||
append("&conferenceId=\$conferenceId")
|
||||
append("&isAudioOnly=\$isAudioOnly")
|
||||
append("&displayName=\$matrix_display_name")
|
||||
append("&avatarUrl=\$matrix_avatar_url")
|
||||
append("&userId=\$matrix_user_id")
|
||||
append("&roomId=\$matrix_room_id")
|
||||
append("&theme=\$theme")
|
||||
}
|
||||
val widgetEventContent = mapOf(
|
||||
"url" to url,
|
||||
"type" to WidgetType.Jitsi.preferred,
|
||||
"data" to mapOf(
|
||||
"widgetSessionId" to WIDGET_ID
|
||||
),
|
||||
"creatorUserId" to USER_ID,
|
||||
"id" to WIDGET_ID,
|
||||
"name" to "jitsi"
|
||||
)
|
||||
return createWidgetWithContent(widgetEventContent)
|
||||
}
|
||||
|
||||
private fun createWidgetV2(): Widget {
|
||||
val widgetEventContent = mapOf(
|
||||
// We don't care of url here because we have data field
|
||||
"url" to "url",
|
||||
"type" to WidgetType.Jitsi.preferred,
|
||||
"data" to JitsiWidgetData(DOMAIN, CONF_ID, false).toContent(),
|
||||
"creatorUserId" to USER_ID,
|
||||
"id" to WIDGET_ID,
|
||||
"name" to "jitsi"
|
||||
)
|
||||
return createWidgetWithContent(widgetEventContent)
|
||||
}
|
||||
|
||||
private fun createWidgetWithContent(widgetContent: Content): Widget {
|
||||
val event = Event(type = EventType.STATE_ROOM_WIDGET, eventId = "eventId", content = widgetContent)
|
||||
val widgetContentModel = widgetContent.toModel<WidgetContent>()
|
||||
return Widget(
|
||||
widgetContent = widgetContentModel!!,
|
||||
event = event,
|
||||
widgetId = WIDGET_ID,
|
||||
senderInfo = SenderInfo(USER_ID, null, false, null),
|
||||
isAddedByMe = true,
|
||||
type = WidgetType.Jitsi
|
||||
)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user