From 29c9d3070c7975d0c8b29ec9a8a01aaaa9ff4569 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 5 May 2020 13:05:45 +0200 Subject: [PATCH 001/115] Start creating bunch of necessary classes --- .../matrix/android/api/auth/data/WellKnown.kt | 31 +-------- .../session/widgets/model/WidgetContent.kt | 32 +++++++++ .../database/mapper/AccountDataMapper.kt | 35 ++++++++++ .../AllowedWidgetsContent.kt | 39 +++++++++++ .../IntegrationManagerConfig.kt} | 10 +-- .../IntegrationManagerConfigExtractor.kt | 45 +++++++++++++ .../IntegrationManagerWidgetData.kt | 25 +++++++ .../IntegrationProvisioningContent.kt | 25 +++++++ .../sync/model/accountdata/UserAccountData.kt | 4 ++ .../UserAccountDataAllowedWidgets.kt | 27 ++++++++ .../UserAccountDataIntegrationProvisioning.kt | 27 ++++++++ .../accountdata/UserAccountDataWidgets.kt | 50 ++++++++++++++ .../user/accountdata/AccountDataDataSource.kt | 66 +++++++++++++++++++ .../accountdata/DefaultAccountDataService.kt | 46 ++----------- .../accountdata/UpdateUserAccountDataTask.kt | 9 +++ 15 files changed, 397 insertions(+), 74 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/AccountDataMapper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/{api/auth/data/WellKnownManagerConfig.kt => internal/session/integrationmanager/IntegrationManagerConfig.kt} (74%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerWidgetData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationProvisioningContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataAllowedWidgets.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIntegrationProvisioning.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataWidgets.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataDataSource.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt index bdad4702b7..93067d8ebb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.auth.data import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict /** * https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery @@ -52,31 +53,5 @@ data class WellKnown( val identityServer: WellKnownBaseConfig? = null, @Json(name = "m.integrations") - val integrations: Map? = null -) { - /** - * Returns the list of integration managers proposed - */ - fun getIntegrationManagers(): List { - val managers = ArrayList() - integrations?.get("managers")?.let { - (it as? ArrayList<*>)?.let { configs -> - configs.forEach { config -> - (config as? Map<*, *>)?.let { map -> - val apiUrl = map["api_url"] as? String - val uiUrl = map["ui_url"] as? String ?: apiUrl - if (apiUrl != null - && apiUrl.startsWith("https://") - && uiUrl!!.startsWith("https://")) { - managers.add(WellKnownManagerConfig( - apiUrl = apiUrl, - uiUrl = uiUrl - )) - } - } - } - } - } - return managers - } -} + val integrations: JsonDict? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt new file mode 100644 index 0000000000..8b71fad29c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.widgets.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class WidgetContent( + @Json(name = "creatorUserId") val creatorUserId: String, + @Json(name = "id") val id: String, + @Json(name = "type") val type: String, + @Json(name = "url") val url: String, + @Json(name = "name") val name: String, + @Json(name = "data") val data: JsonDict, + @Json(name = "waitForIframeLoad") val waitForIframeLoad: Boolean = false +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/AccountDataMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/AccountDataMapper.kt new file mode 100644 index 0000000000..ebeeb144d3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/AccountDataMapper.kt @@ -0,0 +1,35 @@ +/* + * 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.database.mapper + +import com.squareup.moshi.Moshi +import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE +import im.vector.matrix.android.internal.database.model.UserAccountDataEntity +import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent +import javax.inject.Inject + +internal class AccountDataMapper @Inject constructor(moshi: Moshi) { + + private val adapter = moshi.adapter>(JSON_DICT_PARAMETERIZED_TYPE) + + fun map(entity: UserAccountDataEntity): UserAccountDataEvent { + return UserAccountDataEvent( + type = entity.type ?: "", + content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap() + ) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt new file mode 100644 index 0000000000..6f2c0883d5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.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.internal.session.integrationmanager + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class AllowedWidgetsContent( + /** + * Map of stateEventId to Allowed + */ + @Json(name = "widgets") val widgets: Map, + + /** + * Map of native widgetType to a map of domain to Allowed + * { + * "jitsi" : { + * "jitsi.domain.org" : true, + * "jitsi.other.org" : false + * } + * } + */ + @Json(name = "native_widgets") val native: Map> +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnownManagerConfig.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt similarity index 74% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnownManagerConfig.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt index 33ed412a2a..2ebbccc522 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnownManagerConfig.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package im.vector.matrix.android.api.auth.data +package im.vector.matrix.android.internal.session.integrationmanager -data class WellKnownManagerConfig( - val apiUrl : String, - val uiUrl: String +data class IntegrationManagerConfig( + val uiUrl: String, + val apiUrl: String ) 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 new file mode 100644 index 0000000000..5750eb8b5d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt @@ -0,0 +1,45 @@ +/* + * 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.auth.data.WellKnown + +internal class IntegrationManagerConfigExtractor { + + fun extract(wellKnown: WellKnown): List { + val managers = ArrayList() + wellKnown.integrations?.get("managers")?.let { + (it as? List<*>)?.let { configs -> + configs.forEach { config -> + (config as? Map<*, *>)?.let { map -> + val apiUrl = map["api_url"] as? String + val uiUrl = map["ui_url"] as? String ?: apiUrl + if (apiUrl != null + && apiUrl.startsWith("https://") + && uiUrl!!.startsWith("https://")) { + managers.add(IntegrationManagerConfig( + apiUrl = apiUrl, + uiUrl = uiUrl + )) + } + } + } + } + } + return managers + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerWidgetData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerWidgetData.kt new file mode 100644 index 0000000000..a6e69319a3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManagerWidgetData.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session.integrationmanager + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class IntegrationManagerWidgetData( + @Json(name = "api_url") val apiUrl: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationProvisioningContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationProvisioningContent.kt new file mode 100644 index 0000000000..55c93b6ec7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationProvisioningContent.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session.integrationmanager + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class IntegrationProvisioningContent( + @Json(name = "enabled") val enabled: Boolean +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt index c508413665..43a5d8a5cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt @@ -30,5 +30,9 @@ abstract class UserAccountData : AccountDataContent { const val TYPE_PREVIEW_URLS = "org.matrix.preview_urls" const val TYPE_WIDGETS = "m.widgets" const val TYPE_PUSH_RULES = "m.push_rules" + + const val ACCOUNT_DATA_TYPE_INTEGRATION_PROVISIONING = "im.vector.setting.integration_provisioning" + const val ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS = "im.vector.setting.allowed_widgets" + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataAllowedWidgets.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataAllowedWidgets.kt new file mode 100644 index 0000000000..ae6ca686ed --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataAllowedWidgets.kt @@ -0,0 +1,27 @@ +/* + * 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.sync.model.accountdata + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.session.integrationmanager.AllowedWidgetsContent + +@JsonClass(generateAdapter = true) +internal data class UserAccountDataAllowedWidgets( + @Json(name = "type") override val type: String = ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS, + @Json(name = "content") val content: AllowedWidgetsContent +) : UserAccountData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIntegrationProvisioning.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIntegrationProvisioning.kt new file mode 100644 index 0000000000..c65bbd82c1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIntegrationProvisioning.kt @@ -0,0 +1,27 @@ +/* + * 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.sync.model.accountdata + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.session.integrationmanager.IntegrationProvisioningContent + +@JsonClass(generateAdapter = true) +internal data class UserAccountDataIntegrationProvisioning( + @Json(name = "type") override val type: String = ACCOUNT_DATA_TYPE_INTEGRATION_PROVISIONING, + @Json(name = "content") val content: IntegrationProvisioningContent +) : UserAccountData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataWidgets.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataWidgets.kt new file mode 100644 index 0000000000..5c13b4d5fe --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataWidgets.kt @@ -0,0 +1,50 @@ +/* + * 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.sync.model.accountdata + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.events.model.Event + +/* +"m.widgets":{ + "stickerpicker_@rxl881:matrix.org_1514573757015":{ + "content":{ + "creatorUserId":"@rxl881:matrix.org", + "data":{ + "..." + }, + "id":"stickerpicker_@rxl881:matrix.org_1514573757015", + "name":"Stickerpicker", + "type":"m.stickerpicker", + "url":"https://...", + "waitForIframeLoad":true + }, + "sender":"@rxl881:matrix.org" + "state_key":"stickerpicker_@rxl881:matrix.org_1514573757015", + "type":"m.widget" + }, +{ + "..." + } +} + */ +@JsonClass(generateAdapter = true) +internal data class UserAccountDataWidgets( + @Json(name = "type") override val type: String = TYPE_WIDGETS, + @Json(name = "content") val content: Map +) : UserAccountData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataDataSource.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataDataSource.kt new file mode 100644 index 0000000000..056d9c41f0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataDataSource.kt @@ -0,0 +1,66 @@ +/* + * 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.user.accountdata + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.android.internal.database.mapper.AccountDataMapper +import im.vector.matrix.android.internal.database.model.UserAccountDataEntity +import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields +import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent +import io.realm.Realm +import io.realm.RealmQuery +import javax.inject.Inject + +internal class AccountDataDataSource @Inject constructor(private val monarchy: Monarchy, + private val accountDataMapper: AccountDataMapper) { + + fun getAccountDataEvent(type: String): UserAccountDataEvent? { + return getAccountDataEvents(setOf(type)).firstOrNull() + } + + fun getLiveAccountDataEvent(type: String): LiveData> { + return Transformations.map(getLiveAccountDataEvents(setOf(type))) { + it.firstOrNull()?.toOptional() + } + } + + fun getAccountDataEvents(types: Set): List { + return monarchy.fetchAllMappedSync( + { accountDataEventsQuery(it, types) }, + accountDataMapper::map + ) + } + + fun getLiveAccountDataEvents(types: Set): LiveData> { + return monarchy.findAllMappedWithChanges( + { accountDataEventsQuery(it, types) }, + accountDataMapper::map + ) + } + + private fun accountDataEventsQuery(realm: Realm, types: Set): RealmQuery { + val query = realm.where(UserAccountDataEntity::class.java) + if (types.isNotEmpty()) { + query.`in`(UserAccountDataEntityFields.TYPE, types.toTypedArray()) + } + return query + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt index 7756b22510..9787f47844 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt @@ -17,18 +17,12 @@ package im.vector.matrix.android.internal.session.user.accountdata import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.accountdata.AccountDataService import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.Optional -import im.vector.matrix.android.api.util.toOptional -import im.vector.matrix.android.internal.database.model.UserAccountDataEntity -import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields -import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import im.vector.matrix.android.internal.task.TaskExecutor @@ -39,54 +33,24 @@ internal class DefaultAccountDataService @Inject constructor( private val monarchy: Monarchy, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val userAccountDataSyncHandler: UserAccountDataSyncHandler, + private val accountDataDataSource: AccountDataDataSource, private val taskExecutor: TaskExecutor ) : AccountDataService { - private val moshi = MoshiProvider.providesMoshi() - private val adapter = moshi.adapter>(JSON_DICT_PARAMETERIZED_TYPE) - override fun getAccountDataEvent(type: String): UserAccountDataEvent? { - return getAccountDataEvents(setOf(type)).firstOrNull() + return accountDataDataSource.getAccountDataEvent(type) } override fun getLiveAccountDataEvent(type: String): LiveData> { - return Transformations.map(getLiveAccountDataEvents(setOf(type))) { - it.firstOrNull()?.toOptional() - } + return accountDataDataSource.getLiveAccountDataEvent(type) } override fun getAccountDataEvents(types: Set): List { - return monarchy.fetchAllCopiedSync { realm -> - realm.where(UserAccountDataEntity::class.java) - .apply { - if (types.isNotEmpty()) { - `in`(UserAccountDataEntityFields.TYPE, types.toTypedArray()) - } - } - }.mapNotNull { entity -> - entity.type?.let { type -> - UserAccountDataEvent( - type = type, - content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap() - ) - } - } + return accountDataDataSource.getAccountDataEvents(types) } override fun getLiveAccountDataEvents(types: Set): LiveData> { - return monarchy.findAllMappedWithChanges({ realm -> - realm.where(UserAccountDataEntity::class.java) - .apply { - if (types.isNotEmpty()) { - `in`(UserAccountDataEntityFields.TYPE, types.toTypedArray()) - } - } - }, { entity -> - UserAccountDataEvent( - type = entity.type ?: "", - content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap() - ) - }) + return accountDataDataSource.getLiveAccountDataEvents(types) } override fun updateAccountData(type: String, content: Content, callback: MatrixCallback?): Cancelable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt index beb3a0fcc0..3586f9b3af 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.integrationmanager.AllowedWidgetsContent import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData import im.vector.matrix.android.internal.task.Task @@ -50,6 +51,14 @@ internal interface UpdateUserAccountDataTask : Task Date: Wed, 6 May 2020 11:53:35 +0200 Subject: [PATCH 002/115] Start creating integration manager --- .../session/widgets/model/WidgetContent.kt | 12 +- .../android/internal/extensions/LiveData.kt | 29 ++ .../internal/session/DefaultSession.kt | 4 + .../AllowedWidgetsContent.kt | 4 +- .../integrationmanager/IntegrationManager.kt | 288 ++++++++++++++++++ .../model/accountdata/UserAccountDataEvent.kt | 3 +- .../accountdata/UpdateUserAccountDataTask.kt | 9 + 7 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/LiveData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt index 8b71fad29c..e71b6f61fc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/model/WidgetContent.kt @@ -22,11 +22,11 @@ import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) data class WidgetContent( - @Json(name = "creatorUserId") val creatorUserId: String, - @Json(name = "id") val id: String, - @Json(name = "type") val type: String, - @Json(name = "url") val url: String, - @Json(name = "name") val name: String, - @Json(name = "data") val data: JsonDict, + @Json(name = "creatorUserId") val creatorUserId: String? = null, + @Json(name = "id") val id: String? = null, + @Json(name = "type") val type: String? = null, + @Json(name = "url") val url: String? = null, + @Json(name = "name") val name: String? = null, + @Json(name = "data") val data: JsonDict = emptyMap(), @Json(name = "waitForIframeLoad") val waitForIframeLoad: Boolean = false ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/LiveData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/LiveData.kt new file mode 100644 index 0000000000..55c9e238e6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/LiveData.kt @@ -0,0 +1,29 @@ +/* + * 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.extensions + +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer + +inline fun LiveData.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) { + this.observe(owner, Observer { observer(it) }) +} + +inline fun LiveData.observeNotNull(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { + this.observe(owner, Observer { it?.run(observer) }) +} 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 b30c29a719..6dc2a5862d 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 @@ -50,6 +50,7 @@ import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater 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.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 @@ -97,6 +98,7 @@ internal class DefaultSession @Inject constructor( private val _sharedSecretStorageService: Lazy, private val accountService: Lazy, private val timelineEventDecryptor: TimelineEventDecryptor, + private val integrationManager: IntegrationManager, private val shieldTrustUpdater: ShieldTrustUpdater) : Session, RoomService by roomService.get(), @@ -133,6 +135,7 @@ internal class DefaultSession @Inject constructor( eventBus.register(this) timelineEventDecryptor.start() shieldTrustUpdater.start() + integrationManager.start() } override fun requireBackgroundSync() { @@ -175,6 +178,7 @@ internal class DefaultSession @Inject constructor( isOpen = false eventBus.unregister(this) shieldTrustUpdater.stop() + integrationManager.stop() } override fun getSyncStateLive(): LiveData { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt index 6f2c0883d5..0ef6fad8e7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/AllowedWidgetsContent.kt @@ -24,7 +24,7 @@ data class AllowedWidgetsContent( /** * Map of stateEventId to Allowed */ - @Json(name = "widgets") val widgets: Map, + @Json(name = "widgets") val widgets: Map = emptyMap(), /** * Map of native widgetType to a map of domain to Allowed @@ -35,5 +35,5 @@ data class AllowedWidgetsContent( * } * } */ - @Json(name = "native_widgets") val native: Map> + @Json(name = "native_widgets") val native: Map> = emptyMap() ) 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 new file mode 100644 index 0000000000..c2578e0660 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/integrationmanager/IntegrationManager.kt @@ -0,0 +1,288 @@ +/* + * 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 androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.events.model.toModel +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 +import im.vector.matrix.android.internal.extensions.observeNotNull +import im.vector.matrix.android.internal.session.SessionScope +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.user.accountdata.UpdateUserAccountDataTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import javax.inject.Inject + +/** + * The integration manager allows to + * - Get the Integration Manager that a user has explicitly set for its account (via account data) + * - Get the recommended/preferred Integration Manager list as defined by the HomeServer (via wellknown) + * - Check if the user has disabled the integration manager feature + * - Allow / Disallow Integration manager (propagated to other riot clients) + * + * The integration manager listen to account data, and can notify observer for changes. + * + * The wellknown is refreshed at each application fresh start + * + */ +@SessionScope +internal class IntegrationManager @Inject constructor(private val taskExecutor: TaskExecutor, + private val updateUserAccountDataTask: UpdateUserAccountDataTask, + private val accountDataDataSource: AccountDataDataSource) { + + interface Listener { + fun onIsEnabledChanged(enabled: Boolean) { + //No-op + } + + fun onConfigurationChanged(config: IntegrationManagerConfig) { + //No-op + } + + fun onWidgetPermissionsChanged(widgets: Map) { + //No-op + } + } + + private var currentConfig: IntegrationManagerConfig? = null + private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } + private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) + + private val listeners = HashSet() + fun addListener(listener: Listener) = synchronized(listeners) { listeners.add(listener) } + fun removeListener(listener: Listener) = synchronized(listeners) { listeners.remove(listener) } + + fun start() { + lifecycleRegistry.currentState = Lifecycle.State.STARTED + accountDataDataSource + .getLiveAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS) + .observeNotNull(lifecycleOwner) { + val allowedWidgetsContent = it.getOrNull()?.content?.toModel() + if (allowedWidgetsContent != null) { + notifyWidgetPermissionsChanged(allowedWidgetsContent) + } + } + accountDataDataSource + .getLiveAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_INTEGRATION_PROVISIONING) + .observeNotNull(lifecycleOwner) { + val integrationProvisioningContent = it.getOrNull()?.content?.toModel() + if (integrationProvisioningContent != null) { + notifyIsEnabledChanged(integrationProvisioningContent) + } + } + accountDataDataSource + .getLiveAccountDataEvent(UserAccountData.TYPE_WIDGETS) + .observeNotNull(lifecycleOwner) { + val integrationManager = it.getOrNull()?.asIntegrationManagerWidgetContent() + val config = integrationManager?.extractIntegrationManagerConfig() + if (config != null && config != currentConfig) { + currentConfig = config + notifyConfigurationChanged(config) + } + } + } + + fun stop() { + lifecycleRegistry.currentState = Lifecycle.State.DESTROYED + } + + /** + * Returns false if the user as disabled integration manager feature + */ + fun isIntegrationEnabled(): Boolean { + val integrationProvisioningData = accountDataDataSource.getAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_INTEGRATION_PROVISIONING) + val integrationProvisioningContent = integrationProvisioningData?.content?.toModel() + return integrationProvisioningContent?.enabled ?: false + } + + fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable { + val isIntegrationEnabled = isIntegrationEnabled() + if (enable == isIntegrationEnabled) { + callback.onSuccess(Unit) + return NoOpCancellable + } + val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable) + val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent) + return updateUserAccountDataTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) + } + + fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS) + val currentContent = currentAllowedWidgets?.content?.toModel() + val newContent = if (currentContent == null) { + val allowedWidget = mapOf(stateEventId to allowed) + AllowedWidgetsContent(widgets = allowedWidget, native = emptyMap()) + } else { + val allowedWidgets = currentContent.widgets.toMutableMap().apply { + put(stateEventId, allowed) + } + currentContent.copy(widgets = allowedWidgets) + } + val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) + return updateUserAccountDataTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) + } + + fun isWidgetAllowed(stateEventId: String): Boolean { + val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS) + val currentContent = currentAllowedWidgets?.content?.toModel() + return currentContent?.widgets?.get(stateEventId) ?: false + } + + fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS) + val currentContent = currentAllowedWidgets?.content?.toModel() + val newContent = if (currentContent == null) { + val nativeAllowedWidgets = mapOf(widgetType to mapOf(domain to allowed)) + AllowedWidgetsContent(widgets = emptyMap(), native = nativeAllowedWidgets) + } else { + val nativeAllowedWidgets = currentContent.native.toMutableMap().apply { + (get(widgetType))?.let { + set(widgetType, it.toMutableMap().apply { set(domain, allowed) }) + } ?: run { + set(widgetType, mapOf(domain to allowed)) + } + } + currentContent.copy(native = nativeAllowedWidgets) + } + val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) + return updateUserAccountDataTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) + } + + fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean { + val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.ACCOUNT_DATA_TYPE_ALLOWED_WIDGETS) + val currentContent = currentAllowedWidgets?.content?.toModel() + return currentContent?.native?.get(widgetType)?.get(domain) ?: false + } + + private fun notifyConfigurationChanged(config: IntegrationManagerConfig) { + Timber.v("On configuration changed : $config") + synchronized(listeners) { + listeners.forEach { + try { + it.onConfigurationChanged(config) + } catch (t: Throwable) { + Timber.e(t, "Failed to notify listener") + } + } + } + } + + private fun notifyWidgetPermissionsChanged(allowedWidgets: AllowedWidgetsContent) { + Timber.v("On widget permissions changed: $allowedWidgets") + synchronized(listeners) { + listeners.forEach { + try { + it.onWidgetPermissionsChanged(allowedWidgets.widgets) + } catch (t: Throwable) { + Timber.e(t, "Failed to notify listener") + } + } + } + } + + private fun notifyIsEnabledChanged(provisioningContent: IntegrationProvisioningContent) { + Timber.v("On provisioningContent changed : $provisioningContent") + synchronized(listeners) { + listeners.forEach { + try { + it.onIsEnabledChanged(provisioningContent.enabled) + } catch (t: Throwable) { + Timber.e(t, "Failed to notify listener") + } + } + } + } + + /* + private fun getStoreWellknownIM(): List { + val prefs = context.getSharedPreferences(PREFS_IM, Context.MODE_PRIVATE) + return prefs.getString(WELLKNOWN_KEY, null)?.let { + try { + Gson().fromJson>(it, + object : TypeToken>() {}.type) + } catch (any: Throwable) { + emptyList() + } + } ?: emptyList() + } + + private fun setStoreWellknownIM(list: List) { + val prefs = context.getSharedPreferences(PREFS_IM, Context.MODE_PRIVATE) + try { + val serialized = Gson().toJson(list) + prefs.edit().putString(WELLKNOWN_KEY, serialized).apply() + } catch (any: Throwable) { + //nop + } + } + + */ + + fun getConfig(): IntegrationManagerConfig? { + val accountWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.TYPE_WIDGETS) ?: return null + return accountWidgets.asIntegrationManagerWidgetContent()?.extractIntegrationManagerConfig() + } + + private fun WidgetContent.extractIntegrationManagerConfig(): IntegrationManagerConfig? { + if (url.isNullOrBlank()) { + return null + } + val integrationManagerData = data.toModel() + return IntegrationManagerConfig( + uiUrl = url, + apiUrl = integrationManagerData?.apiUrl ?: url + ) + } + + private fun UserAccountDataEvent.asIntegrationManagerWidgetContent(): WidgetContent? { + return content.asSequence() + .mapNotNull { + @Suppress("UNCHECKED_CAST") + (it.value as? Content)?.toModel() + }.filter { + it.type == INTEGRATION_MANAGER_WIDGET + } + .firstOrNull() + } + + companion object { + private const val INTEGRATION_MANAGER_WIDGET = "m.integration_manager" + private const val PREFS_IM = "IntegrationManager.Storage" + private const val WELLKNOWN_KEY = "WellKnown" + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt index a4ba0fc91a..65d3ce1ce7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt @@ -18,9 +18,10 @@ package im.vector.matrix.android.internal.session.sync.model.accountdata import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) data class UserAccountDataEvent( @Json(name = "type") override val type: String, - @Json(name = "content") val content: Map + @Json(name = "content") val content: JsonDict ) : UserAccountData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt index 3586f9b3af..22ab61c93a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.user.accountdata import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.integrationmanager.AllowedWidgetsContent +import im.vector.matrix.android.internal.session.integrationmanager.IntegrationProvisioningContent import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData import im.vector.matrix.android.internal.task.Task @@ -59,6 +60,14 @@ internal interface UpdateUserAccountDataTask : Task Date: Wed, 6 May 2020 15:53:28 +0200 Subject: [PATCH 003/115] Create WidgetsAPI --- .../session/widgets/RegisterWidgetResponse.kt | 25 +++++++++++++ .../internal/session/widgets/WidgetsAPI.kt | 36 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/RegisterWidgetResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/RegisterWidgetResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/RegisterWidgetResponse.kt new file mode 100644 index 0000000000..d84e36ce95 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/RegisterWidgetResponse.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session.widgets + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class RegisterWidgetResponse( + @Json(name = "scalar_token") val scalarToken: String? +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt new file mode 100644 index 0000000000..9a90561414 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt @@ -0,0 +1,36 @@ +/* + * 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 retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query + +internal interface WidgetsAPI { + /** + * register to the server + * + * @param requestOpenIdTokenResponse the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961) + */ + + //TODO Require work on RequestOpenIdTokenResponse from Benoit + @POST("register") + //fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call + + @GET("account") + fun validateToken(@Query("scalar_token") scalarToken: String?, @Query("v") version: String?): Call +} \ No newline at end of file From b047f36e863a1edaef437f0468a435e391dc208c Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 6 May 2020 20:49:07 +0200 Subject: [PATCH 004/115] 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 -> From 01d6b52a606b6ccf54f1e5561c5562f4dc94a240 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 11 May 2020 20:00:50 +0200 Subject: [PATCH 005/115] Widgets: handle scalar token --- .../database/model/ScalarTokenEntity.kt | 29 +++++++ .../database/model/SessionRealmModule.kt | 3 +- .../database/query/ScalarTokenQuery.kt | 29 +++++++ .../internal/session/DefaultSession.kt | 4 + .../internal/session/SessionComponent.kt | 2 + .../internal/session/widgets/WidgetModule.kt | 43 ++++++++++ .../internal/session/widgets/WidgetsAPI.kt | 7 +- .../session/widgets/WidgetsAPIProvider.kt | 38 +++++++++ .../widgets/token/GetScalarTokenTask.kt | 83 +++++++++++++++++++ .../session/widgets/token/ScalarTokenStore.kt | 30 ++++++- 10 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ScalarTokenEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ScalarTokenQuery.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPIProvider.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/GetScalarTokenTask.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ScalarTokenEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ScalarTokenEntity.kt new file mode 100644 index 0000000000..7c93624804 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ScalarTokenEntity.kt @@ -0,0 +1,29 @@ +/* + * 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.database.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +internal open class ScalarTokenEntity( + @PrimaryKey var serverUrl: String = "", + var token: String = "" +) : RealmObject() { + + companion object + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt index 081a6a5152..47ae0199cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt @@ -54,6 +54,7 @@ import io.realm.annotations.RealmModule HomeServerCapabilitiesEntity::class, RoomMemberSummaryEntity::class, CurrentStateEventEntity::class, - UserAccountDataEntity::class + UserAccountDataEntity::class, + ScalarTokenEntity::class ]) internal class SessionRealmModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ScalarTokenQuery.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ScalarTokenQuery.kt new file mode 100644 index 0000000000..02210394f5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ScalarTokenQuery.kt @@ -0,0 +1,29 @@ +/* + * 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.database.query + +import im.vector.matrix.android.internal.database.model.ScalarTokenEntity +import im.vector.matrix.android.internal.database.model.ScalarTokenEntityFields +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where + +internal fun ScalarTokenEntity.Companion.where(realm: Realm, serverUrl: String): RealmQuery { + return realm + .where() + .equalTo(ScalarTokenEntityFields.SERVER_URL, serverUrl) +} 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 6dc2a5862d..179d905412 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 @@ -55,6 +55,7 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecr 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.WidgetManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -99,6 +100,7 @@ internal class DefaultSession @Inject constructor( private val accountService: Lazy, private val timelineEventDecryptor: TimelineEventDecryptor, private val integrationManager: IntegrationManager, + private val widgetManager: WidgetManager, private val shieldTrustUpdater: ShieldTrustUpdater) : Session, RoomService by roomService.get(), @@ -136,6 +138,7 @@ internal class DefaultSession @Inject constructor( timelineEventDecryptor.start() shieldTrustUpdater.start() integrationManager.start() + widgetManager.start() } override fun requireBackgroundSync() { @@ -179,6 +182,7 @@ internal class DefaultSession @Inject constructor( eventBus.unregister(this) shieldTrustUpdater.stop() integrationManager.stop() + widgetManager.stop() } override fun getSyncStateLive(): LiveData { 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 0ebfc1c4c5..a99c2ba0aa 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 @@ -53,6 +53,7 @@ import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.session.user.UserModule import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule +import im.vector.matrix.android.internal.session.widgets.WidgetModule import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers @@ -72,6 +73,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers CryptoModule::class, PushersModule::class, OpenIdModule::class, + WidgetModule::class, AccountDataModule::class, ProfileModule::class, SessionAssistedInjectModule::class, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt new file mode 100644 index 0000000000..b3d9ddccfb --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetModule.kt @@ -0,0 +1,43 @@ +/* + * 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 dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask +import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask +import retrofit2.Retrofit + +@Module +internal abstract class WidgetModule { + + @Module + companion object { + @JvmStatic + @Provides + fun providesWidgetsAPI(retrofit: Retrofit): WidgetsAPI { + return retrofit.create(WidgetsAPI::class.java) + } + } + + @Binds + abstract fun bindCreateWidgetTask(task: DefaultCreateWidgetTask): CreateWidgetTask + + @Binds + abstract fun bindGetScalarTokenTask(task: DefaultGetScalarTokenTask): GetScalarTokenTask +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt index 9a90561414..7ad839bc9f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPI.kt @@ -15,21 +15,22 @@ */ package im.vector.matrix.android.internal.session.widgets +import im.vector.matrix.android.internal.session.openid.RequestOpenIdTokenResponse import retrofit2.Call +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Query internal interface WidgetsAPI { + /** * register to the server * * @param requestOpenIdTokenResponse the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961) */ - - //TODO Require work on RequestOpenIdTokenResponse from Benoit @POST("register") - //fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call + fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call @GET("account") fun validateToken(@Query("scalar_token") scalarToken: String?, @Query("v") version: String?): Call diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPIProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPIProvider.kt new file mode 100644 index 0000000000..05d10dee56 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetsAPIProvider.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.matrix.android.internal.session.widgets + +import dagger.Lazy +import im.vector.matrix.android.internal.di.Unauthenticated +import im.vector.matrix.android.internal.network.RetrofitFactory +import im.vector.matrix.android.internal.session.SessionScope +import okhttp3.OkHttpClient +import javax.inject.Inject + +@SessionScope +internal class WidgetsAPIProvider @Inject constructor(@Unauthenticated private val okHttpClient: Lazy, + private val retrofitFactory: RetrofitFactory) { + + // Map to keep one WidgetAPI instance by serverUrl + private val widgetsAPIs = mutableMapOf() + + fun get(serverUrl: String): WidgetsAPI { + return widgetsAPIs.getOrPut(serverUrl) { + retrofitFactory.create(okHttpClient, serverUrl).create(WidgetsAPI::class.java) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/GetScalarTokenTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/GetScalarTokenTask.kt new file mode 100644 index 0000000000..4e8d257f69 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/token/GetScalarTokenTask.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.widgets.token + +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.openid.GetOpenIdTokenTask +import im.vector.matrix.android.internal.session.widgets.RegisterWidgetResponse +import im.vector.matrix.android.internal.session.widgets.WidgetsAPI +import im.vector.matrix.android.internal.session.widgets.WidgetsAPIProvider +import im.vector.matrix.android.internal.task.Task +import java.net.HttpURLConnection +import javax.inject.Inject + +internal interface GetScalarTokenTask : Task { + + data class Params( + val serverUrl: String + ) +} + +private const val WIDGET_API_VERSION = "1.1" + +internal class DefaultGetScalarTokenTask @Inject constructor(private val widgetsAPIProvider: WidgetsAPIProvider, + private val scalarTokenStore: ScalarTokenStore, + private val getOpenIdTokenTask: GetOpenIdTokenTask) : GetScalarTokenTask { + + override suspend fun execute(params: GetScalarTokenTask.Params): String { + val widgetsAPI = widgetsAPIProvider.get(params.serverUrl) + val scalarToken = scalarTokenStore.getToken(params.serverUrl) + return if (scalarToken == null) { + getNewScalarToken(widgetsAPI, params.serverUrl) + } else { + validateToken(widgetsAPI, params.serverUrl, scalarToken) + } + } + + private suspend fun getNewScalarToken(widgetsAPI: WidgetsAPI, serverUrl: String): String { + val openId = getOpenIdTokenTask.execute(Unit) + val registerWidgetResponse = executeRequest(null) { + apiCall = widgetsAPI.register(openId, WIDGET_API_VERSION) + } + if (registerWidgetResponse.scalarToken == null) { + // Should not happen + throw IllegalStateException("Scalar token is null") + } + scalarTokenStore.setToken(serverUrl, registerWidgetResponse.scalarToken) + widgetsAPI.validateToken(registerWidgetResponse.scalarToken, WIDGET_API_VERSION) + return registerWidgetResponse.scalarToken + } + + private suspend fun validateToken(widgetsAPI: WidgetsAPI, serverUrl: String, scalarToken: String): String { + return try { + widgetsAPI.validateToken(scalarToken, WIDGET_API_VERSION) + scalarToken + } catch (failure: Throwable) { + if (failure.isScalarTokenError()) { + scalarTokenStore.clearToken(serverUrl) + getNewScalarToken(widgetsAPI, serverUrl) + } else { + throw failure + } + } + } + + private fun Throwable.isScalarTokenError(): Boolean { + return this is Failure.ServerError && this.httpCode == HttpURLConnection.HTTP_FORBIDDEN + } +} 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 index db87c6af8e..87a2eec782 100644 --- 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 @@ -16,5 +16,33 @@ package im.vector.matrix.android.internal.session.widgets.token -internal class ScalarTokenStore { +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.internal.database.model.ScalarTokenEntity +import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.util.awaitTransaction +import im.vector.matrix.android.internal.util.fetchCopyMap +import javax.inject.Inject + +internal class ScalarTokenStore @Inject constructor(private val monarchy: Monarchy) { + + fun getToken(apiUrl: String): String? { + return monarchy.fetchCopyMap({ realm -> + ScalarTokenEntity.where(realm, apiUrl).findFirst() + }, { scalarToken, _ -> + scalarToken.serverUrl + }) + } + + suspend fun setToken(apiUrl: String, token: String) { + monarchy.awaitTransaction { realm -> + val scalarTokenEntity = ScalarTokenEntity(apiUrl, token) + realm.insertOrUpdate(scalarTokenEntity) + } + } + + suspend fun clearToken(apiUrl: String) { + monarchy.awaitTransaction { realm -> + ScalarTokenEntity.where(realm, apiUrl).findFirst()?.deleteFromRealm() + } + } } From 91301197eac5422ebbc1495c9e86777b083f049a Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 13 May 2020 20:04:08 +0200 Subject: [PATCH 006/115] Try to communicate with WidgetPostAPI --- .idea/codeStyles/Project.xml | 3 + .../src/main/assets/postMessageAPI.js | 54 ++++++ .../matrix/android/api/session/Session.kt | 4 +- .../session/widgets/WidgetPostAPIMediator.kt | 87 +++++++++ .../api/session/widgets/WidgetService.kt | 41 +++++ .../session/widgets/model/WidgetContent.kt | 26 ++- .../internal/session/DefaultSession.kt | 5 +- .../widgets/DefaultWidgetPostAPIMediator.kt | 173 ++++++++++++++++++ .../session/widgets/DefaultWidgetService.kt | 50 +++++ ...tFailure.kt => WidgetManagementFailure.kt} | 6 +- .../internal/session/widgets/WidgetManager.kt | 18 +- .../internal/session/widgets/WidgetModule.kt | 8 + .../widgets/WidgetPostMessageAPIProvider.kt | 44 +++++ .../internal/task/CoroutineToCallback.kt | 6 +- 14 files changed, 516 insertions(+), 9 deletions(-) create mode 100755 matrix-sdk-android/src/main/assets/postMessageAPI.js create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetPostAPIMediator.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/widgets/WidgetService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetPostAPIMediator.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/DefaultWidgetService.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/{CreateWidgetFailure.kt => WidgetManagementFailure.kt} (80%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetPostMessageAPIProvider.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index b3719669fd..40ee4ee5cf 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,9 @@