diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 1b9544bed6..5736e78a30 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService import im.vector.matrix.android.api.session.identity.IdentityService +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.pushers.PushersService import im.vector.matrix.android.api.session.room.RoomDirectoryService @@ -65,8 +66,7 @@ interface Session : HomeServerCapabilitiesService, SecureStorageService, AccountDataService, - AccountService, - WidgetService { + AccountService { /** * The params associated to the session @@ -155,6 +155,16 @@ interface Session : */ fun identityService(): IdentityService + /** + * Returns the widget service associated with the session + */ + fun widgetService(): WidgetService + + /** + * Returns the integration manager service associated with the session + */ + fun integrationManagerService(): IntegrationManagerService + /** * Add a listener to the session. * @param listener the listener to add. 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/api/session/integrationmanager/IntegrationManagerConfig.kt similarity index 92% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/integrationmanager/IntegrationManagerConfig.kt index e05c2a8f5d..14942f6d58 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/api/session/integrationmanager/IntegrationManagerConfig.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package im.vector.matrix.android.internal.session.integrationmanager +package im.vector.matrix.android.api.session.integrationmanager data class IntegrationManagerConfig( val uiUrl: String, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/integrationmanager/IntegrationManagerService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/integrationmanager/IntegrationManagerService.kt new file mode 100644 index 0000000000..08d5700b71 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/integrationmanager/IntegrationManagerService.kt @@ -0,0 +1,39 @@ +/* + * 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.integrationmanager + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable + +interface IntegrationManagerService { + + fun getOrderedConfigs(): List + + fun getPreferredConfig(): IntegrationManagerConfig + + fun isIntegrationEnabled(): Boolean + + fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable + + fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable + + fun isWidgetAllowed(stateEventId: String): Boolean + + fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable + + fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt index b18a7dd97b..697b5213ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt @@ -48,11 +48,30 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { return if (eventType.isNotEmpty() && userId.isNotEmpty()) { val powerLevel = getUserPowerLevel(userId) val minimumPowerLevel = powerLevelsContent.events[eventType] - ?: if (EventType.isStateEvent(eventType)) { - powerLevelsContent.stateDefault - } else { - powerLevelsContent.eventsDefault - } + ?: if (EventType.isStateEvent(eventType)) { + powerLevelsContent.stateDefault + } else { + powerLevelsContent.eventsDefault + } + powerLevel >= minimumPowerLevel + } else false + } + + /** + * Tell if an user can send an event of a certain type + * + * @param eventType the event type to check for + * @param userId the user id + * @return true if the user can send this type of event + */ + fun isAllowedToSend(isState: Boolean, userId: String): Boolean { + return if (userId.isNotEmpty()) { + val powerLevel = getUserPowerLevel(userId) + val minimumPowerLevel = if (isState) { + powerLevelsContent.stateDefault + } else { + powerLevelsContent.eventsDefault + } powerLevel >= minimumPowerLevel } else false } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt index 576747df35..827ce50e13 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt @@ -20,6 +20,8 @@ import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.Optional interface StateService { @@ -27,7 +29,9 @@ interface StateService { /** * Update the topic of the room */ - fun updateTopic(topic: String, callback: MatrixCallback) + fun updateTopic(topic: String, callback: MatrixCallback): Cancelable + + fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict, callback: MatrixCallback): Cancelable fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetPostAPIMediator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetPostAPIMediator.kt index 34848b7d17..4ef5009549 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetPostAPIMediator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetPostAPIMediator.kt @@ -58,10 +58,11 @@ interface WidgetPostAPIMediator { /** * Send an object response * + * @param klass the class of the response * @param response the response * @param eventData the modular data */ - fun sendObjectResponse(response: JsonDict?, eventData: JsonDict) + fun sendObjectResponse(klass: Class, response: T?, eventData: JsonDict) /** * Send success @@ -82,6 +83,6 @@ interface WidgetPostAPIMediator { /** * Triggered when a widget is posting */ - fun handleWidgetRequest(data: JsonDict): Boolean + fun handleWidgetRequest(eventData: JsonDict): Boolean } } 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 7be715e4d6..a76912b11b 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 @@ -16,6 +16,7 @@ package im.vector.matrix.android.api.session.widgets +import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.events.model.Content @@ -35,6 +36,23 @@ interface WidgetService { excludedTypes: Set? = null ): List + fun getRoomWidgetsLive( + roomId: String, + widgetId: QueryStringValue = QueryStringValue.NoCondition, + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): LiveData> + + fun getUserWidgets( + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): List + + fun getUserWidgetsLive( + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): LiveData> + fun createRoomWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback): Cancelable fun destroyRoomWidget(roomId: String, widgetId: String, callback: MatrixCallback): Cancelable diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 605cf5fde7..d62eb7b505 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -34,6 +34,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.pushers.PushersService import im.vector.matrix.android.api.session.room.RoomDirectoryService @@ -46,7 +47,6 @@ import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.terms.TermsService import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.widgets.WidgetService -import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.crypto.DefaultCryptoService import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater @@ -54,13 +54,11 @@ import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.di.SessionId import im.vector.matrix.android.internal.di.WorkManagerProvider import im.vector.matrix.android.internal.session.identity.DefaultIdentityService -import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecryptor import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.job.SyncThread import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.session.widgets.WidgetDependenciesHolder -import im.vector.matrix.android.internal.session.widgets.WidgetManager import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.Dispatchers @@ -109,6 +107,7 @@ internal class DefaultSession @Inject constructor( private val timelineEventDecryptor: TimelineEventDecryptor, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val defaultIdentityService: DefaultIdentityService, + private val integrationManagerService: IntegrationManagerService, private val taskExecutor: TaskExecutor, private val widgetDependenciesHolder: WidgetDependenciesHolder, private val shieldTrustUpdater: ShieldTrustUpdater) @@ -128,8 +127,7 @@ internal class DefaultSession @Inject constructor( HomeServerCapabilitiesService by homeServerCapabilitiesService.get(), ProfileService by profileService.get(), AccountDataService by accountDataService.get(), - AccountService by accountService.get(), - WidgetService by widgetService.get() { + AccountService by accountService.get() { override val sharedSecretStorageService: SharedSecretStorageService get() = _sharedSecretStorageService.get() @@ -243,6 +241,10 @@ internal class DefaultSession @Inject constructor( override fun identityService() = defaultIdentityService + override fun widgetService(): WidgetService = widgetService.get() + + override fun integrationManagerService() = integrationManagerService + override fun addListener(listener: Session.Listener) { sessionListeners.addListener(listener) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt index 3fd8472edf..5b64f2a60a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt @@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.session.group.GetGroupDataWorker import im.vector.matrix.android.internal.session.group.GroupModule import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule import im.vector.matrix.android.internal.session.identity.IdentityModule +import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManagerModule import im.vector.matrix.android.internal.session.openid.OpenIdModule import im.vector.matrix.android.internal.session.profile.ProfileModule import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker @@ -76,6 +77,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers PushersModule::class, OpenIdModule::class, WidgetModule::class, + IntegrationManagerModule::class, IdentityModule::class, TermsModule::class, AccountDataModule::class, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/DefaultIntegrationManagerService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/DefaultIntegrationManagerService.kt new file mode 100644 index 0000000000..d39523a516 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/DefaultIntegrationManagerService.kt @@ -0,0 +1,58 @@ +/* + * 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.integrationmanager + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerConfig +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService +import im.vector.matrix.android.api.util.Cancelable +import javax.inject.Inject + +internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService { + + override fun getOrderedConfigs(): List { + return integrationManager.getOrderedConfigs() + } + + override fun getPreferredConfig(): IntegrationManagerConfig { + return integrationManager.getPreferredConfig() + } + + override fun isIntegrationEnabled(): Boolean { + return integrationManager.isIntegrationEnabled() + } + + override fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable { + return integrationManager.setIntegrationEnabled(enable, callback) + } + + override fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + return integrationManager.setWidgetAllowed(stateEventId, allowed, callback) + } + + override fun isWidgetAllowed(stateEventId: String): Boolean { + return integrationManager.isWidgetAllowed(stateEventId) + } + + override fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback) + } + + override fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean { + return integrationManager.isNativeWidgetAllowed(widgetType, domain) + } +} 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 b3deff6195..34a1a956c9 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 @@ -22,6 +22,7 @@ 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.toModel +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerConfig import im.vector.matrix.android.api.session.widgets.model.WidgetContent import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.NoOpCancellable @@ -31,7 +32,7 @@ import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAcco import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import im.vector.matrix.android.internal.session.user.accountdata.AccountDataDataSource import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask -import im.vector.matrix.android.internal.session.widgets.helper.extractWidgets +import im.vector.matrix.android.internal.session.widgets.helper.extractWidgetSequence import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.StringProvider @@ -294,7 +295,7 @@ internal class IntegrationManager @Inject constructor(private val taskExecutor: } private fun UserAccountDataEvent.asIntegrationManagerWidgetContent(): WidgetContent? { - return extractWidgets() + return extractWidgetSequence() .filter { it.type == INTEGRATION_MANAGER_WIDGET } 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 9c1d16361a..3792ea4231 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,6 +17,7 @@ package im.vector.matrix.android.internal.session.integrationmanager import im.vector.matrix.android.api.auth.data.WellKnown +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerConfig import javax.inject.Inject internal class IntegrationManagerConfigExtractor @Inject constructor() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerModule.kt new file mode 100644 index 0000000000..69ac94b47a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerModule.kt @@ -0,0 +1,28 @@ +/* + * 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.integrationmanager + +import dagger.Binds +import dagger.Module +import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService + +@Module +internal abstract class IntegrationManagerModule { + + @Binds + abstract fun bindIntegrationManagerService(integrationManagerService: DefaultIntegrationManagerService): IntegrationManagerService +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index 8a46d57f9d..ebcac20d1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -24,6 +24,8 @@ import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.state.StateService +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -55,17 +57,29 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey) } - override fun updateTopic(topic: String, callback: MatrixCallback) { - val params = SendStateTask.Params(roomId, - EventType.STATE_ROOM_TOPIC, - mapOf( - "topic" to topic - )) - - sendStateTask + override fun sendStateEvent( + eventType: String, + stateKey: String?, + body: JsonDict, + callback: MatrixCallback + ): Cancelable { + val params = SendStateTask.Params( + roomId = roomId, + eventType = eventType, + body = body + ) + return sendStateTask .configureWith(params) { this.callback = callback } .executeBy(taskExecutor) } + + override fun updateTopic(topic: String, callback: MatrixCallback): Cancelable { + return sendStateEvent( + eventType = EventType.STATE_ROOM_TOPIC, + body = mapOf("topic" to topic), + callback = callback + ) + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt index 382f894f75..d632ca4126 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt @@ -26,6 +26,7 @@ import javax.inject.Inject internal interface SendStateTask : Task { data class Params( val roomId: String, + val stateKey: String? = null, val eventType: String, val body: JsonDict ) @@ -38,7 +39,20 @@ internal class DefaultSendStateTask @Inject constructor( override suspend fun execute(params: SendStateTask.Params) { return executeRequest(eventBus) { - apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body) + apiCall = if (params.stateKey == null) { + roomAPI.sendStateEvent( + roomId = params.roomId, + stateEventType = params.eventType, + params = params.body + ) + } else { + roomAPI.sendStateEvent( + roomId = params.roomId, + stateEventType = params.eventType, + stateKey = params.stateKey, + params = params.body + ) + } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetPostAPIMediator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetPostAPIMediator.kt index d70ccaea90..dcdf2fe050 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetPostAPIMediator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetPostAPIMediator.kt @@ -27,10 +27,10 @@ import timber.log.Timber import java.util.HashMap import javax.inject.Inject -internal class DefaultWidgetPostAPIMediator @Inject constructor(moshi: Moshi, +internal class DefaultWidgetPostAPIMediator @Inject constructor(private val moshi: Moshi, private val widgetPostMessageAPIProvider: WidgetPostMessageAPIProvider) : WidgetPostAPIMediator { - private val adapter = moshi.adapter(JSON_DICT_PARAMETERIZED_TYPE) + private val jsonAdapter = moshi.adapter(JSON_DICT_PARAMETERIZED_TYPE) private var handler: WidgetPostAPIMediator.Handler? = null private var webView: WebView? = null @@ -58,7 +58,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(moshi: Moshi, fun onWidgetEvent(jsonEventData: String) { Timber.d("BRIDGE onWidgetEvent : $jsonEventData") try { - val dataAsDict = adapter.fromJson(jsonEventData) + val dataAsDict = jsonAdapter.fromJson(jsonEventData) @Suppress("UNCHECKED_CAST") val eventData = (dataAsDict?.get("event.data") as? JsonDict) ?: return onWidgetMessage(eventData) @@ -111,11 +111,12 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(moshi: Moshi, * @param response the response * @param eventData the modular data */ - override fun sendObjectResponse(response: JsonDict?, eventData: JsonDict) { + override fun sendObjectResponse(klass: Class, response: T?, eventData: JsonDict) { var jsString: String? = null if (response != null) { + val objectAdapter = moshi.adapter(klass) try { - jsString = "JSON.parse('${adapter.toJson(response)}')" + jsString = "JSON.parse('${objectAdapter.toJson(response)}')" } catch (e: Exception) { Timber.e(e, "## sendObjectResponse() : toJson failed ") } @@ -130,7 +131,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(moshi: Moshi, */ override fun sendSuccess(eventData: JsonDict) { val successResponse = mapOf("success" to true) - sendObjectResponse(successResponse, eventData) + sendObjectResponse(Map::class.java, successResponse, eventData) } /** @@ -147,7 +148,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(moshi: Moshi, val subMap = HashMap() subMap["message"] = message params["error"] = subMap - sendObjectResponse(params, eventData) + sendObjectResponse(Map::class.java, params, eventData) } /** 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 f3e413fa18..dc8364469c 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 @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.session.widgets +import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.events.model.Content @@ -42,6 +43,18 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage return widgetManager.getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes) } + override fun getRoomWidgetsLive(roomId: String, widgetId: QueryStringValue, widgetTypes: Set?, excludedTypes: Set?): LiveData> { + return widgetManager.getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes) + } + + override fun getUserWidgetsLive(widgetTypes: Set?, excludedTypes: Set?): LiveData> { + return widgetManager.getUserWidgetsLive(widgetTypes, excludedTypes) + } + + override fun getUserWidgets(widgetTypes: Set?, excludedTypes: Set?): List { + return widgetManager.getUserWidgets(widgetTypes, excludedTypes) + } + override fun createRoomWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback): Cancelable { return widgetManager.createWidget(roomId, widgetId, content, callback) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt index d3139842d2..5e75e2872f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt @@ -20,7 +20,7 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.widgets.model.WidgetContent data class Widget( - private val widgetContent: WidgetContent, - private val event: Event? = null + val widgetContent: WidgetContent, + val event: Event? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt index 2a42448d19..12d1af07dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt @@ -19,6 +19,8 @@ package im.vector.matrix.android.internal.session.widgets import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.events.model.Content @@ -34,8 +36,9 @@ import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager import im.vector.matrix.android.internal.session.room.state.StateEventDataSource import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData +import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import im.vector.matrix.android.internal.session.user.accountdata.AccountDataDataSource -import im.vector.matrix.android.internal.session.widgets.helper.extractWidgets +import im.vector.matrix.android.internal.session.widgets.helper.extractWidgetSequence import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.launchToCallback import java.util.HashMap @@ -66,6 +69,19 @@ internal class WidgetManager @Inject constructor(private val integrationManager: lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } + fun getRoomWidgetsLive( + roomId: String, + widgetId: QueryStringValue = QueryStringValue.NoCondition, + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): LiveData> { + // Get all im.vector.modular.widgets state events in the room + val liveWidgetEvents = stateEventDataSource.getStateEventsLive(roomId, setOf(WIDGET_EVENT_TYPE), widgetId) + return Transformations.map(liveWidgetEvents) { widgetEvents -> + widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes) + } + } + fun getRoomWidgets( roomId: String, widgetId: QueryStringValue = QueryStringValue.NoCondition, @@ -74,6 +90,12 @@ internal class WidgetManager @Inject constructor(private val integrationManager: ): List { // Get all im.vector.modular.widgets state events in the room val widgetEvents: List = stateEventDataSource.getStateEvents(roomId, setOf(WIDGET_EVENT_TYPE), widgetId) + return widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes) + } + + private fun List.mapEventsToWidgets(widgetTypes: Set? = null, + excludedTypes: Set? = null): List { + val widgetEvents = this // Widget id -> widget val widgets: MutableMap = HashMap() // Order widgetEvents with the last event first @@ -102,12 +124,27 @@ internal class WidgetManager @Inject constructor(private val integrationManager: return widgets.values.toList() } + fun getUserWidgetsLive( + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): LiveData> { + val widgetsAccountData = accountDataDataSource.getLiveAccountDataEvent(UserAccountData.TYPE_WIDGETS) + return Transformations.map(widgetsAccountData) { + it.getOrNull()?.mapToWidgets(widgetTypes, excludedTypes) ?: emptyList() + } + } + fun getUserWidgets( widgetTypes: Set? = null, excludedTypes: Set? = null ): List { val widgetsAccountData = accountDataDataSource.getAccountDataEvent(UserAccountData.TYPE_WIDGETS) ?: return emptyList() - return widgetsAccountData.extractWidgets() + return widgetsAccountData.mapToWidgets(widgetTypes, excludedTypes) + } + + private fun UserAccountDataEvent.mapToWidgets(widgetTypes: Set? = null, + excludedTypes: Set? = null): List { + return extractWidgetSequence() .filter { val widgetType = it.type ?: return@filter false (widgetTypes == null || widgetTypes.contains(widgetType)) 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 index ade827564e..380b2404a8 100644 --- 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 @@ -20,7 +20,7 @@ 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.api.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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/UserAccountWidgets.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/UserAccountWidgets.kt index ff90d0ccc3..1d7b9ef226 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/UserAccountWidgets.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/UserAccountWidgets.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.widgets.model.WidgetContent import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent -internal fun UserAccountDataEvent.extractWidgets(): Sequence { +internal fun UserAccountDataEvent.extractWidgetSequence(): Sequence { return content.asSequence() .mapNotNull { @Suppress("UNCHECKED_CAST") diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index b776a450a6..763dc9298e 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -99,7 +99,8 @@ import im.vector.riotx.features.signout.soft.SoftLogoutFragment import im.vector.riotx.features.terms.ReviewTermsFragment import im.vector.riotx.features.userdirectory.KnownUsersFragment import im.vector.riotx.features.userdirectory.UserDirectoryFragment -import im.vector.riotx.features.widgets.RoomWidgetFragment +import im.vector.riotx.features.widgets.admin.AdminWidgetFragment +import im.vector.riotx.features.widgets.room.RoomWidgetFragment @Module interface FragmentModule { @@ -479,11 +480,6 @@ interface FragmentModule { @FragmentKey(SharedSecuredStorageKeyFragment::class) fun bindSharedSecuredStorageKeyFragment(fragment: SharedSecuredStorageKeyFragment): Fragment - @Binds - @IntoMap - @FragmentKey(RoomWidgetFragment::class) - fun bindWidgetFragment(fragment: RoomWidgetFragment): Fragment - @Binds @IntoMap @FragmentKey(SetIdentityServerFragment::class) @@ -498,4 +494,14 @@ interface FragmentModule { @IntoMap @FragmentKey(ReviewTermsFragment::class) fun bindReviewTermsFragment(fragment: ReviewTermsFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomWidgetFragment::class) + fun bindRoomWidgetFragment(fragment: RoomWidgetFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(AdminWidgetFragment::class) + fun bindAdminWidgetFragment(fragment: AdminWidgetFragment): Fragment } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index b2213eb223..cdb41a17a1 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -215,6 +215,10 @@ class DefaultNavigator @Inject constructor( fragment.startActivityForResult(intent, requestCode) } + override fun openIntegrationManager(context: Context) { + //TODO + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index 07ec0e4ca2..a55509ce6d 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -76,4 +76,7 @@ interface Navigator { baseUrl: String, token: String?, requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE) + + fun openIntegrationManager(context: Context, integId: String?, integType: String?) + } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetAPICallback.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetAPICallback.kt new file mode 100644 index 0000000000..8fc2f4b97c --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetAPICallback.kt @@ -0,0 +1,38 @@ +/* + * 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.riotx.features.widgets + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator +import im.vector.matrix.android.api.util.JsonDict +import im.vector.riotx.R +import im.vector.riotx.core.resources.StringProvider + +class WidgetAPICallback(private val postAPIMediator: WidgetPostAPIMediator, + private val eventData: JsonDict, + private val stringProvider: StringProvider) : MatrixCallback { + + override fun onFailure(failure: Throwable) { + super.onFailure(failure) + postAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_failed_to_send_request), eventData) + } + + override fun onSuccess(data: Unit) { + super.onSuccess(data) + postAPIMediator.sendSuccess(eventData) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt new file mode 100644 index 0000000000..cbe7fbe354 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt @@ -0,0 +1,302 @@ +/* + * 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.riotx.features.widgets + +import android.content.Context +import android.text.TextUtils +import im.vector.matrix.android.api.query.QueryStringValue +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper +import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator +import im.vector.matrix.android.api.util.JsonDict +import im.vector.riotx.R +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.navigation.Navigator +import timber.log.Timber +import java.util.HashMap + +class WidgetPostAPIHandler(private val context: Context, + private val roomId: String, + private val navigator: Navigator, + private val stringProvider: StringProvider, + private val widgetPostAPIMediator: WidgetPostAPIMediator, + private val session: Session) : WidgetPostAPIMediator.Handler { + + private val room = session.getRoom(roomId)!! + + override fun handleWidgetRequest(eventData: JsonDict): Boolean { + return when (eventData["action"] as String?) { + "integration_manager_open" -> handleIntegrationManagerOpenAction(eventData).run { true } + "bot_options" -> getBotOptions(eventData).run { true } + "can_send_event" -> canSendEvent(eventData).run { true } + //"close_scalar" -> finish().run { true } + "get_membership_count" -> getMembershipCount(eventData).run { true } + //"get_widgets" -> getWidgets(eventData).run { true } + //"invite" -> inviteUser(eventData).run { true } + "join_rules_state" -> getJoinRules(eventData).run { true } + "membership_state" -> getMembershipState(eventData).run { true } + "set_bot_options" -> setBotOptions(eventData).run { true } + "set_bot_power" -> setBotPower(eventData).run { true } + "set_plumbing_state" -> setPlumbingState(eventData).run { true } + //"set_widget" -> setWidget(eventData).run { true } + else -> false + } + } + + private fun handleIntegrationManagerOpenAction(eventData: JsonDict) { + var integType: String? = null + var integId: String? = null + val data = eventData["data"] + data + .takeIf { it is Map<*, *> } + ?.let { + val dict = data as Map<*, *> + + dict["integType"] + .takeIf { it is String } + ?.let { integType = it as String } + + dict["integId"] + .takeIf { it is String } + ?.let { integId = it as String } + + // Add "type_" as a prefix + integType?.let { integType = "type_$integType" } + } + navigator.openIntegrationManager(context, integId, integType) + } + + /** + * Retrieve the latest botOptions event + * + * @param eventData the modular data + */ + private fun getBotOptions(eventData: JsonDict) { + if (checkRoomId(eventData) || checkUserId(eventData)) { + return + } + val userId = eventData["user_id"] as String + Timber.d("Received request to get options for bot $userId in room $roomId requested") + val stateEvents = room.getStateEvents(setOf(EventType.BOT_OPTIONS)) + var botOptionsEvent: Event? = null + val stateKey = "_$userId" + for (stateEvent in stateEvents) { + if (TextUtils.equals(stateEvent.stateKey, stateKey)) { + if (null == botOptionsEvent || stateEvent.ageLocalTs ?: 0 > botOptionsEvent.ageLocalTs ?: 0) { + botOptionsEvent = stateEvent + } + } + } + if (null != botOptionsEvent) { + Timber.d("Received request to get options for bot $userId returns $botOptionsEvent") + widgetPostAPIMediator.sendObjectResponse(Event::class.java, botOptionsEvent, eventData) + } else { + Timber.d("Received request to get options for bot $userId returns null") + widgetPostAPIMediator.sendObjectResponse(Event::class.java, null, eventData) + } + } + + private fun canSendEvent(eventData: JsonDict) { + if (checkRoomId(eventData)) { + return + } + Timber.d("Received request canSendEvent in room $roomId") + if (room.roomSummary()?.membership != Membership.JOIN) { + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_must_be_in_room), eventData) + return + } + + val eventType = eventData["event_type"] as String + val isState = eventData["is_state"] as Boolean + + Timber.d("## canSendEvent() : eventType $eventType isState $isState") + + val powerLevelsEvent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + val powerLevelsContent = powerLevelsEvent?.content?.toModel() + val canSend = if (powerLevelsContent == null) { + false + } else { + PowerLevelsHelper(powerLevelsContent).isAllowedToSend(eventType, session.myUserId) + } + if (canSend) { + Timber.d("## canSendEvent() returns true") + widgetPostAPIMediator.sendBoolResponse(true, eventData) + } else { + Timber.d("## canSendEvent() returns widget_integration_no_permission_in_room") + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_no_permission_in_room), eventData) + } + } + + /** + * Provides the membership state + * + * @param eventData the modular data + */ + private fun getMembershipState(eventData: JsonDict) { + if (checkRoomId(eventData) || checkUserId(eventData)) { + return + } + val userId = eventData["user_id"] as String + Timber.d("membership_state of $userId in room $roomId requested") + val roomMemberStateEvent = room.getStateEvent(EventType.STATE_ROOM_MEMBER, stateKey = QueryStringValue.Equals(userId, QueryStringValue.Case.SENSITIVE)) + } + + /** + * Request the latest joined room event + * + * @param eventData the modular data + */ + private fun getJoinRules(eventData: JsonDict) { + if (checkRoomId(eventData)) { + return + } + Timber.d("Received request join rules in room $roomId") + val joinedEvents = room.getStateEvents(setOf(EventType.STATE_ROOM_JOIN_RULES)) + if (joinedEvents.isNotEmpty()) { + widgetPostAPIMediator.sendObjectResponse(Event::class.java, joinedEvents.last(), eventData) + } else { + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_failed_to_send_request), eventData) + } + } + + /** + * Update the 'plumbing state" + * + * @param eventData the modular data + */ + private fun setPlumbingState(eventData: JsonDict) { + if (checkRoomId(eventData)) { + return + } + val description = "Received request to set plumbing state to status " + eventData["status"] + " in room " + roomId + " requested" + Timber.d(description) + + val status = eventData["status"] as String + + val params = HashMap() + params["status"] = status + room.sendStateEvent( + eventType = EventType.PLUMBING, + stateKey = null, + body = params, + callback = WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider) + ) + } + + /** + * Update the bot options + * + * @param eventData the modular data + */ + private fun setBotOptions(eventData: JsonDict) { + if (checkRoomId(eventData) || checkUserId(eventData)) { + return + } + val userId = eventData["user_id"] as String + val description = "Received request to set options for bot $userId in room $roomId" + Timber.d(description) + val content = eventData["content"] as JsonDict + val stateKey = "_$userId" + room.sendStateEvent( + eventType = EventType.BOT_OPTIONS, + stateKey = stateKey, + body = content, + callback = WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider)) + } + + /** + * Update the bot power levels + * + * @param eventData the modular data + */ + private fun setBotPower(eventData: JsonDict) { + if (checkRoomId(eventData) || checkUserId(eventData)) { + return + } + val userId = eventData["user_id"] as String + val description = "Received request to set power level to " + eventData["level"] + " for bot " + userId + " in room " + roomId + Timber.d(description) + val level = eventData["level"] as Int + if (level >= 0) { + // TODO + //room.updateUserPowerLevels(userId, level, WidgetApiCallback(eventData, description)) + } else { + Timber.e("## setBotPower() : Power level must be positive integer.") + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_positive_power_level), eventData) + } + } + + /** + * Provides the number of members in the rooms + * + * @param eventData the modular data + */ + private fun getMembershipCount(eventData: JsonDict) { + if (checkRoomId(eventData)) { + return + } + val numberOfJoinedMembers = room.getNumberOfJoinedMembers() + widgetPostAPIMediator.sendIntegerResponse(numberOfJoinedMembers, eventData) + } + + /** + * Check if roomId is present in the event and match + * Send response and return true in case of error + * + * @return true in case of error + */ + private fun checkRoomId(eventData: JsonDict): Boolean { + val roomIdInEvent = eventData["room_id"] as String? + // Check if param is present + if (null == roomIdInEvent) { + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_missing_room_id), eventData) + return true + } + + if (!TextUtils.equals(roomIdInEvent, roomId)) { + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_room_not_visible), eventData) + return true + } + + // OK + return false + } + + /** + * Check if userId is present in the event + * Send response and return true in case of error + * + * @return true in case of error + */ + private fun checkUserId(eventData: JsonDict): Boolean { + val userIdInEvent = eventData["user_id"] as String? + // Check if param is present + if (null == userIdInEvent) { + widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_missing_user_id), eventData) + return true + } + // OK + return false + } +} + + diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetAction.kt b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetAction.kt similarity index 86% rename from vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetAction.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetAction.kt index 4851ef1807..34c33da945 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetAction.kt @@ -14,8 +14,8 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.admin import im.vector.riotx.core.platform.VectorViewModelAction -sealed class RoomWidgetAction : VectorViewModelAction +sealed class AdminWidgetAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetFragment.kt b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetFragment.kt new file mode 100644 index 0000000000..d456fb51f2 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetFragment.kt @@ -0,0 +1,49 @@ +/* + * 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.riotx.features.widgets.admin + +import android.os.Bundle +import android.view.View +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorBaseFragment +import timber.log.Timber +import javax.inject.Inject + +class AdminWidgetFragment @Inject constructor( + private val viewModelFactory: AdminWidgetViewModel.Factory +) : VectorBaseFragment(), AdminWidgetViewModel.Factory by viewModelFactory { + + private val viewModel: AdminWidgetViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_admin_widget + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // Initialize your view, subscribe to viewModel... + } + + override fun onDestroyView() { + super.onDestroyView() + // Clear your view, unsubscribe... + } + + override fun invalidate() = withState(viewModel) { state -> + Timber.v("Invalidate with state: $state") + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetEventData.kt b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewEvents.kt similarity index 64% rename from vector/src/main/java/im/vector/riotx/features/widgets/WidgetEventData.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewEvents.kt index 3763b1925c..c2cfb4c26f 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetEventData.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewEvents.kt @@ -14,16 +14,8 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.admin -import im.vector.matrix.android.api.util.JsonDict +import im.vector.riotx.core.platform.VectorViewEvents -// Example of data: -// { -// "event.data": { -// "action": "get_widgets", -// "room_id": "!byqyNXFYAGirEulaEm:matrix.org", -// "_id": "1526370173321-0.55myregve98-1" -// } -// } -typealias WidgetEventData = Map +sealed class AdminWidgetViewEvents : VectorViewEvents diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewModel.kt similarity index 69% rename from vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewModel.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewModel.kt index 1499351488..be41962cde 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.admin import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext @@ -22,22 +22,20 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import im.vector.matrix.android.api.session.Session import im.vector.riotx.core.platform.VectorViewModel -class RoomWidgetViewModel @AssistedInject constructor(@Assisted initialState: WidgetViewState, - private val session: Session) - : VectorViewModel(initialState) { +class AdminWidgetViewModel @AssistedInject constructor(@Assisted initialState: AdminWidgetViewState) + : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { - fun create(initialState: WidgetViewState): RoomWidgetViewModel + fun create(initialState: AdminWidgetViewState): AdminWidgetViewModel } - companion object : MvRxViewModelFactory { + companion object : MvRxViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: WidgetViewState): RoomWidgetViewModel? { + override fun create(viewModelContext: ViewModelContext, state: AdminWidgetViewState): AdminWidgetViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory is ActivityViewModelContext -> viewModelContext.activity as? Factory @@ -46,7 +44,7 @@ class RoomWidgetViewModel @AssistedInject constructor(@Assisted initialState: Wi } } - override fun handle(action: RoomWidgetAction) { - //TODO + override fun handle(action: AdminWidgetAction) { + } } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewState.kt b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewState.kt new file mode 100644 index 0000000000..08d2184234 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/admin/AdminWidgetViewState.kt @@ -0,0 +1,21 @@ +/* + * 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.riotx.features.widgets.admin + +import com.airbnb.mvrx.MvRxState + +data class AdminWidgetViewState(val boolean: Boolean = false) : MvRxState diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetAction.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetAction.kt new file mode 100644 index 0000000000..47d3c468eb --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetAction.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.riotx.features.widgets.room + +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class RoomWidgetAction : VectorViewModelAction { + data class OnWebViewStartedToLoad(val url: String) : RoomWidgetAction() + data class OnWebViewLoadingError(val url: String) : RoomWidgetAction() + data class OnWebViewLoadingSuccess(val url: String) : RoomWidgetAction() +} diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetFragment.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetFragment.kt similarity index 79% rename from vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetFragment.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetFragment.kt index bbf526da1e..066514eabe 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.room import android.os.Bundle import android.os.Parcelable @@ -22,6 +22,8 @@ import android.view.View import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator +import im.vector.matrix.android.api.util.JsonDict import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.webview.WebViewEventListener @@ -34,12 +36,15 @@ import javax.inject.Inject @Parcelize data class WidgetArgs( - val widgetId: String + val baseUrl: String, + val kind: WidgetKind, + val roomId: String, + val widgetId: String? = null ) : Parcelable class RoomWidgetFragment @Inject constructor( private val viewModelFactory: RoomWidgetViewModel.Factory -) : VectorBaseFragment(), RoomWidgetViewModel.Factory by viewModelFactory, WebViewEventListener { +) : VectorBaseFragment(), RoomWidgetViewModel.Factory by viewModelFactory, WebViewEventListener, WidgetPostAPIMediator.Handler { private val fragmentArgs: WidgetArgs by args() private val viewModel: RoomWidgetViewModel by fragmentViewModel() @@ -49,10 +54,12 @@ class RoomWidgetFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) widgetWebView.setupForWidget(this) + viewModel.getPostAPIMediator().initialize(widgetWebView, this) } override fun onDestroyView() { super.onDestroyView() + viewModel.getPostAPIMediator().clear() widgetWebView.clearAfterWidget() } @@ -77,6 +84,7 @@ class RoomWidgetFragment @Inject constructor( } override fun onPageStarted(url: String) { + } override fun onPageFinished(url: String) { @@ -85,4 +93,8 @@ class RoomWidgetFragment @Inject constructor( override fun onPageError(url: String, errorCode: Int, description: String) { } + + override fun handleWidgetRequest(eventData: JsonDict): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewEvents.kt similarity index 93% rename from vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewEvents.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewEvents.kt index 51eb154017..0c35afa927 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.room import im.vector.riotx.core.platform.VectorViewEvents diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewModel.kt new file mode 100644 index 0000000000..422a89942a --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewModel.kt @@ -0,0 +1,129 @@ +/* + * 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.riotx.features.widgets.room + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.query.QueryStringValue +import im.vector.matrix.android.api.session.Session +import im.vector.riotx.core.platform.VectorViewModel +import kotlinx.coroutines.launch + +class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: WidgetViewState): RoomWidgetViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: WidgetViewState): RoomWidgetViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + private val widgetService = session.widgetService() + private val integrationManagerService = session.integrationManagerService() + private val widgetBuilder = widgetService.getWidgetURLBuilder() + private val postAPIMediator = widgetService.getWidgetPostAPIMediator() + + init { + refreshPermissionStatus() + } + + fun getPostAPIMediator() = postAPIMediator + + override fun handle(action: RoomWidgetAction) { + when (action) { + is RoomWidgetAction.OnWebViewLoadingError -> handleWebViewLoadingError(action.url) + is RoomWidgetAction.OnWebViewLoadingSuccess -> handleWebViewLoadingSuccess(action.url) + is RoomWidgetAction.OnWebViewStartedToLoad -> handleWebViewStartLoading(action.url) + } + } + + private fun refreshPermissionStatus() { + if (initialState.widgetKind == WidgetKind.USER || initialState.widgetKind == WidgetKind.INTEGRATION_MANAGER) { + onWidgetAllowed() + } else { + val widgetId = initialState.widgetId + if (widgetId == null) { + setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) } + return + } + val roomWidget = widgetService.getRoomWidgets(initialState.roomId, widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE)).firstOrNull() + if (roomWidget == null) { + setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) } + return + } + if (roomWidget.event?.senderId == session.myUserId) { + onWidgetAllowed() + } else { + val stateEventId = roomWidget.event?.eventId + // This should not happen + if (stateEventId == null) { + setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) } + return + } + val isAllowed = integrationManagerService.isWidgetAllowed(stateEventId) + if (!isAllowed) { + setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) } + } else { + onWidgetAllowed() + } + } + } + } + + private fun onWidgetAllowed() { + setState { + copy(status = WidgetStatus.WIDGET_ALLOWED, formattedURL = Loading()) + } + viewModelScope.launch { + try { + val formattedUrl = widgetBuilder.build(initialState.baseUrl) + setState { copy(formattedURL = Success(formattedUrl)) } + } catch (failure: Throwable) { + setState { copy(formattedURL = Fail(failure)) } + } + } + } + + private fun handleWebViewStartLoading(url: String) { + } + + private fun handleWebViewLoadingSuccess(url: String) { + } + + private fun handleWebViewLoadingError(url: String) { + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewState.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewState.kt similarity index 70% rename from vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewState.kt rename to vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewState.kt index 537d2fc484..500d867c64 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/RoomWidgetViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/RoomWidgetViewState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.riotx.features.widgets +package im.vector.riotx.features.widgets.room import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState @@ -26,8 +26,17 @@ enum class WidgetStatus { WIDGET_ALLOWED } +enum class WidgetKind { + ROOM, + USER, + INTEGRATION_MANAGER +} + data class WidgetViewState( - val widgetId: String, + val roomId: String, + val baseUrl: String, + val widgetId: String? = null, + val widgetKind: WidgetKind, val status: WidgetStatus = WidgetStatus.UNKNOWN, val formattedURL: Async = Uninitialized, val webviewLoadedUrl: Async = Uninitialized, @@ -36,5 +45,10 @@ data class WidgetViewState( val createdByMe: Boolean = false ) : MvRxState { - constructor(widgetArgs: WidgetArgs) : this(widgetId = widgetArgs.widgetId) + constructor(widgetArgs: WidgetArgs) : this( + widgetKind = widgetArgs.kind, + baseUrl = widgetArgs.baseUrl, + roomId = widgetArgs.roomId, + widgetId = widgetArgs.widgetId + ) } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/room/WidgetParamsProvider.kt b/vector/src/main/java/im/vector/riotx/features/widgets/room/WidgetParamsProvider.kt new file mode 100644 index 0000000000..fb9c690075 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/widgets/room/WidgetParamsProvider.kt @@ -0,0 +1,42 @@ +/* + * 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.riotx.features.widgets.room + +interface WidgetParams { + val params: Map +} + +class IntegrationManagerParams( + private val widgetId: String? = null, + private val screenId: String? = null) : WidgetParams { + + override val params: Map by lazy { + buildParams() + } + + private fun buildParams(): Map { + val map = HashMap() + if (widgetId != null) { + map["integ_id"] = widgetId + } + if (screenId != null) { + map["screen"] = screenId + } + return map + } +} + diff --git a/vector/src/main/res/layout/fragment_admin_widget.xml b/vector/src/main/res/layout/fragment_admin_widget.xml new file mode 100644 index 0000000000..9a0139a89b --- /dev/null +++ b/vector/src/main/res/layout/fragment_admin_widget.xml @@ -0,0 +1,19 @@ + + + + + +