Persists CreateRoomParams into LocalRoomSummaryEntity

This commit is contained in:
Florian Renaud 2022-08-05 09:52:12 +02:00
parent 69917ebc2e
commit c96343f1d7
18 changed files with 193 additions and 194 deletions

View file

@ -104,6 +104,7 @@ ext.libs = [
'moshi' : "com.squareup.moshi:moshi:$moshi", 'moshi' : "com.squareup.moshi:moshi:$moshi",
'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi", 'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi",
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi", 'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
'moshiAdapters' : "com.squareup.moshi:moshi-adapters:$moshi",
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit", 'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit" 'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
], ],

View file

@ -163,6 +163,7 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor' implementation 'com.squareup.okhttp3:logging-interceptor'
implementation libs.squareup.moshi implementation libs.squareup.moshi
implementation libs.squareup.moshiAdapters
kapt libs.squareup.moshiKotlin kapt libs.squareup.moshiKotlin
api "com.atlassian.commonmark:commonmark:0.13.0" api "com.atlassian.commonmark:commonmark:0.13.0"

View file

@ -18,10 +18,14 @@ package org.matrix.android.sdk.api.session.identity
import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier
sealed class ThreePid(open val value: String) { sealed class ThreePid(open val value: String) {
@JsonClass(generateAdapter = true)
data class Email(val email: String) : ThreePid(email) data class Email(val email: String) : ThreePid(email)
@JsonClass(generateAdapter = true)
data class Msisdn(val msisdn: String) : ThreePid(msisdn) data class Msisdn(val msisdn: String) : ThreePid(msisdn)
} }

View file

@ -17,13 +17,16 @@
package org.matrix.android.sdk.api.session.room.model.create package org.matrix.android.sdk.api.session.room.model.create
import android.net.Uri 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.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.api.session.identity.ThreePid 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.GuestAccess
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.internal.di.MoshiProvider
@JsonClass(generateAdapter = true)
open class CreateRoomParams { open class CreateRoomParams {
/** /**
* A public visibility indicates that the room will be shown in the published room list. * 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. * 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. * This will tell the server to invite everyone in the list to the newly created room.
*/ */
val invitedUserIds = mutableListOf<String>() var invitedUserIds: MutableList<String> = mutableListOf()
/** /**
* A list of objects representing third party IDs to invite into the room. * A list of objects representing third party IDs to invite into the room.
*/ */
val invite3pids = mutableListOf<ThreePid>() var invite3pids = mutableListOf<ThreePid>()
/** /**
* Initial Guest Access. * Initial Guest Access.
@ -99,14 +102,14 @@ open class CreateRoomParams {
* The server will clobber the following keys: creator. * The server will clobber the following keys: creator.
* Future versions of the specification may allow the server to clobber other keys. * Future versions of the specification may allow the server to clobber other keys.
*/ */
val creationContent = mutableMapOf<String, Any>() var creationContent = mutableMapOf<String, Any>()
/** /**
* A list of state events to set in the new room. This allows the user to override the default state events * 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. * 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. * Takes precedence over events set by preset, but gets overridden by name and topic keys.
*/ */
val initialStates = mutableListOf<CreateRoomStateEvent>() var initialStates = mutableListOf<CreateRoomStateEvent>()
/** /**
* Set to true to disable federation of this room. * Set to true to disable federation of this room.
@ -151,7 +154,7 @@ open class CreateRoomParams {
* Supported value: MXCRYPTO_ALGORITHM_MEGOLM. * Supported value: MXCRYPTO_ALGORITHM_MEGOLM.
*/ */
var algorithm: String? = null var algorithm: String? = null
private set internal set
var historyVisibility: RoomHistoryVisibility? = null var historyVisibility: RoomHistoryVisibility? = null
@ -161,10 +164,18 @@ open class CreateRoomParams {
var roomVersion: String? = null var roomVersion: String? = null
var featurePreset: RoomFeaturePreset? = null @Transient var featurePreset: RoomFeaturePreset? = null
companion object { companion object {
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate" private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
private const val CREATION_CONTENT_KEY_ROOM_TYPE = "type" 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)
}

View file

@ -16,8 +16,10 @@
package org.matrix.android.sdk.api.session.room.model.create 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 import org.matrix.android.sdk.api.session.events.model.Content
@JsonClass(generateAdapter = true)
data class CreateRoomStateEvent( data class CreateRoomStateEvent(
/** /**
* Required. The type of event to send. * Required. The type of event to send.

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room.model.localecho
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.room.model.Membership 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 = "membership") val membership: Membership,
@Json(name = "displayname") val displayName: String? = null, @Json(name = "displayname") val displayName: String? = null,
@Json(name = "is_direct") val isDirect: Boolean = false, @Json(name = "is_direct") val isDirect: Boolean = false,
@Json(name = "third_party_invite") val thirdPartyInvite: LocalThreePid? = null, @Json(name = "third_party_invite") val thirdPartyInvite: ThreePid? = null,
)
@JsonClass(generateAdapter = true)
data class LocalThreePid(
val email: String? = null,
val msisdn: String? = null,
) )

View file

@ -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.MigrateSessionTo033
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034 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.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.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject import javax.inject.Inject
@ -60,7 +61,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer private val normalizer: Normalizer
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Session", dbName = "Session",
schemaVersion = 35L, schemaVersion = 36L,
) { ) {
/** /**
* Forces all RealmSessionStoreMigration instances to be equal. * Forces all RealmSessionStoreMigration instances to be equal.
@ -105,5 +106,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 33) MigrateSessionTo033(realm).perform() if (oldVersion < 33) MigrateSessionTo033(realm).perform()
if (oldVersion < 34) MigrateSessionTo034(realm).perform() if (oldVersion < 34) MigrateSessionTo034(realm).perform()
if (oldVersion < 35) MigrateSessionTo035(realm).perform() if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
} }
} }

View file

@ -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")!!)
}
}

View file

@ -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
}

View file

@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
ReadReceiptEntity::class, ReadReceiptEntity::class,
RoomEntity::class, RoomEntity::class,
RoomSummaryEntity::class, RoomSummaryEntity::class,
LocalRoomSummaryEntity::class,
RoomTagEntity::class, RoomTagEntity::class,
SyncEntity::class, SyncEntity::class,
PendingThreePidEntity::class, PendingThreePidEntity::class,

View file

@ -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<LocalRoomSummaryEntity> {
val query = realm.where<LocalRoomSummaryEntity>()
if (roomId != null) {
query.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
}
return query
}

View file

@ -17,6 +17,8 @@
package org.matrix.android.sdk.internal.di package org.matrix.android.sdk.internal.di
import com.squareup.moshi.Moshi 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.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageDefaultContent 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) .registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_POLL_RESPONSE)
) )
.add(SerializeNulls.JSON_ADAPTER_FACTORY) .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() .build()
fun providesMoshi(): Moshi { fun providesMoshi(): Moshi {

View file

@ -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.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent 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.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.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary 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.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity 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.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.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity 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.di.UserId
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent 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.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.summary.RoomSummaryUpdater
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
@ -90,7 +91,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
val roomId = RoomLocalEcho.createLocalEchoId() val roomId = RoomLocalEcho.createLocalEchoId()
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->
createLocalRoomEntity(realm, roomId, createRoomBody) createLocalRoomEntity(realm, roomId, createRoomBody)
createLocalRoomSummaryEntity(realm, roomId, createRoomBody) createLocalRoomSummaryEntity(realm, roomId, createRoomBody, params)
} }
// Wait for room to be created in DB // 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) { private fun createLocalRoomSummaryEntity(realm: Realm, roomId: String, createRoomBody: CreateRoomBody, createRoomParams: CreateRoomParams) {
val otherUserId = createRoomBody.getDirectUserId() val roomSummaryEntity = realm.createObject<RoomSummaryEntity>(roomId).apply {
if (otherUserId != null) { val otherUserId = createRoomBody.getDirectUserId()
RoomSummaryEntity.getOrCreate(realm, roomId).apply { if (otherUserId != null) {
isDirect = true isDirect = true
directUserId = otherUserId directUserId = otherUserId
} }
} }
realm.createObject<LocalRoomSummaryEntity>(roomId).also {
it.roomSummaryEntity = roomSummaryEntity
it.createRoomParams = createRoomParams
}
roomSummaryUpdater.update( roomSummaryUpdater.update(
realm = realm, realm = realm,
roomId = roomId, roomId = roomId,
@ -244,10 +249,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
isDirect = createRoomBody.isDirect.orFalse(), isDirect = createRoomBody.isDirect.orFalse(),
membership = Membership.INVITE, membership = Membership.INVITE,
displayName = body.address, displayName = body.address,
thirdPartyInvite = LocalThreePid( thirdPartyInvite = body.toThreePid()
msisdn = body.address.takeIf { body.medium == "msisdn" },
email = body.address.takeIf { body.medium == "email" }
)
).toContent() ).toContent()
) )
} }

View file

@ -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.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility 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.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 import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody
/** /**
@ -119,7 +120,13 @@ internal data class CreateRoomBody(
*/ */
@Json(name = "room_version") @Json(name = "room_version")
val roomVersion: String? 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. * Tells if the created room can be a direct chat one.

View file

@ -16,49 +16,31 @@
package org.matrix.android.sdk.internal.session.room.create 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 com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.kotlin.where
import kotlinx.coroutines.TimeoutCancellationException 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.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event 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.EventType
import org.matrix.android.sdk.api.session.events.model.toContent 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.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.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.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.model.tombstone.RoomTombstoneContent
import org.matrix.android.sdk.api.session.room.send.SendState 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.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.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity 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.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields 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.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.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields 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.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate 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.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
@ -101,21 +83,31 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor(
return replacementRoomId return replacementRoomId
} }
val createRoomParams = getCreateRoomParams(params) var createRoomParams: CreateRoomParams? = null
val roomId = createRoomTask.execute(createRoomParams) var isEncrypted = false
monarchy.doWithRealm { realm ->
realm.where<LocalRoomSummaryEntity>()
.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, params.localRoomId)
.findFirst()
?.let {
createRoomParams = it.createRoomParams
isEncrypted = it.roomSummaryEntity?.isEncrypted.orFalse()
}
}
val roomId = createRoomTask.execute(createRoomParams!!)
try { try {
// Wait for all the room events before triggering the replacement room // Wait for all the room events before triggering the replacement room
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomSummaryEntity::class.java) realm.where(RoomSummaryEntity::class.java)
.equalTo(RoomSummaryEntityFields.ROOM_ID, roomId) .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 -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId) EventEntity.whereRoomId(realm, roomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY)
} }
if (createRoomParams.algorithm != null) { if (isEncrypted) {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId) EventEntity.whereRoomId(realm, roomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION)
@ -129,35 +121,6 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor(
return roomId 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. * 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<RoomMemberContent>(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<LocalRoomThirdPartyInviteContent>(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<RoomHistoryVisibilityContent>(realm, event.eventId) ?: return@apply
historyVisibility = content.historyVisibility
}
private fun handleRoomAliasesEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply {
val content = getEventContent<RoomAliasesContent>(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<RoomAvatarContent>(realm, event.eventId) ?: return@apply
avatarUri = content.avatarUrl?.toUri()
}
private fun handleRoomCanonicalAliasEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply {
val content = getEventContent<RoomCanonicalAliasContent>(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<RoomGuestAccessContent>(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<PowerLevelsContent>(realm, event.eventId) ?: return@apply
powerLevelContentOverride = content
}
private fun handleRoomNameEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply {
val content = getEventContent<RoomNameContent>(realm, event.eventId) ?: return@apply
name = content.name
}
private fun handleRoomTopicEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply {
val content = getEventContent<RoomTopicContent>(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<RoomJoinRulesContent>(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 <reified T> getEventContent(realm: Realm, eventId: String): T? {
return EventEntity.where(realm, eventId).findFirst()?.asDomain()?.getClearContent().toModel<T>()
}
/**
* 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
}
}
} }

View file

@ -70,7 +70,6 @@ internal class DefaultCreateRoomTask @Inject constructor(
} }
val createRoomBody = createRoomBodyBuilder.build(params) val createRoomBody = createRoomBodyBuilder.build(params)
val createRoomResponse = try { val createRoomResponse = try {
executeRequest(globalErrorReceiver) { executeRequest(globalErrorReceiver) {
roomAPI.createRoom(createRoomBody) roomAPI.createRoom(createRoomBody)
@ -89,6 +88,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
} }
throw throwable throw throwable
} }
val roomId = createRoomResponse.roomId 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) // 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 { try {

View file

@ -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.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity 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.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.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
@ -70,6 +71,9 @@ internal class DefaultDeleteLocalRoomTask @Inject constructor(
RoomEntity.where(realm, roomId = roomId).findAll() RoomEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - RoomEntity - delete ${it.size} entries") } ?.also { Timber.i("## DeleteLocalRoomTask - RoomEntity - delete ${it.size} entries") }
?.deleteAllFromRealm() ?.deleteAllFromRealm()
LocalRoomSummaryEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - LocalRoomSummaryEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
} }
} else { } else {
Timber.i("## DeleteLocalRoomTask - Failed to remove room with id $roomId: not a local room") Timber.i("## DeleteLocalRoomTask - Failed to remove room with id $roomId: not a local room")

View file

@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.room.membership.threepid
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass 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) @JsonClass(generateAdapter = true)
internal data class ThreePidInviteBody( internal data class ThreePidInviteBody(
@ -43,3 +45,9 @@ internal data class ThreePidInviteBody(
@Json(name = "address") @Json(name = "address")
val address: String val address: String
) )
internal fun ThreePidInviteBody.toThreePid() = when (medium) {
ThreePidMedium.EMAIL -> ThreePid.Email(address)
ThreePidMedium.MSISDN -> ThreePid.Msisdn(address)
else -> null
}