mirror of
https://github.com/element-hq/element-android
synced 2024-10-26 04:36:48 +03:00
Start creating the widget url builder
This commit is contained in:
parent
03389fc040
commit
8f5589d3e1
10 changed files with 226 additions and 15 deletions
|
@ -24,6 +24,8 @@ import im.vector.matrix.android.internal.session.widgets.Widget
|
||||||
|
|
||||||
interface WidgetService {
|
interface WidgetService {
|
||||||
|
|
||||||
|
fun getWidgetURLBuilder(): WidgetURLBuilder
|
||||||
|
|
||||||
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
||||||
|
|
||||||
fun getRoomWidgets(
|
fun getRoomWidgets(
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.matrix.android.api.session.widgets
|
||||||
|
|
||||||
|
interface WidgetURLBuilder {
|
||||||
|
/**
|
||||||
|
* Takes care of fetching a scalar token if required and build the final url.
|
||||||
|
*/
|
||||||
|
suspend fun build(baseUrl: String, params: Map<String, String> = emptyMap(), forceFetchScalarToken: Boolean = false): String
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.integrationmanager
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.LifecycleRegistry
|
import androidx.lifecycle.LifecycleRegistry
|
||||||
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
@ -33,6 +34,7 @@ import im.vector.matrix.android.internal.session.user.accountdata.AccountDataDat
|
||||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -50,8 +52,10 @@ import javax.inject.Inject
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class IntegrationManager @Inject constructor(private val taskExecutor: TaskExecutor,
|
internal class IntegrationManager @Inject constructor(private val taskExecutor: TaskExecutor,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
private val accountDataDataSource: AccountDataDataSource) {
|
private val accountDataDataSource: AccountDataDataSource,
|
||||||
|
private val configExtractor: IntegrationManagerConfigExtractor) {
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun onIsEnabledChanged(enabled: Boolean) {
|
fun onIsEnabledChanged(enabled: Boolean) {
|
||||||
|
@ -67,7 +71,7 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentConfig: IntegrationManagerConfig? = null
|
private val currentConfigs = ArrayList<IntegrationManagerConfig>()
|
||||||
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry }
|
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry }
|
||||||
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
|
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
|
||||||
|
|
||||||
|
@ -75,6 +79,15 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
fun addListener(listener: Listener) = synchronized(listeners) { listeners.add(listener) }
|
fun addListener(listener: Listener) = synchronized(listeners) { listeners.add(listener) }
|
||||||
fun removeListener(listener: Listener) = synchronized(listeners) { listeners.remove(listener) }
|
fun removeListener(listener: Listener) = synchronized(listeners) { listeners.remove(listener) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
val defaultConfig = IntegrationManagerConfig(
|
||||||
|
uiUrl = stringProvider.getString(R.string.integrations_ui_url),
|
||||||
|
apiUrl = stringProvider.getString(R.string.integrations_rest_url),
|
||||||
|
kind = IntegrationManagerConfig.Kind.DEFAULT
|
||||||
|
)
|
||||||
|
currentConfigs.add(defaultConfig)
|
||||||
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
lifecycleRegistry.currentState = Lifecycle.State.STARTED
|
lifecycleRegistry.currentState = Lifecycle.State.STARTED
|
||||||
accountDataDataSource
|
accountDataDataSource
|
||||||
|
@ -98,8 +111,11 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
.observeNotNull(lifecycleOwner) {
|
.observeNotNull(lifecycleOwner) {
|
||||||
val integrationManager = it.getOrNull()?.asIntegrationManagerWidgetContent()
|
val integrationManager = it.getOrNull()?.asIntegrationManagerWidgetContent()
|
||||||
val config = integrationManager?.extractIntegrationManagerConfig()
|
val config = integrationManager?.extractIntegrationManagerConfig()
|
||||||
if (config != null && config != currentConfig) {
|
val accountConfig = currentConfigs.firstOrNull { currentConfig ->
|
||||||
currentConfig = config
|
currentConfig.kind == IntegrationManagerConfig.Kind.ACCOUNT
|
||||||
|
}
|
||||||
|
if (config != null && accountConfig == null) {
|
||||||
|
currentConfigs.add(config)
|
||||||
notifyConfigurationChanged(config)
|
notifyConfigurationChanged(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +125,18 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasConfig() = currentConfigs.isNotEmpty()
|
||||||
|
|
||||||
|
fun getOrderedConfigs(): List<IntegrationManagerConfig> {
|
||||||
|
return currentConfigs.sortedBy {
|
||||||
|
it.kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPreferredConfig(): IntegrationManagerConfig? {
|
||||||
|
return getOrderedConfigs().firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false if the user as disabled integration manager feature
|
* Returns false if the user as disabled integration manager feature
|
||||||
*/
|
*/
|
||||||
|
@ -250,14 +278,8 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
//nop
|
//nop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun getConfig(): IntegrationManagerConfig? {
|
|
||||||
val accountWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.TYPE_WIDGETS) ?: return null
|
|
||||||
return accountWidgets.asIntegrationManagerWidgetContent()?.extractIntegrationManagerConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun WidgetContent.extractIntegrationManagerConfig(): IntegrationManagerConfig? {
|
private fun WidgetContent.extractIntegrationManagerConfig(): IntegrationManagerConfig? {
|
||||||
if (url.isNullOrBlank()) {
|
if (url.isNullOrBlank()) {
|
||||||
return null
|
return null
|
||||||
|
@ -265,7 +287,8 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor:
|
||||||
val integrationManagerData = data.toModel<IntegrationManagerWidgetData>()
|
val integrationManagerData = data.toModel<IntegrationManagerWidgetData>()
|
||||||
return IntegrationManagerConfig(
|
return IntegrationManagerConfig(
|
||||||
uiUrl = url,
|
uiUrl = url,
|
||||||
apiUrl = integrationManagerData?.apiUrl ?: url
|
apiUrl = integrationManagerData?.apiUrl ?: url,
|
||||||
|
kind = IntegrationManagerConfig.Kind.ACCOUNT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,5 +17,14 @@ package im.vector.matrix.android.internal.session.integrationmanager
|
||||||
|
|
||||||
data class IntegrationManagerConfig(
|
data class IntegrationManagerConfig(
|
||||||
val uiUrl: String,
|
val uiUrl: String,
|
||||||
val apiUrl: String
|
val apiUrl: String,
|
||||||
)
|
val kind: Kind
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Order matters, first is preferred
|
||||||
|
enum class Kind {
|
||||||
|
ACCOUNT,
|
||||||
|
HOMESERVER,
|
||||||
|
DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
package im.vector.matrix.android.internal.session.integrationmanager
|
package im.vector.matrix.android.internal.session.integrationmanager
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.WellKnown
|
import im.vector.matrix.android.api.auth.data.WellKnown
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class IntegrationManagerConfigExtractor {
|
internal class IntegrationManagerConfigExtractor @Inject constructor() {
|
||||||
|
|
||||||
fun extract(wellKnown: WellKnown): List<IntegrationManagerConfig> {
|
fun extract(wellKnown: WellKnown): List<IntegrationManagerConfig> {
|
||||||
val managers = ArrayList<IntegrationManagerConfig>()
|
val managers = ArrayList<IntegrationManagerConfig>()
|
||||||
|
@ -33,7 +34,8 @@ internal class IntegrationManagerConfigExtractor {
|
||||||
&& uiUrl!!.startsWith("https://")) {
|
&& uiUrl!!.startsWith("https://")) {
|
||||||
managers.add(IntegrationManagerConfig(
|
managers.add(IntegrationManagerConfig(
|
||||||
apiUrl = apiUrl,
|
apiUrl = apiUrl,
|
||||||
uiUrl = uiUrl
|
uiUrl = uiUrl,
|
||||||
|
kind = IntegrationManagerConfig.Kind.HOMESERVER
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,19 @@ import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetService
|
import im.vector.matrix.android.api.session.widgets.WidgetService
|
||||||
|
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
|
||||||
internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager,
|
internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager,
|
||||||
|
private val widgetURLBuilder: Provider<WidgetURLBuilder>,
|
||||||
private val widgetPostAPIMediator: Provider<WidgetPostAPIMediator>) : WidgetService {
|
private val widgetPostAPIMediator: Provider<WidgetPostAPIMediator>) : WidgetService {
|
||||||
|
|
||||||
|
override fun getWidgetURLBuilder(): WidgetURLBuilder {
|
||||||
|
return widgetURLBuilder.get()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
||||||
return widgetPostAPIMediator.get()
|
return widgetPostAPIMediator.get()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetService
|
import im.vector.matrix.android.api.session.widgets.WidgetService
|
||||||
|
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
||||||
import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask
|
import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask
|
||||||
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
@ -40,6 +41,9 @@ internal abstract class WidgetModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService
|
abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindWidgetURLBuilder(widgetURLBuilder: DefaultWidgetURLBuilder): WidgetURLBuilder
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
|
abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.matrix.android.internal.session.widgets
|
||||||
|
|
||||||
|
import im.vector.matrix.android.R
|
||||||
|
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
||||||
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
|
import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager
|
||||||
|
import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManagerConfig
|
||||||
|
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
||||||
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SessionScope
|
||||||
|
internal class DefaultWidgetURLBuilder @Inject constructor(private val integrationManager: IntegrationManager,
|
||||||
|
private val getScalarTokenTask: GetScalarTokenTask,
|
||||||
|
private val stringProvider: StringProvider
|
||||||
|
) : IntegrationManager.Listener, WidgetURLBuilder {
|
||||||
|
|
||||||
|
private var currentConfig: IntegrationManagerConfig? = null
|
||||||
|
private var whiteListedUrls: List<String> = emptyList()
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
setupWithConfiguration()
|
||||||
|
integrationManager.addListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
integrationManager.removeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(config: IntegrationManagerConfig) {
|
||||||
|
setupWithConfiguration()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupWithConfiguration() {
|
||||||
|
val preferredConfig = integrationManager.getPreferredConfig()
|
||||||
|
if (currentConfig != preferredConfig) {
|
||||||
|
currentConfig = preferredConfig
|
||||||
|
val defaultWhiteList = stringProvider.getStringArray(R.array.integrations_widgets_urls).asList()
|
||||||
|
whiteListedUrls = when (preferredConfig?.kind) {
|
||||||
|
IntegrationManagerConfig.Kind.DEFAULT -> defaultWhiteList
|
||||||
|
IntegrationManagerConfig.Kind.ACCOUNT -> defaultWhiteList + preferredConfig.apiUrl
|
||||||
|
IntegrationManagerConfig.Kind.HOMESERVER -> listOf(preferredConfig.apiUrl)
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes care of fetching a scalar token if required and build the final url.
|
||||||
|
*/
|
||||||
|
override suspend fun build(baseUrl: String, params: Map<String, String>, forceFetchScalarToken: Boolean): String {
|
||||||
|
return if (isScalarUrl(baseUrl) || forceFetchScalarToken) {
|
||||||
|
val taskParams = GetScalarTokenTask.Params(baseUrl)
|
||||||
|
val scalarToken = getScalarTokenTask.execute(taskParams)
|
||||||
|
buildString {
|
||||||
|
append(baseUrl)
|
||||||
|
append("scalar_token", scalarToken)
|
||||||
|
appendParamsToUrl(params)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildString {
|
||||||
|
append(baseUrl)
|
||||||
|
appendParamsToUrl(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isScalarUrl(url: String): Boolean {
|
||||||
|
val allowed: List<String> = whiteListedUrls
|
||||||
|
for (allowedUrl in allowed) {
|
||||||
|
if (url.startsWith(allowedUrl)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendParamsToUrl(params: Map<String, String>): StringBuilder {
|
||||||
|
params.forEach { (param, value) ->
|
||||||
|
appendParamToUrl(param, value)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
|
||||||
|
if (contains("?")) {
|
||||||
|
append("&")
|
||||||
|
} else {
|
||||||
|
append("?")
|
||||||
|
}
|
||||||
|
|
||||||
|
append(param)
|
||||||
|
append("=")
|
||||||
|
append(URLEncoder.encode(value, "utf-8"))
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.internal.util
|
package im.vector.matrix.android.internal.util
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import androidx.annotation.ArrayRes
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import dagger.Reusable
|
import dagger.Reusable
|
||||||
|
@ -53,4 +54,9 @@ internal class StringProvider @Inject constructor(private val resources: Resourc
|
||||||
fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String {
|
fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String {
|
||||||
return resources.getString(resId, *formatArgs)
|
return resources.getString(resId, *formatArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(Resources.NotFoundException::class)
|
||||||
|
fun getStringArray(@ArrayRes id: Int): Array<String> {
|
||||||
|
return resources.getStringArray(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Widget urls -->
|
||||||
|
<string name="integrations_ui_url" translatable="false">"https://scalar.vector.im/"</string>
|
||||||
|
<string name="integrations_rest_url" translatable="false">"https://scalar.vector.im/api"</string>
|
||||||
|
<string name="integrations_jitsi_widget_url" translatable="false">"https://scalar.vector.im/api/widgets/jitsi.html"</string>
|
||||||
|
|
||||||
|
<string-array name="integrations_widgets_urls" translatable="false">
|
||||||
|
<item>https://scalar.vector.im/_matrix/integrations/v1</item>
|
||||||
|
<item>https://scalar.vector.im/api</item>
|
||||||
|
<item>https://scalar-staging.vector.im/_matrix/integrations/v1</item>
|
||||||
|
<item>https://scalar-staging.vector.im/api</item>
|
||||||
|
<item>https://scalar-staging.riot.im/scalar/api</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
Loading…
Reference in a new issue