saving sync filter changed (#7627)

This commit is contained in:
Nikita Fedrunov 2022-11-28 09:48:28 +01:00 committed by GitHub
parent 9349b1ae15
commit 5aeca1f81a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 736 additions and 159 deletions

2
changelog.d/7626.sdk Normal file
View file

@ -0,0 +1,2 @@
Sync Filter now taking in account homeserver capabilities to not pass unsupported parameters.
Sync Filter is now configured by providing SyncFilterBuilder class instance, instead of Filter to identify Filter changes related to homeserver capabilities

View file

@ -50,6 +50,7 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@ -346,6 +347,10 @@ class CommonTestHelper internal constructor(context: Context, val cryptoConfig:
assertTrue(registrationResult is RegistrationResult.Success) assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session val session = (registrationResult as RegistrationResult.Success).session
session.open() session.open()
session.filterService().setSyncFilter(
SyncFilterBuilder()
.lazyLoadMembersForStateEvents(true)
)
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session, 120_000) syncSession(session, 120_000)
} }

View file

@ -16,19 +16,12 @@
package org.matrix.android.sdk.api.session.sync package org.matrix.android.sdk.api.session.sync
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
interface FilterService { interface FilterService {
enum class FilterPreset {
NoFilter,
/**
* Filter for Element, will include only known event type.
*/
ElementFilter
}
/** /**
* Configure the filter for the sync. * Configure the filter for the sync.
*/ */
fun setFilter(filterPreset: FilterPreset) suspend fun setSyncFilter(filterBuilder: SyncFilterBuilder)
} }

View file

@ -0,0 +1,129 @@
/*
* 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.api.session.sync.filter
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.session.filter.Filter
import org.matrix.android.sdk.internal.session.filter.RoomEventFilter
import org.matrix.android.sdk.internal.session.filter.RoomFilter
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
class SyncFilterBuilder {
private var lazyLoadMembersForStateEvents: Boolean? = null
private var lazyLoadMembersForMessageEvents: Boolean? = null
private var useThreadNotifications: Boolean? = null
private var listOfSupportedEventTypes: List<String>? = null
private var listOfSupportedStateEventTypes: List<String>? = null
fun lazyLoadMembersForStateEvents(lazyLoadMembersForStateEvents: Boolean) = apply { this.lazyLoadMembersForStateEvents = lazyLoadMembersForStateEvents }
fun lazyLoadMembersForMessageEvents(lazyLoadMembersForMessageEvents: Boolean) =
apply { this.lazyLoadMembersForMessageEvents = lazyLoadMembersForMessageEvents }
fun useThreadNotifications(useThreadNotifications: Boolean) =
apply { this.useThreadNotifications = useThreadNotifications }
fun listOfSupportedStateEventTypes(listOfSupportedStateEventTypes: List<String>) =
apply { this.listOfSupportedStateEventTypes = listOfSupportedStateEventTypes }
fun listOfSupportedTimelineEventTypes(listOfSupportedEventTypes: List<String>) =
apply { this.listOfSupportedEventTypes = listOfSupportedEventTypes }
internal fun with(currentFilterParams: SyncFilterParams?) =
apply {
currentFilterParams?.let {
useThreadNotifications = currentFilterParams.useThreadNotifications
lazyLoadMembersForMessageEvents = currentFilterParams.lazyLoadMembersForMessageEvents
lazyLoadMembersForStateEvents = currentFilterParams.lazyLoadMembersForStateEvents
listOfSupportedEventTypes = currentFilterParams.listOfSupportedEventTypes?.toList()
listOfSupportedStateEventTypes = currentFilterParams.listOfSupportedStateEventTypes?.toList()
}
}
internal fun extractParams(): SyncFilterParams {
return SyncFilterParams(
useThreadNotifications = useThreadNotifications,
lazyLoadMembersForMessageEvents = lazyLoadMembersForMessageEvents,
lazyLoadMembersForStateEvents = lazyLoadMembersForStateEvents,
listOfSupportedEventTypes = listOfSupportedEventTypes,
listOfSupportedStateEventTypes = listOfSupportedStateEventTypes,
)
}
internal fun build(homeServerCapabilities: HomeServerCapabilities): Filter {
return Filter(
room = buildRoomFilter(homeServerCapabilities)
)
}
private fun buildRoomFilter(homeServerCapabilities: HomeServerCapabilities): RoomFilter {
return RoomFilter(
timeline = buildTimelineFilter(homeServerCapabilities),
state = buildStateFilter()
)
}
private fun buildTimelineFilter(homeServerCapabilities: HomeServerCapabilities): RoomEventFilter? {
val resolvedUseThreadNotifications = if (homeServerCapabilities.canUseThreadReadReceiptsAndNotifications) {
useThreadNotifications
} else {
null
}
return RoomEventFilter(
enableUnreadThreadNotifications = resolvedUseThreadNotifications,
lazyLoadMembers = lazyLoadMembersForMessageEvents
).orNullIfEmpty()
}
private fun buildStateFilter(): RoomEventFilter? =
RoomEventFilter(
lazyLoadMembers = lazyLoadMembersForStateEvents,
types = listOfSupportedStateEventTypes
).orNullIfEmpty()
private fun RoomEventFilter.orNullIfEmpty(): RoomEventFilter? {
return if (hasData()) {
this
} else {
null
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as SyncFilterBuilder
if (lazyLoadMembersForStateEvents != other.lazyLoadMembersForStateEvents) return false
if (lazyLoadMembersForMessageEvents != other.lazyLoadMembersForMessageEvents) return false
if (useThreadNotifications != other.useThreadNotifications) return false
if (listOfSupportedEventTypes != other.listOfSupportedEventTypes) return false
if (listOfSupportedStateEventTypes != other.listOfSupportedStateEventTypes) return false
return true
}
override fun hashCode(): Int {
var result = lazyLoadMembersForStateEvents?.hashCode() ?: 0
result = 31 * result + (lazyLoadMembersForMessageEvents?.hashCode() ?: 0)
result = 31 * result + (useThreadNotifications?.hashCode() ?: 0)
result = 31 * result + (listOfSupportedEventTypes?.hashCode() ?: 0)
result = 31 * result + (listOfSupportedStateEventTypes?.hashCode() ?: 0)
return result
}
}

View file

@ -61,6 +61,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo041
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo043 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo043
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo044 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo044
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo045
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
@ -69,7 +70,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer private val normalizer: Normalizer
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Session", dbName = "Session",
schemaVersion = 44L, schemaVersion = 45L,
) { ) {
/** /**
* Forces all RealmSessionStoreMigration instances to be equal. * Forces all RealmSessionStoreMigration instances to be equal.
@ -123,5 +124,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 42) MigrateSessionTo042(realm).perform() if (oldVersion < 42) MigrateSessionTo042(realm).perform()
if (oldVersion < 43) MigrateSessionTo043(realm).perform() if (oldVersion < 43) MigrateSessionTo043(realm).perform()
if (oldVersion < 44) MigrateSessionTo044(realm).perform() if (oldVersion < 44) MigrateSessionTo044(realm).perform()
if (oldVersion < 45) MigrateSessionTo045(realm).perform()
} }
} }

View file

@ -0,0 +1,61 @@
/*
* 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.mapper
import io.realm.RealmList
import org.matrix.android.sdk.internal.database.model.SyncFilterParamsEntity
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
import javax.inject.Inject
internal class FilterParamsMapper @Inject constructor() {
fun map(entity: SyncFilterParamsEntity): SyncFilterParams {
val eventTypes = if (entity.listOfSupportedEventTypesHasBeenSet) {
entity.listOfSupportedEventTypes?.toList()
} else {
null
}
val stateEventTypes = if (entity.listOfSupportedStateEventTypesHasBeenSet) {
entity.listOfSupportedStateEventTypes?.toList()
} else {
null
}
return SyncFilterParams(
useThreadNotifications = entity.useThreadNotifications,
lazyLoadMembersForMessageEvents = entity.lazyLoadMembersForMessageEvents,
lazyLoadMembersForStateEvents = entity.lazyLoadMembersForStateEvents,
listOfSupportedEventTypes = eventTypes,
listOfSupportedStateEventTypes = stateEventTypes,
)
}
fun map(params: SyncFilterParams): SyncFilterParamsEntity {
return SyncFilterParamsEntity(
useThreadNotifications = params.useThreadNotifications,
lazyLoadMembersForMessageEvents = params.lazyLoadMembersForMessageEvents,
lazyLoadMembersForStateEvents = params.lazyLoadMembersForStateEvents,
listOfSupportedEventTypes = params.listOfSupportedEventTypes.toRealmList(),
listOfSupportedEventTypesHasBeenSet = params.listOfSupportedEventTypes != null,
listOfSupportedStateEventTypes = params.listOfSupportedStateEventTypes.toRealmList(),
listOfSupportedStateEventTypesHasBeenSet = params.listOfSupportedStateEventTypes != null,
)
}
private fun List<String>?.toRealmList(): RealmList<String>? {
return this?.toTypedArray()?.let { RealmList(*it) }
}
}

View file

@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.EditionOfEventFields import org.matrix.android.sdk.internal.database.model.EditionOfEventFields
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.query.where
import org.matrix.android.sdk.internal.util.database.RealmMigrator import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo043(realm: DynamicRealm) : RealmMigrator(realm, 43) { internal class MigrateSessionTo043(realm: DynamicRealm) : RealmMigrator(realm, 43) {

View file

@ -0,0 +1,38 @@
/*
* 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.SyncFilterParamsEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo045(realm: DynamicRealm) : RealmMigrator(realm, 45) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.create("SyncFilterParamsEntity")
.addField(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_STATE_EVENTS, Boolean::class.java)
.setNullable(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_STATE_EVENTS, true)
.addField(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_MESSAGE_EVENTS, Boolean::class.java)
.setNullable(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_MESSAGE_EVENTS, true)
.addField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_EVENT_TYPES_HAS_BEEN_SET, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_STATE_EVENT_TYPES_HAS_BEEN_SET, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.USE_THREAD_NOTIFICATIONS, Boolean::class.java)
.setNullable(SyncFilterParamsEntityFields.USE_THREAD_NOTIFICATIONS, true)
.addRealmListField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_EVENT_TYPES.`$`, String::class.java)
.addRealmListField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_STATE_EVENT_TYPES.`$`, String::class.java)
}
}

View file

@ -70,7 +70,8 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
SpaceChildSummaryEntity::class, SpaceChildSummaryEntity::class,
SpaceParentSummaryEntity::class, SpaceParentSummaryEntity::class,
UserPresenceEntity::class, UserPresenceEntity::class,
ThreadSummaryEntity::class ThreadSummaryEntity::class,
SyncFilterParamsEntity::class,
] ]
) )
internal class SessionRealmModule internal class SessionRealmModule

View file

@ -0,0 +1,36 @@
/*
* 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.RealmList
import io.realm.RealmObject
/**
* This entity stores Sync Filter configuration data, provided by the client.
*/
internal open class SyncFilterParamsEntity(
var lazyLoadMembersForStateEvents: Boolean? = null,
var lazyLoadMembersForMessageEvents: Boolean? = null,
var useThreadNotifications: Boolean? = null,
var listOfSupportedEventTypes: RealmList<String>? = null,
var listOfSupportedEventTypesHasBeenSet: Boolean = false,
var listOfSupportedStateEventTypes: RealmList<String>? = null,
var listOfSupportedStateEventTypesHasBeenSet: Boolean = false,
) : RealmObject() {
companion object
}

View file

@ -17,74 +17,71 @@
package org.matrix.android.sdk.internal.session.filter package org.matrix.android.sdk.internal.session.filter
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.kotlin.where import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.mapper.FilterParamsMapper
import org.matrix.android.sdk.internal.database.model.FilterEntity import org.matrix.android.sdk.internal.database.model.FilterEntity
import org.matrix.android.sdk.internal.database.model.FilterEntityFields import org.matrix.android.sdk.internal.database.model.SyncFilterParamsEntity
import org.matrix.android.sdk.internal.database.query.get import org.matrix.android.sdk.internal.database.query.get
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.awaitTransaction
import javax.inject.Inject import javax.inject.Inject
internal class DefaultFilterRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : FilterRepository { internal class DefaultFilterRepository @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
private val filterParamsMapper: FilterParamsMapper
) : FilterRepository {
override suspend fun storeFilter(filter: Filter, roomEventFilter: RoomEventFilter): Boolean { override suspend fun storeSyncFilter(filter: Filter, filterId: String, roomEventFilter: RoomEventFilter) {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> monarchy.awaitTransaction { realm ->
val filterEntity = FilterEntity.get(realm)
// Filter has changed, or no filter Id yet
filterEntity == null ||
filterEntity.filterBodyJson != filter.toJSONString() ||
filterEntity.filterId.isBlank()
}.also { hasChanged ->
if (hasChanged) {
// Filter is new or has changed, store it and reset the filter Id.
// This has to be done outside of the Realm.use(), because awaitTransaction change the current thread
monarchy.awaitTransaction { realm ->
// We manage only one filter for now
val filterJson = filter.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString()
val filterEntity = FilterEntity.getOrCreate(realm)
filterEntity.filterBodyJson = filterJson
filterEntity.roomEventFilterJson = roomEventFilterJson
// Reset filterId
filterEntity.filterId = ""
}
}
}
}
override suspend fun storeFilterId(filter: Filter, filterId: String) {
monarchy.awaitTransaction {
// We manage only one filter for now // We manage only one filter for now
val filterJson = filter.toJSONString() val filterJson = filter.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString()
// Update the filter id, only if the filter body matches val filterEntity = FilterEntity.getOrCreate(realm)
it.where<FilterEntity>()
.equalTo(FilterEntityFields.FILTER_BODY_JSON, filterJson) filterEntity.filterBodyJson = filterJson
?.findFirst() filterEntity.roomEventFilterJson = roomEventFilterJson
?.filterId = filterId filterEntity.filterId = filterId
} }
} }
override suspend fun getFilter(): String { override suspend fun getStoredSyncFilterBody(): String {
return monarchy.awaitTransaction { return monarchy.awaitTransaction {
val filter = FilterEntity.getOrCreate(it) FilterEntity.getOrCreate(it).filterBodyJson
if (filter.filterId.isBlank()) { }
// Use the Json format }
filter.filterBodyJson
override suspend fun getStoredSyncFilterId(): String? {
return monarchy.awaitTransaction {
val id = FilterEntity.get(it)?.filterId
if (id.isNullOrBlank()) {
null
} else { } else {
// Use FilterId id
filter.filterId
} }
} }
} }
override suspend fun getRoomFilter(): String { override suspend fun getRoomFilterBody(): String {
return monarchy.awaitTransaction { return monarchy.awaitTransaction {
FilterEntity.getOrCreate(it).roomEventFilterJson FilterEntity.getOrCreate(it).roomEventFilterJson
} }
} }
override suspend fun getStoredFilterParams(): SyncFilterParams? {
return monarchy.awaitTransaction { realm ->
realm.where<SyncFilterParamsEntity>().findFirst()?.let {
filterParamsMapper.map(it)
}
}
}
override suspend fun storeFilterParams(params: SyncFilterParams) {
return monarchy.awaitTransaction { realm ->
val entity = filterParamsMapper.map(params)
realm.insertOrUpdate(entity)
}
}
} }

View file

@ -17,19 +17,27 @@
package org.matrix.android.sdk.internal.session.filter package org.matrix.android.sdk.internal.session.filter
import org.matrix.android.sdk.api.session.sync.FilterService import org.matrix.android.sdk.api.session.sync.FilterService
import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
import javax.inject.Inject import javax.inject.Inject
internal class DefaultFilterService @Inject constructor( internal class DefaultFilterService @Inject constructor(
private val saveFilterTask: SaveFilterTask, private val saveFilterTask: SaveFilterTask,
private val taskExecutor: TaskExecutor private val filterRepository: FilterRepository,
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource,
) : FilterService { ) : FilterService {
// TODO Pass a list of support events instead // TODO Pass a list of support events instead
override fun setFilter(filterPreset: FilterService.FilterPreset) { override suspend fun setSyncFilter(filterBuilder: SyncFilterBuilder) {
saveFilterTask filterRepository.storeFilterParams(filterBuilder.extractParams())
.configureWith(SaveFilterTask.Params(filterPreset))
.executeBy(taskExecutor) // don't upload/store filter until homeserver capabilities are fetched
homeServerCapabilitiesDataSource.getHomeServerCapabilities()?.let { homeServerCapabilities ->
saveFilterTask.execute(
SaveFilterTask.Params(
filter = filterBuilder.build(homeServerCapabilities)
)
)
}
} }
} }

View file

@ -45,46 +45,7 @@ internal object FilterFactory {
return FilterUtil.enableLazyLoading(Filter(), true) return FilterUtil.enableLazyLoading(Filter(), true)
} }
fun createElementFilter(): Filter {
return Filter(
room = RoomFilter(
timeline = createElementTimelineFilter(),
state = createElementStateFilter()
)
)
}
fun createDefaultRoomFilter(): RoomEventFilter { fun createDefaultRoomFilter(): RoomEventFilter {
return RoomEventFilter(lazyLoadMembers = true) return RoomEventFilter(lazyLoadMembers = true)
} }
fun createElementRoomFilter(): RoomEventFilter {
return RoomEventFilter(
lazyLoadMembers = true,
// TODO Enable this for optimization
// types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList()
)
}
private fun createElementTimelineFilter(): RoomEventFilter? {
// we need to check if homeserver supports thread notifications before setting this param
// return RoomEventFilter(enableUnreadThreadNotifications = true)
return null
}
private fun createElementStateFilter(): RoomEventFilter {
return RoomEventFilter(lazyLoadMembers = true)
}
// Get only managed types by Element
private val listOfSupportedEventTypes = listOf(
// TODO Complete the list
EventType.MESSAGE
)
// Get only managed types by Element
private val listOfSupportedStateEventTypes = listOf(
// TODO Complete the list
EventType.STATE_ROOM_MEMBER
)
} }

View file

@ -44,4 +44,7 @@ internal abstract class FilterModule {
@Binds @Binds
abstract fun bindSaveFilterTask(task: DefaultSaveFilterTask): SaveFilterTask abstract fun bindSaveFilterTask(task: DefaultSaveFilterTask): SaveFilterTask
@Binds
abstract fun bindGetCurrentFilterTask(task: DefaultGetCurrentFilterTask): GetCurrentFilterTask
} }

View file

@ -16,25 +16,42 @@
package org.matrix.android.sdk.internal.session.filter package org.matrix.android.sdk.internal.session.filter
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
/**
* Repository for request filters.
*/
internal interface FilterRepository { internal interface FilterRepository {
/** /**
* Return true if the filterBody has changed, or need to be sent to the server. * Stores sync filter and room filter.
* Note: It looks like we could use [Filter.room.timeline] instead of a separate [RoomEventFilter], but it's not clear if it's safe, so research is needed
* @return true if the filterBody has changed, or need to be sent to the server.
*/ */
suspend fun storeFilter(filter: Filter, roomEventFilter: RoomEventFilter): Boolean suspend fun storeSyncFilter(filter: Filter, filterId: String, roomEventFilter: RoomEventFilter)
/** /**
* Set the filterId of this filter. * Returns stored sync filter's JSON body if it exists.
*/ */
suspend fun storeFilterId(filter: Filter, filterId: String) suspend fun getStoredSyncFilterBody(): String?
/** /**
* Return filter json or filter id. * Returns stored sync filter's ID if it exists.
*/ */
suspend fun getFilter(): String suspend fun getStoredSyncFilterId(): String?
/** /**
* Return the room filter. * Return the room filter.
*/ */
suspend fun getRoomFilter(): String suspend fun getRoomFilterBody(): String
/**
* Returns filter params stored in local storage if it exists.
*/
suspend fun getStoredFilterParams(): SyncFilterParams?
/**
* Stores filter params to local storage.
*/
suspend fun storeFilterParams(params: SyncFilterParams)
} }

View file

@ -0,0 +1,55 @@
/*
* 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.session.filter
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface GetCurrentFilterTask : Task<Unit, String>
internal class DefaultGetCurrentFilterTask @Inject constructor(
private val filterRepository: FilterRepository,
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource,
private val saveFilterTask: SaveFilterTask
) : GetCurrentFilterTask {
override suspend fun execute(params: Unit): String {
val storedFilterId = filterRepository.getStoredSyncFilterId()
val storedFilterBody = filterRepository.getStoredSyncFilterBody()
val homeServerCapabilities = homeServerCapabilitiesDataSource.getHomeServerCapabilities() ?: HomeServerCapabilities()
val currentFilter = SyncFilterBuilder()
.with(filterRepository.getStoredFilterParams())
.build(homeServerCapabilities)
val currentFilterBody = currentFilter.toJSONString()
return when (storedFilterBody) {
currentFilterBody -> storedFilterId ?: storedFilterBody
else -> saveFilter(currentFilter)
}
}
private suspend fun saveFilter(filter: Filter) = saveFilterTask
.execute(
SaveFilterTask.Params(
filter = filter
)
)
}

View file

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session.filter package org.matrix.android.sdk.internal.session.filter
import org.matrix.android.sdk.api.session.sync.FilterService
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
@ -26,10 +25,10 @@ import javax.inject.Inject
/** /**
* Save a filter, in db and if any changes, upload to the server. * Save a filter, in db and if any changes, upload to the server.
*/ */
internal interface SaveFilterTask : Task<SaveFilterTask.Params, Unit> { internal interface SaveFilterTask : Task<SaveFilterTask.Params, String> {
data class Params( data class Params(
val filterPreset: FilterService.FilterPreset val filter: Filter
) )
} }
@ -37,33 +36,21 @@ internal class DefaultSaveFilterTask @Inject constructor(
@UserId private val userId: String, @UserId private val userId: String,
private val filterAPI: FilterApi, private val filterAPI: FilterApi,
private val filterRepository: FilterRepository, private val filterRepository: FilterRepository,
private val globalErrorReceiver: GlobalErrorReceiver private val globalErrorReceiver: GlobalErrorReceiver,
) : SaveFilterTask { ) : SaveFilterTask {
override suspend fun execute(params: SaveFilterTask.Params) { override suspend fun execute(params: SaveFilterTask.Params): String {
val filterBody = when (params.filterPreset) { val filter = params.filter
FilterService.FilterPreset.ElementFilter -> { val filterResponse = executeRequest(globalErrorReceiver) {
FilterFactory.createElementFilter() // TODO auto retry
} filterAPI.uploadFilter(userId, filter)
FilterService.FilterPreset.NoFilter -> {
FilterFactory.createDefaultFilter()
}
}
val roomFilter = when (params.filterPreset) {
FilterService.FilterPreset.ElementFilter -> {
FilterFactory.createElementRoomFilter()
}
FilterService.FilterPreset.NoFilter -> {
FilterFactory.createDefaultRoomFilter()
}
}
val updated = filterRepository.storeFilter(filterBody, roomFilter)
if (updated) {
val filterResponse = executeRequest(globalErrorReceiver) {
// TODO auto retry
filterAPI.uploadFilter(userId, filterBody)
}
filterRepository.storeFilterId(filterBody, filterResponse.filterId)
} }
filterRepository.storeSyncFilter(
filter = filter,
filterId = filterResponse.filterId,
roomEventFilter = FilterFactory.createDefaultRoomFilter()
)
return filterResponse.filterId
} }
} }

View file

@ -47,7 +47,7 @@ internal class DefaultFetchTokenAndPaginateTask @Inject constructor(
) : FetchTokenAndPaginateTask { ) : FetchTokenAndPaginateTask {
override suspend fun execute(params: FetchTokenAndPaginateTask.Params): TokenChunkEventPersistor.Result { override suspend fun execute(params: FetchTokenAndPaginateTask.Params): TokenChunkEventPersistor.Result {
val filter = filterRepository.getRoomFilter() val filter = filterRepository.getRoomFilterBody()
val response = executeRequest(globalErrorReceiver) { val response = executeRequest(globalErrorReceiver) {
roomAPI.getContextOfEvent(params.roomId, params.lastKnownEventId, 0, filter) roomAPI.getContextOfEvent(params.roomId, params.lastKnownEventId, 0, filter)
} }

View file

@ -39,7 +39,7 @@ internal class DefaultGetContextOfEventTask @Inject constructor(
) : GetContextOfEventTask { ) : GetContextOfEventTask {
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result { override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
val filter = filterRepository.getRoomFilter() val filter = filterRepository.getRoomFilterBody()
val response = executeRequest(globalErrorReceiver) { val response = executeRequest(globalErrorReceiver) {
// We are limiting the response to the event with eventId to be sure we don't have any issue with potential merging process. // We are limiting the response to the event with eventId to be sure we don't have any issue with potential merging process.
roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter) roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter)

View file

@ -41,7 +41,7 @@ internal class DefaultPaginationTask @Inject constructor(
) : PaginationTask { ) : PaginationTask {
override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result { override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
val filter = filterRepository.getRoomFilter() val filter = filterRepository.getRoomFilterBody()
val chunk = executeRequest( val chunk = executeRequest(
globalErrorReceiver, globalErrorReceiver,
canRetry = true canRetry = true

View file

@ -36,7 +36,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.network.toFailure import org.matrix.android.sdk.internal.network.toFailure
import org.matrix.android.sdk.internal.session.SessionListeners import org.matrix.android.sdk.internal.session.SessionListeners
import org.matrix.android.sdk.internal.session.dispatchTo import org.matrix.android.sdk.internal.session.dispatchTo
import org.matrix.android.sdk.internal.session.filter.FilterRepository import org.matrix.android.sdk.internal.session.filter.GetCurrentFilterTask
import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask
import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser
import org.matrix.android.sdk.internal.session.user.UserStore import org.matrix.android.sdk.internal.session.user.UserStore
@ -64,11 +64,9 @@ internal interface SyncTask : Task<SyncTask.Params, SyncResponse> {
internal class DefaultSyncTask @Inject constructor( internal class DefaultSyncTask @Inject constructor(
private val syncAPI: SyncAPI, private val syncAPI: SyncAPI,
@UserId private val userId: String, @UserId private val userId: String,
private val filterRepository: FilterRepository,
private val syncResponseHandler: SyncResponseHandler, private val syncResponseHandler: SyncResponseHandler,
private val syncRequestStateTracker: SyncRequestStateTracker, private val syncRequestStateTracker: SyncRequestStateTracker,
private val syncTokenStore: SyncTokenStore, private val syncTokenStore: SyncTokenStore,
private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask,
private val userStore: UserStore, private val userStore: UserStore,
private val session: Session, private val session: Session,
private val sessionListeners: SessionListeners, private val sessionListeners: SessionListeners,
@ -79,6 +77,8 @@ internal class DefaultSyncTask @Inject constructor(
private val syncResponseParser: InitialSyncResponseParser, private val syncResponseParser: InitialSyncResponseParser,
private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore, private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore,
private val clock: Clock, private val clock: Clock,
private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask,
private val getCurrentFilterTask: GetCurrentFilterTask
) : SyncTask { ) : SyncTask {
private val workingDir = File(fileDirectory, "is") private val workingDir = File(fileDirectory, "is")
@ -100,8 +100,13 @@ internal class DefaultSyncTask @Inject constructor(
requestParams["since"] = token requestParams["since"] = token
timeout = params.timeout timeout = params.timeout
} }
// Maybe refresh the homeserver capabilities data we know
getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false))
val filter = getCurrentFilterTask.execute(Unit)
requestParams["timeout"] = timeout.toString() requestParams["timeout"] = timeout.toString()
requestParams["filter"] = filterRepository.getFilter() requestParams["filter"] = filter
params.presence?.let { requestParams["set_presence"] = it.value } params.presence?.let { requestParams["set_presence"] = it.value }
val isInitialSync = token == null val isInitialSync = token == null
@ -115,8 +120,6 @@ internal class DefaultSyncTask @Inject constructor(
) )
syncRequestStateTracker.startRoot(InitialSyncStep.ImportingAccount, 100) syncRequestStateTracker.startRoot(InitialSyncStep.ImportingAccount, 100)
} }
// Maybe refresh the homeserver capabilities data we know
getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false))
val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)

View file

@ -0,0 +1,25 @@
/*
* 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.sync.filter
internal data class SyncFilterParams(
val lazyLoadMembersForStateEvents: Boolean? = null,
val lazyLoadMembersForMessageEvents: Boolean? = null,
val useThreadNotifications: Boolean? = null,
val listOfSupportedEventTypes: List<String>? = null,
val listOfSupportedStateEventTypes: List<String>? = null,
)

View file

@ -0,0 +1,100 @@
/*
* 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.sync
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
import org.matrix.android.sdk.internal.session.filter.DefaultGetCurrentFilterTask
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
import org.matrix.android.sdk.test.fakes.FakeFilterRepository
import org.matrix.android.sdk.test.fakes.FakeHomeServerCapabilitiesDataSource
import org.matrix.android.sdk.test.fakes.FakeSaveFilterTask
private const val A_FILTER_ID = "filter-id"
private val A_HOMESERVER_CAPABILITIES = HomeServerCapabilities()
private val A_SYNC_FILTER_PARAMS = SyncFilterParams(
lazyLoadMembersForMessageEvents = true,
lazyLoadMembersForStateEvents = true,
useThreadNotifications = true
)
@ExperimentalCoroutinesApi
class DefaultGetCurrentFilterTaskTest {
private val filterRepository = FakeFilterRepository()
private val homeServerCapabilitiesDataSource = FakeHomeServerCapabilitiesDataSource()
private val saveFilterTask = FakeSaveFilterTask()
private val getCurrentFilterTask = DefaultGetCurrentFilterTask(
filterRepository = filterRepository,
homeServerCapabilitiesDataSource = homeServerCapabilitiesDataSource.instance,
saveFilterTask = saveFilterTask
)
@Test
fun `given no filter is stored, when execute, then executes task to save new filter`() = runTest {
filterRepository.givenFilterParamsAreStored(A_SYNC_FILTER_PARAMS)
homeServerCapabilitiesDataSource.givenHomeServerCapabilities(A_HOMESERVER_CAPABILITIES)
filterRepository.givenFilterStored(null, null)
getCurrentFilterTask.execute(Unit)
val filter = SyncFilterBuilder()
.with(A_SYNC_FILTER_PARAMS)
.build(A_HOMESERVER_CAPABILITIES)
saveFilterTask.verifyExecution(filter)
}
@Test
fun `given filter is stored and didn't change, when execute, then returns stored filter id`() = runTest {
filterRepository.givenFilterParamsAreStored(A_SYNC_FILTER_PARAMS)
homeServerCapabilitiesDataSource.givenHomeServerCapabilities(A_HOMESERVER_CAPABILITIES)
val filter = SyncFilterBuilder().with(A_SYNC_FILTER_PARAMS).build(A_HOMESERVER_CAPABILITIES)
filterRepository.givenFilterStored(A_FILTER_ID, filter.toJSONString())
val result = getCurrentFilterTask.execute(Unit)
result shouldBeEqualTo A_FILTER_ID
}
@Test
fun `given filter is set and home server capabilities has changed, when execute, then executes task to save new filter`() = runTest {
filterRepository.givenFilterParamsAreStored(A_SYNC_FILTER_PARAMS)
homeServerCapabilitiesDataSource.givenHomeServerCapabilities(A_HOMESERVER_CAPABILITIES)
val filter = SyncFilterBuilder().with(A_SYNC_FILTER_PARAMS).build(A_HOMESERVER_CAPABILITIES)
filterRepository.givenFilterStored(A_FILTER_ID, filter.toJSONString())
val newHomeServerCapabilities = HomeServerCapabilities(canUseThreadReadReceiptsAndNotifications = true)
homeServerCapabilitiesDataSource.givenHomeServerCapabilities(newHomeServerCapabilities)
val newFilter = SyncFilterBuilder().with(A_SYNC_FILTER_PARAMS).build(newHomeServerCapabilities)
getCurrentFilterTask.execute(Unit)
saveFilterTask.verifyExecution(newFilter)
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.test.fakes
import io.mockk.coEvery
import io.mockk.mockk
import org.matrix.android.sdk.internal.session.filter.FilterRepository
import org.matrix.android.sdk.internal.sync.filter.SyncFilterParams
internal class FakeFilterRepository : FilterRepository by mockk() {
fun givenFilterStored(filterId: String?, filterBody: String?) {
coEvery { getStoredSyncFilterId() } returns filterId
coEvery { getStoredSyncFilterBody() } returns filterBody
}
fun givenFilterParamsAreStored(syncFilterParams: SyncFilterParams?) {
coEvery { getStoredFilterParams() } returns syncFilterParams
}
}

View file

@ -0,0 +1,30 @@
/*
* 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.test.fakes
import io.mockk.every
import io.mockk.mockk
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
internal class FakeHomeServerCapabilitiesDataSource {
val instance = mockk<HomeServerCapabilitiesDataSource>()
fun givenHomeServerCapabilities(homeServerCapabilities: HomeServerCapabilities) {
every { instance.getHomeServerCapabilities() } returns homeServerCapabilities
}
}

View file

@ -0,0 +1,40 @@
/*
* 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.test.fakes
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.slot
import org.amshove.kluent.shouldBeEqualTo
import org.matrix.android.sdk.internal.session.filter.Filter
import org.matrix.android.sdk.internal.session.filter.SaveFilterTask
import java.util.UUID
internal class FakeSaveFilterTask : SaveFilterTask by mockk() {
init {
coEvery { execute(any()) } returns UUID.randomUUID().toString()
}
fun verifyExecution(filter: Filter) {
val slot = slot<SaveFilterTask.Params>()
coVerify { execute(capture(slot)) }
val params = slot.captured
params.filter shouldBeEqualTo filter
}
}

View file

@ -24,9 +24,9 @@ import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.sync.SyncUtils
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.sync.FilterService
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -41,7 +41,9 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
fun execute(session: Session, startSyncing: Boolean = true) { fun execute(session: Session, startSyncing: Boolean = true) {
Timber.i("Configure and start session for ${session.myUserId}. startSyncing: $startSyncing") Timber.i("Configure and start session for ${session.myUserId}. startSyncing: $startSyncing")
session.open() session.open()
session.filterService().setFilter(FilterService.FilterPreset.ElementFilter) session.coroutineScope.launch {
session.filterService().setSyncFilter(SyncUtils.getSyncFilterBuilder())
}
if (startSyncing) { if (startSyncing) {
session.startSyncing(context) session.startSyncing(context)
} }

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022 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.app.features.sync
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
object SyncUtils {
// Get only managed types by Element
private val listOfSupportedTimelineEventTypes = listOf(
// TODO Complete the list
EventType.MESSAGE
)
// Get only managed types by Element
private val listOfSupportedStateEventTypes = listOf(
// TODO Complete the list
EventType.STATE_ROOM_MEMBER
)
fun getSyncFilterBuilder(): SyncFilterBuilder {
return SyncFilterBuilder()
.useThreadNotifications(true)
.lazyLoadMembersForStateEvents(true)
/**
* Currently we don't set [lazy_load_members = true] for Filter.room.timeline even though we set it for RoomFilter which is used later to
* fetch messages in a room. It's not clear if it's done so by mistake or intentionally, so changing it could case side effects and need
* careful testing
* */
// .lazyLoadMembersForMessageEvents(true)
// .listOfSupportedStateEventTypes(listOfSupportedStateEventTypes)
// .listOfSupportedTimelineEventTypes(listOfSupportedTimelineEventTypes)
}
}

View file

@ -19,6 +19,7 @@ package im.vector.app.core.session
import im.vector.app.core.extensions.startSyncing import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.sync.SyncUtils
import im.vector.app.test.fakes.FakeContext import im.vector.app.test.fakes.FakeContext
import im.vector.app.test.fakes.FakeEnableNotificationsSettingUpdater import im.vector.app.test.fakes.FakeEnableNotificationsSettingUpdater
import im.vector.app.test.fakes.FakeSession import im.vector.app.test.fakes.FakeSession
@ -38,7 +39,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.session.sync.FilterService
class ConfigureAndStartSessionUseCaseTest { class ConfigureAndStartSessionUseCaseTest {
@ -83,7 +83,7 @@ class ConfigureAndStartSessionUseCaseTest {
// Then // Then
verify { fakeSession.startSyncing(fakeContext.instance) } verify { fakeSession.startSyncing(fakeContext.instance) }
fakeSession.fakeFilterService.verifySetFilter(FilterService.FilterPreset.ElementFilter) fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder())
fakeSession.fakePushersService.verifyRefreshPushers() fakeSession.fakePushersService.verifyRefreshPushers()
fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded()
coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) }
@ -105,7 +105,7 @@ class ConfigureAndStartSessionUseCaseTest {
// Then // Then
verify { fakeSession.startSyncing(fakeContext.instance) } verify { fakeSession.startSyncing(fakeContext.instance) }
fakeSession.fakeFilterService.verifySetFilter(FilterService.FilterPreset.ElementFilter) fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder())
fakeSession.fakePushersService.verifyRefreshPushers() fakeSession.fakePushersService.verifyRefreshPushers()
fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded()
coVerify(inverse = true) { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } coVerify(inverse = true) { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) }
@ -127,7 +127,7 @@ class ConfigureAndStartSessionUseCaseTest {
// Then // Then
verify(inverse = true) { fakeSession.startSyncing(fakeContext.instance) } verify(inverse = true) { fakeSession.startSyncing(fakeContext.instance) }
fakeSession.fakeFilterService.verifySetFilter(FilterService.FilterPreset.ElementFilter) fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder())
fakeSession.fakePushersService.verifyRefreshPushers() fakeSession.fakePushersService.verifyRefreshPushers()
fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded()
coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) }

View file

@ -16,20 +16,21 @@
package im.vector.app.test.fakes package im.vector.app.test.fakes
import io.mockk.every import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.runs import io.mockk.runs
import io.mockk.verify
import org.matrix.android.sdk.api.session.sync.FilterService import org.matrix.android.sdk.api.session.sync.FilterService
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterBuilder
class FakeFilterService : FilterService by mockk() { class FakeFilterService : FilterService by mockk() {
fun givenSetFilterSucceeds() { fun givenSetFilterSucceeds() {
every { setFilter(any()) } just runs coEvery { setSyncFilter(any()) } just runs
} }
fun verifySetFilter(filterPreset: FilterService.FilterPreset) { fun verifySetSyncFilter(filterBuilder: SyncFilterBuilder) {
verify { setFilter(filterPreset) } coVerify { setSyncFilter(filterBuilder) }
} }
} }