From 8f5589d3e17a9f92a9be03618c125f4d13339a32 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 14 May 2020 17:05:22 +0200 Subject: [PATCH] Start creating the widget url builder --- .../api/session/widgets/WidgetService.kt | 2 + .../api/session/widgets/WidgetURLBuilder.kt | 25 ++++ .../integrationmanager/IntegrationManager.kt | 45 +++++-- .../IntegrationManagerConfig.kt | 13 +- .../IntegrationManagerConfigExtractor.kt | 6 +- .../session/widgets/DefaultWidgetService.kt | 6 + .../internal/session/widgets/WidgetModule.kt | 4 + .../session/widgets/WidgetURLBuilder.kt | 117 ++++++++++++++++++ .../android/internal/util/StringProvider.kt | 6 + .../res/values/integration_manager_config.xml | 17 +++ 10 files changed, 226 insertions(+), 15 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetURLBuilder.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetURLBuilder.kt create mode 100644 matrix-sdk-android/src/main/res/values/integration_manager_config.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetService.kt index 3e723d6212..7be715e4d6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetService.kt @@ -24,6 +24,8 @@ import im.vector.matrix.android.internal.session.widgets.Widget interface WidgetService { + fun getWidgetURLBuilder(): WidgetURLBuilder + fun getWidgetPostAPIMediator(): WidgetPostAPIMediator fun getRoomWidgets( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetURLBuilder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetURLBuilder.kt new file mode 100644 index 0000000000..059d4d84cd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetURLBuilder.kt @@ -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 = emptyMap(), forceFetchScalarToken: Boolean = false): String + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt index c2578e0660..2adb3378f3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.integrationmanager import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry +import im.vector.matrix.android.R 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.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.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.StringProvider import timber.log.Timber import javax.inject.Inject @@ -50,8 +52,10 @@ import javax.inject.Inject */ @SessionScope internal class IntegrationManager @Inject constructor(private val taskExecutor: TaskExecutor, + private val stringProvider: StringProvider, private val updateUserAccountDataTask: UpdateUserAccountDataTask, - private val accountDataDataSource: AccountDataDataSource) { + private val accountDataDataSource: AccountDataDataSource, + private val configExtractor: IntegrationManagerConfigExtractor) { interface Listener { 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() private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } 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 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() { lifecycleRegistry.currentState = Lifecycle.State.STARTED accountDataDataSource @@ -98,8 +111,11 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor: .observeNotNull(lifecycleOwner) { val integrationManager = it.getOrNull()?.asIntegrationManagerWidgetContent() val config = integrationManager?.extractIntegrationManagerConfig() - if (config != null && config != currentConfig) { - currentConfig = config + val accountConfig = currentConfigs.firstOrNull { currentConfig -> + currentConfig.kind == IntegrationManagerConfig.Kind.ACCOUNT + } + if (config != null && accountConfig == null) { + currentConfigs.add(config) notifyConfigurationChanged(config) } } @@ -109,6 +125,18 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor: lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } + fun hasConfig() = currentConfigs.isNotEmpty() + + fun getOrderedConfigs(): List { + return currentConfigs.sortedBy { + it.kind + } + } + + fun getPreferredConfig(): IntegrationManagerConfig? { + return getOrderedConfigs().firstOrNull() + } + /** * Returns false if the user as disabled integration manager feature */ @@ -250,14 +278,8 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor: //nop } } - */ - fun getConfig(): IntegrationManagerConfig? { - val accountWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.TYPE_WIDGETS) ?: return null - return accountWidgets.asIntegrationManagerWidgetContent()?.extractIntegrationManagerConfig() - } - private fun WidgetContent.extractIntegrationManagerConfig(): IntegrationManagerConfig? { if (url.isNullOrBlank()) { return null @@ -265,7 +287,8 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor: val integrationManagerData = data.toModel() return IntegrationManagerConfig( uiUrl = url, - apiUrl = integrationManagerData?.apiUrl ?: url + apiUrl = integrationManagerData?.apiUrl ?: url, + kind = IntegrationManagerConfig.Kind.ACCOUNT ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt index 2ebbccc522..e05c2a8f5d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt @@ -17,5 +17,14 @@ package im.vector.matrix.android.internal.session.integrationmanager data class IntegrationManagerConfig( val uiUrl: String, - val apiUrl: String -) + val apiUrl: String, + val kind: Kind +) { + + // Order matters, first is preferred + enum class Kind { + ACCOUNT, + HOMESERVER, + DEFAULT + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt index 5750eb8b5d..9c1d16361a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt @@ -17,8 +17,9 @@ package im.vector.matrix.android.internal.session.integrationmanager 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 { val managers = ArrayList() @@ -33,7 +34,8 @@ internal class IntegrationManagerConfigExtractor { && uiUrl!!.startsWith("https://")) { managers.add(IntegrationManagerConfig( apiUrl = apiUrl, - uiUrl = uiUrl + uiUrl = uiUrl, + kind = IntegrationManagerConfig.Kind.HOMESERVER )) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetService.kt index 3a3a20e226..f3e413fa18 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetService.kt @@ -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.widgets.WidgetPostAPIMediator 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 javax.inject.Inject import javax.inject.Provider internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager, + private val widgetURLBuilder: Provider, private val widgetPostAPIMediator: Provider) : WidgetService { + override fun getWidgetURLBuilder(): WidgetURLBuilder { + return widgetURLBuilder.get() + } + override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator { return widgetPostAPIMediator.get() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt index f2c9e99439..19c0cb5f1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt @@ -21,6 +21,7 @@ import dagger.Module import dagger.Provides 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.WidgetURLBuilder import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask import retrofit2.Retrofit @@ -40,6 +41,9 @@ internal abstract class WidgetModule { @Binds abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService + @Binds + abstract fun bindWidgetURLBuilder(widgetURLBuilder: DefaultWidgetURLBuilder): WidgetURLBuilder + @Binds abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetURLBuilder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetURLBuilder.kt new file mode 100644 index 0000000000..ae6e570c19 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetURLBuilder.kt @@ -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 = 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, 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 = whiteListedUrls + for (allowedUrl in allowed) { + if (url.startsWith(allowedUrl)) { + return true + } + } + return false + } + + private fun StringBuilder.appendParamsToUrl(params: Map): 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 + } +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt index 6cba29ceec..61bb575c7d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.util import android.content.res.Resources +import androidx.annotation.ArrayRes import androidx.annotation.NonNull import androidx.annotation.StringRes 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 { return resources.getString(resId, *formatArgs) } + + @Throws(Resources.NotFoundException::class) + fun getStringArray(@ArrayRes id: Int): Array { + return resources.getStringArray(id) + } } diff --git a/matrix-sdk-android/src/main/res/values/integration_manager_config.xml b/matrix-sdk-android/src/main/res/values/integration_manager_config.xml new file mode 100644 index 0000000000..0180706810 --- /dev/null +++ b/matrix-sdk-android/src/main/res/values/integration_manager_config.xml @@ -0,0 +1,17 @@ + + + + + "https://scalar.vector.im/" + "https://scalar.vector.im/api" + "https://scalar.vector.im/api/widgets/jitsi.html" + + + https://scalar.vector.im/_matrix/integrations/v1 + https://scalar.vector.im/api + https://scalar-staging.vector.im/_matrix/integrations/v1 + https://scalar-staging.vector.im/api + https://scalar-staging.riot.im/scalar/api + + + \ No newline at end of file