From b047f36e863a1edaef437f0468a435e391dc208c Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 6 May 2020 20:49:07 +0200 Subject: [PATCH] Creates a Widget Manager to be used internally and state event service --- .../main/java/im/vector/matrix/rx/RxRoom.kt | 3 +- .../api/session/room/state/StateService.kt | 9 +- .../query/CurrentStateEventEntityQueries.kt | 4 +- .../pushers/DefaultConditionResolver.kt | 3 +- .../android/internal/session/room/RoomAPI.kt | 5 +- .../session/room/state/DefaultStateService.kt | 35 ++--- .../session/room/state/SendStateTask.kt | 3 +- .../room/state/StateEventDataSource.kt | 83 ++++++++++++ .../session/widgets/CreateWidgetFailure.kt | 24 ++++ .../session/widgets/CreateWidgetTask.kt | 62 +++++++++ .../internal/session/widgets/Widget.kt | 26 ++++ .../internal/session/widgets/WidgetManager.kt | 125 ++++++++++++++++++ .../session/widgets/token/ScalarTokenStore.kt | 20 +++ .../home/room/detail/RoomDetailViewModel.kt | 2 +- .../RoomMemberProfileViewModel.kt | 2 +- .../members/RoomMemberListViewModel.kt | 2 +- 16 files changed, 375 insertions(+), 33 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/StateEventDataSource.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetFailure.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/ScalarTokenStore.kt diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index 193b5c3fbf..ad50b9b972 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -16,6 +16,7 @@ package im.vector.matrix.rx +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.room.Room import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams @@ -60,7 +61,7 @@ class RxRoom(private val room: Room) { } } - fun liveStateEvent(eventType: String, stateKey: String): Observable> { + fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Observable> { return room.getStateEventLive(eventType, stateKey).asObservable() .startWithCallable { room.getStateEvent(eventType, stateKey).toOptional() 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 83c691ebdf..576747df35 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 @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room.state 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.Optional @@ -28,7 +29,11 @@ interface StateService { */ fun updateTopic(topic: String, callback: MatrixCallback) - fun getStateEvent(eventType: String, stateKey: String): Event? + fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? - fun getStateEventLive(eventType: String, stateKey: String): LiveData> + fun getStateEventLive(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> + + fun getStateEvents(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): List + + fun getStateEventsLive(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/CurrentStateEventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/CurrentStateEventEntityQueries.kt index 814e38276a..45f33ae8df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/CurrentStateEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/CurrentStateEventEntityQueries.kt @@ -23,7 +23,7 @@ import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.createObject -internal fun CurrentStateEventEntity.Companion.where(realm: Realm, roomId: String, type: String): RealmQuery { +internal fun CurrentStateEventEntity.Companion.whereType(realm: Realm, roomId: String, type: String): RealmQuery { return realm.where(CurrentStateEventEntity::class.java) .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) .equalTo(CurrentStateEventEntityFields.TYPE, type) @@ -31,7 +31,7 @@ internal fun CurrentStateEventEntity.Companion.where(realm: Realm, roomId: Strin internal fun CurrentStateEventEntity.Companion.whereStateKey(realm: Realm, roomId: String, type: String, stateKey: String) : RealmQuery { - return where(realm = realm, roomId = roomId, type = type) + return whereType(realm = realm, roomId = roomId, type = type) .equalTo(CurrentStateEventEntityFields.STATE_KEY, stateKey) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt index f130cf5bc7..6cbcac4881 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.pushrules.ContainsDisplayNameCondition import im.vector.matrix.android.api.pushrules.EventMatchCondition import im.vector.matrix.android.api.pushrules.RoomMemberCountCondition import im.vector.matrix.android.api.pushrules.SenderNotificationPermissionCondition +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.events.model.toModel @@ -48,7 +49,7 @@ internal class DefaultConditionResolver @Inject constructor( val roomId = event.roomId ?: return false val room = roomGetter.getRoom(roomId) ?: return false - val powerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, "") + val powerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) ?.content ?.toModel() ?: PowerLevelsContent() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index f5ddf6ae4b..3184416d23 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.create.JoinRoomResponse import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.network.NetworkConstants import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription import im.vector.matrix.android.internal.session.room.membership.RoomMembersResponse @@ -175,7 +176,7 @@ internal interface RoomAPI { @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}") fun sendStateEvent(@Path("roomId") roomId: String, @Path("state_event_type") stateEventType: String, - @Body params: Map): Call + @Body params: JsonDict): Call /** * Send a generic state events @@ -189,7 +190,7 @@ internal interface RoomAPI { fun sendStateEvent(@Path("roomId") roomId: String, @Path("state_event_type") stateEventType: String, @Path("state_key") stateKey: String, - @Body params: Map): Call + @Body params: JsonDict): Call /** * Send a relation event to a room. 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 e831ffbb38..8a46d57f9d 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 @@ -17,26 +17,19 @@ package im.vector.matrix.android.internal.session.room.state import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import com.zhuinden.monarchy.Monarchy 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.session.events.model.EventType import im.vector.matrix.android.api.session.room.state.StateService import im.vector.matrix.android.api.util.Optional -import im.vector.matrix.android.api.util.toOptional -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity -import im.vector.matrix.android.internal.database.query.getOrNull -import im.vector.matrix.android.internal.database.query.whereStateKey import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import io.realm.Realm internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String, - private val monarchy: Monarchy, + private val stateEventDataSource: StateEventDataSource, private val taskExecutor: TaskExecutor, private val sendStateTask: SendStateTask ) : StateService { @@ -46,20 +39,20 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private fun create(roomId: String): StateService } - override fun getStateEvent(eventType: String, stateKey: String): Event? { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> - CurrentStateEventEntity.getOrNull(realm, roomId, type = eventType, stateKey = stateKey)?.root?.asDomain() - } + override fun getStateEvent(eventType: String, stateKey: QueryStringValue): Event? { + return stateEventDataSource.getStateEvent(roomId, eventType, stateKey) } - override fun getStateEventLive(eventType: String, stateKey: String): LiveData> { - val liveData = monarchy.findAllMappedWithChanges( - { realm -> CurrentStateEventEntity.whereStateKey(realm, roomId, type = eventType, stateKey = "") }, - { it.root?.asDomain() } - ) - return Transformations.map(liveData) { results -> - results.firstOrNull().toOptional() - } + override fun getStateEventLive(eventType: String, stateKey: QueryStringValue): LiveData> { + return stateEventDataSource.getStateEventLive(roomId, eventType, stateKey) + } + + override fun getStateEvents(eventTypes: Set, stateKey: QueryStringValue): List { + return stateEventDataSource.getStateEvents(roomId, eventTypes, stateKey) + } + + override fun getStateEventsLive(eventTypes: Set, stateKey: QueryStringValue): LiveData> { + return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey) } override fun updateTopic(topic: String, callback: MatrixCallback) { 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 b0d583c6d1..382f894f75 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 @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.session.room.state +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task @@ -26,7 +27,7 @@ internal interface SendStateTask : Task { data class Params( val roomId: String, val eventType: String, - val body: Map + val body: JsonDict ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/StateEventDataSource.kt new file mode 100644 index 0000000000..3f0e9d7dda --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/StateEventDataSource.kt @@ -0,0 +1,83 @@ +/* + * 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.room.state + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy +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.Optional +import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity +import im.vector.matrix.android.internal.database.model.CurrentStateEventEntityFields +import im.vector.matrix.android.internal.query.process +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where +import javax.inject.Inject + +internal class StateEventDataSource @Inject constructor(private val monarchy: Monarchy) { + + fun getStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue): Event? { + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + buildStateEventQuery(realm, roomId, setOf(eventType), stateKey).findFirst()?.root?.asDomain() + } + } + + fun getStateEventLive(roomId: String, eventType: String, stateKey: QueryStringValue): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { realm -> buildStateEventQuery(realm, roomId, setOf(eventType), stateKey) }, + { it.root?.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.firstOrNull().toOptional() + } + } + + fun getStateEvents(roomId: String, eventTypes: Set, stateKey: QueryStringValue): List { + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + buildStateEventQuery(realm, roomId, eventTypes, stateKey) + .findAll() + .mapNotNull { + it.root?.asDomain() + } + } + } + + fun getStateEventsLive(roomId: String, eventTypes: Set, stateKey: QueryStringValue): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { realm -> buildStateEventQuery(realm, roomId, eventTypes, stateKey) }, + { it.root?.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.filterNotNull() + } + } + + private fun buildStateEventQuery(realm: Realm, + roomId: String, + eventTypes: Set, + stateKey: QueryStringValue + ): RealmQuery { + return realm.where() + .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) + .`in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + .process(CurrentStateEventEntityFields.STATE_KEY, stateKey) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetFailure.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetFailure.kt new file mode 100644 index 0000000000..c37495d546 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetFailure.kt @@ -0,0 +1,24 @@ +/* + * 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.api.failure.Failure + +sealed class CreateWidgetFailure : Failure.FeatureFailure() { + object NotEnoughtPower : CreateWidgetFailure() + object CreationFailed : CreateWidgetFailure() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetTask.kt new file mode 100644 index 0000000000..c5516f79cf --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/CreateWidgetTask.kt @@ -0,0 +1,62 @@ +/* + * 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 com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.internal.database.awaitNotEmptyResult +import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity +import im.vector.matrix.android.internal.database.model.CurrentStateEventEntityFields +import im.vector.matrix.android.internal.database.query.whereStateKey +import im.vector.matrix.android.internal.di.UserId +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task +import org.greenrobot.eventbus.EventBus +import javax.inject.Inject + +internal interface CreateWidgetTask : Task { + + data class Params( + val roomId: String, + val widgetId: String, + val content: Content + ) +} + +internal class DefaultCreateWidgetTask @Inject constructor(private val monarchy: Monarchy, + private val roomAPI: RoomAPI, + @UserId private val userId: String, + private val eventBus: EventBus) : CreateWidgetTask { + + override suspend fun execute(params: CreateWidgetTask.Params) { + executeRequest(eventBus) { + apiCall = roomAPI.sendStateEvent( + roomId = params.roomId, + stateEventType = WidgetManager.WIDGET_EVENT_TYPE, + stateKey = params.widgetId, + params = params.content + ) + } + awaitNotEmptyResult(monarchy.realmConfiguration, 30_000L) { + CurrentStateEventEntity + .whereStateKey(it, params.roomId, type = WidgetManager.WIDGET_EVENT_TYPE, stateKey = params.widgetId) + .and() + .equalTo(CurrentStateEventEntityFields.ROOT.SENDER, userId) + } + } +} 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 new file mode 100644 index 0000000000..b876b236ab --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt @@ -0,0 +1,26 @@ +/* + * 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.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 +) + 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 new file mode 100644 index 0000000000..da0d1e3117 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt @@ -0,0 +1,125 @@ +/* + * 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 androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +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 +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.PowerLevelsContent +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper +import im.vector.matrix.android.api.session.widgets.model.WidgetContent +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.di.UserId +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.task.TaskExecutor +import im.vector.matrix.android.internal.task.launchToCallback +import java.util.HashMap +import javax.inject.Inject + +@SessionScope +internal class WidgetManager @Inject constructor(private val integrationManager: IntegrationManager, + private val stateEventDataSource: StateEventDataSource, + private val taskExecutor: TaskExecutor, + private val createWidgetTask: CreateWidgetTask, + @UserId private val userId: String) : IntegrationManager.Listener { + + companion object { + const val WIDGET_EVENT_TYPE = "im.vector.modular.widgets" + } + + private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } + private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) + + fun start() { + lifecycleRegistry.currentState = Lifecycle.State.STARTED + integrationManager.addListener(this) + } + + fun stop() { + integrationManager.removeListener(this) + lifecycleRegistry.currentState = Lifecycle.State.DESTROYED + } + + fun getRoomWidgets( + roomId: String, + widgetId: QueryStringValue = QueryStringValue.NoCondition, + widgetTypes: Set? = null, + excludedTypes: Set? = null + ): List { + // Get all im.vector.modular.widgets state events in the room + val widgetEvents: List = stateEventDataSource.getStateEvents(roomId, setOf(WIDGET_EVENT_TYPE), widgetId) + // Widget id -> widget + val widgets: MutableMap = HashMap() + // Order widgetEvents with the last event first + // There can be several im.vector.modular.widgets state events for a same widget but + // only the last one must be considered. + val sortedWidgetEvents = widgetEvents.sortedByDescending { + it.originServerTs + } + // Create each widget from its latest im.vector.modular.widgets state event + for (widgetEvent in sortedWidgetEvents) { // Filter widget types if required + val widgetContent = widgetEvent.content.toModel() + if (widgetContent?.url == null) continue + val widgetType = widgetContent.type ?: continue + if (widgetTypes != null && !widgetTypes.contains(widgetType)) { + continue + } + if (excludedTypes != null && excludedTypes.contains(widgetType)) { + continue + } + // widgetEvent.stateKey = widget id + if (widgetEvent.stateKey != null && !widgets.containsKey(widgetEvent.stateKey)) { + val widget = Widget(widgetContent, widgetEvent) + widgets[widgetEvent.stateKey] = widget + } + } + return widgets.values.toList() + } + + fun createWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback): Cancelable { + return taskExecutor.executorScope.launchToCallback(callback = callback) { + if (!hasPermissionsToHandleWidgets(roomId)) { + throw CreateWidgetFailure.NotEnoughtPower + } + val params = CreateWidgetTask.Params( + roomId = roomId, + widgetId = widgetId, + content = content + ) + createWidgetTask.execute(params) + try { + getRoomWidgets(roomId, widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.INSENSITIVE)).first() + } catch (failure: Throwable) { + throw CreateWidgetFailure.CreationFailed + } + } + } + + fun hasPermissionsToHandleWidgets(roomId: String): Boolean { + val powerLevelsEvent = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) + val powerLevelsContent = powerLevelsEvent?.content?.toModel() ?: return false + return PowerLevelsHelper(powerLevelsContent).isAllowedToSend(EventType.STATE_ROOM_POWER_LEVELS, userId) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/ScalarTokenStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/ScalarTokenStore.kt new file mode 100644 index 0000000000..db87c6af8e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/ScalarTokenStore.kt @@ -0,0 +1,20 @@ +/* + * 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.token + +internal class ScalarTokenStore { +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index d0dcac6ecc..727b33ee37 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -1002,7 +1002,7 @@ class RoomDetailViewModel @AssistedInject constructor( setState { copy(asyncInviter = Success(it)) } } } - room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, "")?.also { + room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE)?.also { setState { copy(tombstoneEvent = it) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 44c214bc99..b32d2adaa6 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -184,7 +184,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v private fun observeRoomSummaryAndPowerLevels(room: Room) { val roomSummaryLive = room.rx().liveRoomSummary().unwrap() - val powerLevelsContentLive = room.rx().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, "") + val powerLevelsContentLive = room.rx().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap() diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 81b2809c4f..7b47f757ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -80,7 +80,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState .combineLatest, PowerLevelsContent, RoomMemberSummaries>( room.rx().liveRoomMembers(roomMemberQueryParams), room.rx() - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, "") + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition) .mapOptional { it.content.toModel() } .unwrap(), BiFunction { roomMembers, powerLevelsContent ->