diff --git a/dependencies.gradle b/dependencies.gradle index 80dc203740..7a9ed3f931 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -104,6 +104,7 @@ ext.libs = [ 'moshi' : "com.squareup.moshi:moshi:$moshi", 'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi", 'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi", + 'moshiAdapters' : "com.squareup.moshi:moshi-adapters:$moshi", 'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit", 'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit" ], diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index fcd1e7d622..faa798c9dc 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -163,6 +163,7 @@ dependencies { implementation 'com.squareup.okhttp3:logging-interceptor' implementation libs.squareup.moshi + implementation libs.squareup.moshiAdapters kapt libs.squareup.moshiKotlin api "com.atlassian.commonmark:commonmark:0.13.0" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/ThreePid.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/ThreePid.kt index 6bcf576824..24748f88e4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/ThreePid.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/ThreePid.kt @@ -18,10 +18,14 @@ package org.matrix.android.sdk.api.session.identity import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.PhoneNumberUtil +import com.squareup.moshi.JsonClass import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier sealed class ThreePid(open val value: String) { + @JsonClass(generateAdapter = true) data class Email(val email: String) : ThreePid(email) + + @JsonClass(generateAdapter = true) data class Msisdn(val msisdn: String) : ThreePid(msisdn) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt index b7b0cc890b..e959dcc81c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt @@ -17,13 +17,16 @@ package org.matrix.android.sdk.api.session.room.model.create import android.net.Uri +import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.internal.di.MoshiProvider +@JsonClass(generateAdapter = true) open class CreateRoomParams { /** * A public visibility indicates that the room will be shown in the published room list. @@ -61,12 +64,12 @@ open class CreateRoomParams { * A list of user IDs to invite to the room. * This will tell the server to invite everyone in the list to the newly created room. */ - val invitedUserIds = mutableListOf() + var invitedUserIds: MutableList = mutableListOf() /** * A list of objects representing third party IDs to invite into the room. */ - val invite3pids = mutableListOf() + var invite3pids = mutableListOf() /** * Initial Guest Access. @@ -99,14 +102,14 @@ open class CreateRoomParams { * The server will clobber the following keys: creator. * Future versions of the specification may allow the server to clobber other keys. */ - val creationContent = mutableMapOf() + var creationContent = mutableMapOf() /** * A list of state events to set in the new room. This allows the user to override the default state events * set in the new room. The expected format of the state events are an object with type, state_key and content keys set. * Takes precedence over events set by preset, but gets overridden by name and topic keys. */ - val initialStates = mutableListOf() + var initialStates = mutableListOf() /** * Set to true to disable federation of this room. @@ -151,7 +154,7 @@ open class CreateRoomParams { * Supported value: MXCRYPTO_ALGORITHM_MEGOLM. */ var algorithm: String? = null - private set + internal set var historyVisibility: RoomHistoryVisibility? = null @@ -161,10 +164,18 @@ open class CreateRoomParams { var roomVersion: String? = null - var featurePreset: RoomFeaturePreset? = null + @Transient var featurePreset: RoomFeaturePreset? = null companion object { private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate" private const val CREATION_CONTENT_KEY_ROOM_TYPE = "type" + + fun fromJson(json: String?): CreateRoomParams? { + return json?.let { MoshiProvider.providesMoshi().adapter(CreateRoomParams::class.java).fromJson(it) } + } } } + +internal fun CreateRoomParams.toJSONString(): String { + return MoshiProvider.providesMoshi().adapter(CreateRoomParams::class.java).toJson(this) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomStateEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomStateEvent.kt index fcfdc3e333..d89c72c513 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomStateEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomStateEvent.kt @@ -16,8 +16,10 @@ package org.matrix.android.sdk.api.session.room.model.create +import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Content +@JsonClass(generateAdapter = true) data class CreateRoomStateEvent( /** * Required. The type of event to send. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt index 72e998b377..78347fcafc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room.model.localecho import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.model.Membership /** @@ -29,11 +30,5 @@ data class LocalRoomThirdPartyInviteContent( @Json(name = "membership") val membership: Membership, @Json(name = "displayname") val displayName: String? = null, @Json(name = "is_direct") val isDirect: Boolean = false, - @Json(name = "third_party_invite") val thirdPartyInvite: LocalThreePid? = null, -) - -@JsonClass(generateAdapter = true) -data class LocalThreePid( - val email: String? = null, - val msisdn: String? = null, + @Json(name = "third_party_invite") val thirdPartyInvite: ThreePid? = null, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index b733aa6fc0..0b11863864 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -52,6 +52,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo032 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo033 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -60,7 +61,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 35L, + schemaVersion = 36L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -105,5 +106,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 33) MigrateSessionTo033(realm).perform() if (oldVersion < 34) MigrateSessionTo034(realm).perform() if (oldVersion < 35) MigrateSessionTo035(realm).perform() + if (oldVersion < 36) MigrateSessionTo036(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo036.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo036.kt new file mode 100644 index 0000000000..efcb181ecb --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo036.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo036(realm: DynamicRealm) : RealmMigrator(realm, 36) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.create("LocalRoomSummaryEntity") + .addField(LocalRoomSummaryEntityFields.ROOM_ID, String::class.java) + .addPrimaryKey(LocalRoomSummaryEntityFields.ROOM_ID) + .setRequired(LocalRoomSummaryEntityFields.ROOM_ID, true) + .addField(LocalRoomSummaryEntityFields.CREATE_ROOM_PARAMS_STR, String::class.java) + .addRealmObjectField(LocalRoomSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt new file mode 100644 index 0000000000..fd8331e986 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.database.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams +import org.matrix.android.sdk.api.session.room.model.create.toJSONString + +internal open class LocalRoomSummaryEntity( + @PrimaryKey var roomId: String = "", + var roomSummaryEntity: RoomSummaryEntity? = null, + private var createRoomParamsStr: String? = null +) : RealmObject() { + + var createRoomParams: CreateRoomParams? + get() { + return CreateRoomParams.fromJson(createRoomParamsStr) + } + set(value) { + createRoomParamsStr = value?.toJSONString() + } + + companion object +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt index d131589dd1..b222bcb710 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit ReadReceiptEntity::class, RoomEntity::class, RoomSummaryEntity::class, + LocalRoomSummaryEntity::class, RoomTagEntity::class, SyncEntity::class, PendingThreePidEntity::class, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt new file mode 100644 index 0000000000..527350bedc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.database.query + +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields + +internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery { + val query = realm.where() + if (roomId != null) { + query.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId) + } + return query +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt index 8f007f227c..0a737d5e64 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.internal.di import com.squareup.moshi.Moshi +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory +import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageDefaultContent @@ -60,6 +62,12 @@ internal object MoshiProvider { .registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_POLL_RESPONSE) ) .add(SerializeNulls.JSON_ADAPTER_FACTORY) + .add( + PolymorphicJsonAdapterFactory.of(ThreePid::class.java, "type") + .withSubtype(ThreePid.Email::class.java, "email") + .withSubtype(ThreePid.Msisdn::class.java, "msisdn") + .withDefaultValue(null) + ) .build() fun providesMoshi(): Moshi { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt index 4f8dc11d2c..78c8ebe099 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt @@ -38,7 +38,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.model.localecho.LocalRoomThirdPartyInviteContent -import org.matrix.android.sdk.api.session.room.model.localecho.LocalThreePid import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary @@ -52,6 +51,7 @@ import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EventInsertType +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity @@ -63,6 +63,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler +import org.matrix.android.sdk.internal.session.room.membership.threepid.toThreePid import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.task.Task @@ -90,7 +91,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor( val roomId = RoomLocalEcho.createLocalEchoId() monarchy.awaitTransaction { realm -> createLocalRoomEntity(realm, roomId, createRoomBody) - createLocalRoomSummaryEntity(realm, roomId, createRoomBody) + createLocalRoomSummaryEntity(realm, roomId, createRoomBody, params) } // Wait for room to be created in DB @@ -119,14 +120,18 @@ internal class DefaultCreateLocalRoomTask @Inject constructor( } } - private fun createLocalRoomSummaryEntity(realm: Realm, roomId: String, createRoomBody: CreateRoomBody) { - val otherUserId = createRoomBody.getDirectUserId() - if (otherUserId != null) { - RoomSummaryEntity.getOrCreate(realm, roomId).apply { + private fun createLocalRoomSummaryEntity(realm: Realm, roomId: String, createRoomBody: CreateRoomBody, createRoomParams: CreateRoomParams) { + val roomSummaryEntity = realm.createObject(roomId).apply { + val otherUserId = createRoomBody.getDirectUserId() + if (otherUserId != null) { isDirect = true directUserId = otherUserId } } + realm.createObject(roomId).also { + it.roomSummaryEntity = roomSummaryEntity + it.createRoomParams = createRoomParams + } roomSummaryUpdater.update( realm = realm, roomId = roomId, @@ -244,10 +249,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor( isDirect = createRoomBody.isDirect.orFalse(), membership = Membership.INVITE, displayName = body.address, - thirdPartyInvite = LocalThreePid( - msisdn = body.address.takeIf { body.medium == "msisdn" }, - email = body.address.takeIf { body.medium == "email" } - ) + thirdPartyInvite = body.toThreePid() ).toContent() ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt index b326c3618c..17e1aba6f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset +import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody /** @@ -119,7 +120,13 @@ internal data class CreateRoomBody( */ @Json(name = "room_version") val roomVersion: String? -) +) { + companion object { + fun fromJson(json: String?): CreateRoomBody? { + return json?.let { MoshiProvider.providesMoshi().adapter(CreateRoomBody::class.java).fromJson(it) } + } + } +} /** * Tells if the created room can be a direct chat one. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt index 1d569370db..c59c56ffde 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt @@ -16,49 +16,31 @@ package org.matrix.android.sdk.internal.session.room.create -import android.util.Patterns -import androidx.core.net.toUri -import com.google.i18n.phonenumbers.NumberParseException -import com.google.i18n.phonenumbers.PhoneNumberUtil import com.zhuinden.monarchy.Monarchy -import io.realm.Realm +import io.realm.kotlin.where import kotlinx.coroutines.TimeoutCancellationException -import org.matrix.android.sdk.api.extensions.ensurePrefix +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent -import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent -import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent -import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent -import org.matrix.android.sdk.api.session.room.model.RoomJoinRules -import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent -import org.matrix.android.sdk.api.session.room.model.RoomMemberContent -import org.matrix.android.sdk.api.session.room.model.RoomNameContent -import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams -import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset -import org.matrix.android.sdk.api.session.room.model.localecho.LocalRoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.database.awaitNotEmptyResult -import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.EventInsertType +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.getOrCreate -import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.whereRoomId import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId @@ -101,21 +83,31 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( return replacementRoomId } - val createRoomParams = getCreateRoomParams(params) - val roomId = createRoomTask.execute(createRoomParams) + var createRoomParams: CreateRoomParams? = null + var isEncrypted = false + monarchy.doWithRealm { realm -> + realm.where() + .equalTo(LocalRoomSummaryEntityFields.ROOM_ID, params.localRoomId) + .findFirst() + ?.let { + createRoomParams = it.createRoomParams + isEncrypted = it.roomSummaryEntity?.isEncrypted.orFalse() + } + } + val roomId = createRoomTask.execute(createRoomParams!!) try { // Wait for all the room events before triggering the replacement room awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> realm.where(RoomSummaryEntity::class.java) .equalTo(RoomSummaryEntityFields.ROOM_ID, roomId) - .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams.invitedUserIds.size.minus(1)) + .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams?.invitedUserIds?.size ?: 0) } awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> EventEntity.whereRoomId(realm, roomId) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY) } - if (createRoomParams.algorithm != null) { + if (isEncrypted) { awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> EventEntity.whereRoomId(realm, roomId) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION) @@ -129,35 +121,6 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( return roomId } - /** - * Retrieve the room configuration by parsing the state events related to the local room. - */ - private suspend fun getCreateRoomParams(params: CreateRoomFromLocalRoomTask.Params): CreateRoomParams { - var createRoomParams = CreateRoomParams() - monarchy.awaitTransaction { realm -> - val stateEvents = CurrentStateEventEntity.whereRoomId(realm, params.localRoomId).findAll() - stateEvents.forEach { event -> - createRoomParams = when (event.type) { - EventType.STATE_ROOM_MEMBER -> handleRoomMemberEvent(realm, event, createRoomParams) - EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE -> handleLocalRoomThirdPartyInviteEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_HISTORY_VISIBILITY -> handleRoomHistoryVisibilityEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_ALIASES -> handleRoomAliasesEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_AVATAR -> handleRoomAvatarEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_CANONICAL_ALIAS -> handleRoomCanonicalAliasEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_GUEST_ACCESS -> handleRoomGuestAccessEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_ENCRYPTION -> handleRoomEncryptionEvent(createRoomParams) - EventType.STATE_ROOM_POWER_LEVELS -> handleRoomPowerRoomLevelsEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_NAME -> handleRoomNameEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_TOPIC -> handleRoomTopicEvent(realm, event, createRoomParams) - EventType.STATE_ROOM_THIRD_PARTY_INVITE -> handleRoomThirdPartyInviteEvent(event, createRoomParams) - EventType.STATE_ROOM_JOIN_RULES -> handleRoomJoinRulesEvent(realm, event, createRoomParams) - else -> createRoomParams - } - } - } - return createRoomParams - } - /** * Create a Tombstone event to indicate that the local room has been replaced by a new one. */ @@ -183,117 +146,4 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( } } } - - /* ========================================================================================== - * Local events handling - * ========================================================================================== */ - - private fun handleRoomMemberEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - invitedUserIds.add(event.stateKey) - if (content.isDirect) { - setDirectMessage() - } - } - - private fun handleLocalRoomThirdPartyInviteEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - val threePid = when { - content.thirdPartyInvite?.email != null -> ThreePid.Email(content.thirdPartyInvite.email) - content.thirdPartyInvite?.msisdn != null -> ThreePid.Msisdn(content.thirdPartyInvite.msisdn) - else -> return@apply - } - invite3pids.add(threePid) - if (content.isDirect) { - setDirectMessage() - } - } - - private fun handleRoomHistoryVisibilityEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - historyVisibility = content.historyVisibility - } - - private fun handleRoomAliasesEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - roomAliasName = content.aliases.firstOrNull()?.substringAfter("#")?.substringBefore(":") - } - - private fun handleRoomAvatarEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - avatarUri = content.avatarUrl?.toUri() - } - - private fun handleRoomCanonicalAliasEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - roomAliasName = content.canonicalAlias?.substringAfter("#")?.substringBefore(":") - } - - private fun handleRoomGuestAccessEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - guestAccess = content.guestAccess - } - - private fun handleRoomEncryptionEvent(params: CreateRoomParams): CreateRoomParams = params.apply { - // Having an encryption event means the room is encrypted, so just enable it again - enableEncryption() - } - - private fun handleRoomPowerRoomLevelsEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - powerLevelContentOverride = content - } - - private fun handleRoomNameEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - name = content.name - } - - private fun handleRoomTopicEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - topic = content.topic - } - - private fun handleRoomThirdPartyInviteEvent(event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - when { - event.stateKey.isEmail() -> invite3pids.add(ThreePid.Email(event.stateKey)) - event.stateKey.isMsisdn() -> invite3pids.add(ThreePid.Msisdn(event.stateKey)) - } - } - - private fun handleRoomJoinRulesEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { - val content = getEventContent(realm, event.eventId) ?: return@apply - preset = when { - // If preset has already been set for direct chat, keep it - preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT -> CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT - content.joinRules == RoomJoinRules.PUBLIC -> CreateRoomPreset.PRESET_PUBLIC_CHAT - content.joinRules == RoomJoinRules.INVITE -> CreateRoomPreset.PRESET_PRIVATE_CHAT - else -> null - } - } - - /* ========================================================================================== - * Helper methods - * ========================================================================================== */ - - private inline fun getEventContent(realm: Realm, eventId: String): T? { - return EventEntity.where(realm, eventId).findFirst()?.asDomain()?.getClearContent().toModel() - } - - /** - * Check if a CharSequence is an email. - */ - private fun CharSequence.isEmail() = Patterns.EMAIL_ADDRESS.matcher(this).matches() - - /** - * Check if a CharSequence is a phone number. - */ - private fun CharSequence.isMsisdn(): Boolean { - return try { - PhoneNumberUtil.getInstance().parse(ensurePrefix("+"), null) - true - } catch (e: NumberParseException) { - false - } - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 137134e43f..e558d34ff9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -70,7 +70,6 @@ internal class DefaultCreateRoomTask @Inject constructor( } val createRoomBody = createRoomBodyBuilder.build(params) - val createRoomResponse = try { executeRequest(globalErrorReceiver) { roomAPI.createRoom(createRoomBody) @@ -89,6 +88,7 @@ internal class DefaultCreateRoomTask @Inject constructor( } throw throwable } + val roomId = createRoomResponse.roomId // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) try { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt index 936c94e520..49951d2da0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EventEntity +import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity @@ -70,6 +71,9 @@ internal class DefaultDeleteLocalRoomTask @Inject constructor( RoomEntity.where(realm, roomId = roomId).findAll() ?.also { Timber.i("## DeleteLocalRoomTask - RoomEntity - delete ${it.size} entries") } ?.deleteAllFromRealm() + LocalRoomSummaryEntity.where(realm, roomId = roomId).findAll() + ?.also { Timber.i("## DeleteLocalRoomTask - LocalRoomSummaryEntity - delete ${it.size} entries") } + ?.deleteAllFromRealm() } } else { Timber.i("## DeleteLocalRoomTask - Failed to remove room with id $roomId: not a local room") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/ThreePidInviteBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/ThreePidInviteBody.kt index 3141c052c3..d7b78faea8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/ThreePidInviteBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/ThreePidInviteBody.kt @@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.room.membership.threepid import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.internal.auth.data.ThreePidMedium @JsonClass(generateAdapter = true) internal data class ThreePidInviteBody( @@ -43,3 +45,9 @@ internal data class ThreePidInviteBody( @Json(name = "address") val address: String ) + +internal fun ThreePidInviteBody.toThreePid() = when (medium) { + ThreePidMedium.EMAIL -> ThreePid.Email(address) + ThreePidMedium.MSISDN -> ThreePid.Msisdn(address) + else -> null +}