Start creating the widget url builder
This commit is contained in:
parent
03389fc040
commit
8f5589d3e1
@ -24,6 +24,8 @@ import im.vector.matrix.android.internal.session.widgets.Widget
|
||||
|
||||
interface WidgetService {
|
||||
|
||||
fun getWidgetURLBuilder(): WidgetURLBuilder
|
||||
|
||||
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
||||
|
||||
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.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<IntegrationManagerConfig>()
|
||||
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<IntegrationManagerConfig> {
|
||||
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<IntegrationManagerWidgetData>()
|
||||
return IntegrationManagerConfig(
|
||||
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(
|
||||
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
|
||||
|
||||
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> {
|
||||
val managers = ArrayList<IntegrationManagerConfig>()
|
||||
@ -33,7 +34,8 @@ internal class IntegrationManagerConfigExtractor {
|
||||
&& uiUrl!!.startsWith("https://")) {
|
||||
managers.add(IntegrationManagerConfig(
|
||||
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.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<WidgetURLBuilder>,
|
||||
private val widgetPostAPIMediator: Provider<WidgetPostAPIMediator>) : WidgetService {
|
||||
|
||||
override fun getWidgetURLBuilder(): WidgetURLBuilder {
|
||||
return widgetURLBuilder.get()
|
||||
}
|
||||
|
||||
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
||||
return widgetPostAPIMediator.get()
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
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<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…
x
Reference in New Issue
Block a user