From 4ae04fc29717f1bbbb16e43731de585c4babe110 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 27 Oct 2021 13:06:55 +0100 Subject: [PATCH] adding normalised room display name field and making use of it when filtering rooms by name - fixes non latin-1 character set room names from being ignored when searching with inexact casing --- .../android/sdk/api/query/QueryStringValue.kt | 2 ++ .../database/RealmSessionStoreMigration.kt | 23 +++++++++++++++++-- .../SessionRealmConfigurationFactory.kt | 3 ++- .../database/model/RoomSummaryEntity.kt | 12 ++++++++++ .../room/summary/RoomSummaryDataSource.kt | 6 ++++- .../room/summary/RoomSummaryUpdater.kt | 8 +++++-- .../android/sdk/internal/util/Normalizer.kt | 4 ++-- .../home/room/list/RoomListViewModel.kt | 2 +- 8 files changed, 51 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt index 5141751888..da32d745d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt @@ -39,4 +39,6 @@ sealed interface QueryStringValue { INSENSITIVE, NORMALIZED } + + fun isNormalized() = this is ContentQueryStringValue && case == Case.NORMALIZED } 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 05137f8105..d6e86fd22b 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 @@ -45,11 +45,17 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntityFields import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.query.process +import org.matrix.android.sdk.internal.util.Normalizer import timber.log.Timber +import javax.inject.Inject -internal object RealmSessionStoreMigration : RealmMigration { +internal class RealmSessionStoreMigration @Inject constructor( + private val normalizer: Normalizer +) : RealmMigration { - const val SESSION_STORE_SCHEMA_VERSION = 18L + companion object { + const val SESSION_STORE_SCHEMA_VERSION = 19L + } override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.v("Migrating Realm Session from $oldVersion to $newVersion") @@ -72,6 +78,7 @@ internal object RealmSessionStoreMigration : RealmMigration { if (oldVersion <= 15) migrateTo16(realm) if (oldVersion <= 16) migrateTo17(realm) if (oldVersion <= 17) migrateTo18(realm) + if (oldVersion <= 18) migrateTo19(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -364,4 +371,16 @@ internal object RealmSessionStoreMigration : RealmMigration { realm.schema.get("RoomMemberSummaryEntity") ?.addRealmObjectField(RoomMemberSummaryEntityFields.USER_PRESENCE_ENTITY.`$`, userPresenceEntity) } + + private fun migrateTo19(realm: DynamicRealm) { + Timber.d("Step 18 -> 19") + realm.schema.get("RoomSummaryEntity") + ?.addField(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, String::class.java) + ?.transform { + it.getString(RoomSummaryEntityFields.DISPLAY_NAME)?.let { displayName -> + val normalised = normalizer.normalize(displayName) + it.set(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, normalised) + } + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt index 1771c5b202..04ca26a943 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt @@ -40,6 +40,7 @@ private const val REALM_NAME = "disk_store.realm" */ internal class SessionRealmConfigurationFactory @Inject constructor( private val realmKeysUtils: RealmKeysUtils, + private val realmSessionStoreMigration: RealmSessionStoreMigration, @SessionFilesDirectory val directory: File, @SessionId val sessionId: String, @UserMd5 val userMd5: String, @@ -71,7 +72,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor( .allowWritesOnUiThread(true) .modules(SessionRealmModule()) .schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION) - .migration(RealmSessionStoreMigration) + .migration(realmSessionStoreMigration) .build() // Try creating a realm instance and if it succeeds we can clear the flag diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 88b8886936..23df6d6234 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -40,6 +40,17 @@ internal open class RoomSummaryEntity( set(value) { if (value != field) field = value } + + /** + * Workaround for Realm only supporting Latin-1 character sets when sorting + * or filtering by case + * See https://github.com/realm/realm-core/issues/777 + */ + var normalizedDisplayName: String? = "" + set(value) { + if (value != field) field = value + } + var avatarUrl: String? = "" set(value) { if (value != field) field = value @@ -284,5 +295,6 @@ internal open class RoomSummaryEntity( roomEncryptionTrustLevelStr = value?.name } } + companion object } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt index 8e6d97be80..e86bd27ed2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt @@ -245,7 +245,11 @@ internal class RoomSummaryDataSource @Inject constructor( val query = with(queryStringValueProcessor) { val query = RoomSummaryEntity.where(realm) query.process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId) - query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) + if (queryParams.displayName.isNormalized()) { + query.process(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, queryParams.displayName) + } else { + query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) + } query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias) query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) query.equalTo(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, false) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 30014f4539..03640d0555 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -65,6 +65,7 @@ import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataD import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo +import org.matrix.android.sdk.internal.util.Normalizer import timber.log.Timber import javax.inject.Inject import kotlin.system.measureTimeMillis @@ -75,7 +76,8 @@ internal class RoomSummaryUpdater @Inject constructor( private val roomAvatarResolver: RoomAvatarResolver, private val eventDecryptor: EventDecryptor, private val crossSigningService: DefaultCrossSigningService, - private val roomAccountDataDataSource: RoomAccountDataDataSource) { + private val roomAccountDataDataSource: RoomAccountDataDataSource, + private val normalizer: Normalizer) { fun update(realm: Realm, roomId: String, @@ -136,7 +138,9 @@ internal class RoomSummaryUpdater @Inject constructor( // avoid this call if we are sure there are unread events !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId) - roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId) + val roomDisplayName = roomDisplayNameResolver.resolve(realm, roomId) + roomSummaryEntity.displayName = roomDisplayName + roomSummaryEntity.normalizedDisplayName = normalizer.normalize(roomDisplayName) roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId) roomSummaryEntity.name = ContentMapper.map(lastNameEvent?.content).toModel()?.name roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel()?.topic diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt index 2df1faa784..0e9c885394 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt @@ -1,11 +1,11 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright 2021 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 + * 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, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 89f5aec8fb..b38f2565b6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -191,7 +191,7 @@ class RoomListViewModel @AssistedInject constructor( } updatableQuery?.updateQuery { it.copy( - displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) + displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.NORMALIZED) ) } }