diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt index 3967c15704..cdc8bc1621 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt @@ -16,9 +16,20 @@ package im.vector.matrix.android.api.session.group +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable + /** * This interface defines methods to interact within a group. */ interface Group { val groupId: String + + /** + * This methods allows you to refresh data about this group. It will be reflected on the GroupSummary. + * The SDK also takes care of refreshing group data every hour. + * @param callback : the matrix callback to be notified of success or failure + * @return a Cancelable to be able to cancel requests. + */ + fun fetchGroupData(callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupMapper.kt deleted file mode 100644 index 89ed5844c2..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupMapper.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.matrix.android.internal.database.mapper - -import im.vector.matrix.android.api.session.group.Group -import im.vector.matrix.android.internal.database.model.GroupEntity -import im.vector.matrix.android.internal.session.group.DefaultGroup - -internal object GroupMapper { - - fun map(groupEntity: GroupEntity): Group { - return DefaultGroup( - groupEntity.groupId - ) - } -} - -internal fun GroupEntity.asDomain(): Group { - return GroupMapper.map(this) -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt index eb346a74ca..a0054ae8d6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt @@ -22,8 +22,7 @@ import io.realm.annotations.PrimaryKey /** * This class is used to store group info (groupId and membership) from the sync response. - * Then [im.vector.matrix.android.internal.session.group.GroupSummaryUpdater] observes change and - * makes requests to fetch group information from the homeserver + * Then GetGroupDataTask is called regularly to fetch group information from the homeserver. */ internal open class GroupEntity(@PrimaryKey var groupId: String = "") : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt index 802bfbeae6..1e4f5639c4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.database.query import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.model.GroupEntityFields +import im.vector.matrix.android.internal.query.process import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where @@ -28,10 +29,6 @@ internal fun GroupEntity.Companion.where(realm: Realm, groupId: String): RealmQu .equalTo(GroupEntityFields.GROUP_ID, groupId) } -internal fun GroupEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery { - val query = realm.where() - if (membership != null) { - query.equalTo(GroupEntityFields.MEMBERSHIP_STR, membership.name) - } - return query +internal fun GroupEntity.Companion.where(realm: Realm, memberships: List): RealmQuery { + return realm.where().process(GroupEntityFields.MEMBERSHIP_STR, memberships) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt index 601da098ca..b062793f00 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt @@ -18,8 +18,10 @@ package im.vector.matrix.android.internal.database.query import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields +import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import io.realm.Realm import io.realm.RealmQuery +import io.realm.kotlin.createObject import io.realm.kotlin.where internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupId: String? = null): RealmQuery { @@ -34,3 +36,7 @@ internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupIds: List() .`in`(GroupSummaryEntityFields.GROUP_ID, groupIds.toTypedArray()) } + +internal fun GroupSummaryEntity.Companion.getOrCreate(realm: Realm, groupId: String): GroupSummaryEntity { + return where(realm, groupId).findFirst() ?: realm.createObject(groupId) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/WorkManagerProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/WorkManagerProvider.kt index 5a0202719b..71a3c4bfba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/WorkManagerProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/WorkManagerProvider.kt @@ -21,7 +21,10 @@ import androidx.work.Constraints import androidx.work.ListenableWorker import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager +import java.time.Duration +import java.util.concurrent.TimeUnit import javax.inject.Inject internal class WorkManagerProvider @Inject constructor( @@ -39,6 +42,15 @@ internal class WorkManagerProvider @Inject constructor( OneTimeWorkRequestBuilder() .addTag(tag) + /** + * Create a PeriodicWorkRequestBuilder, with the Matrix SDK tag + */ + inline fun matrixPeriodicWorkRequestBuilder(repeatInterval: Long, + repeatIntervalTimeUnit: TimeUnit) = + PeriodicWorkRequestBuilder(repeatInterval, repeatIntervalTimeUnit) + .addTag(tag) + + /** * Cancel all works instantiated by the Matrix SDK for the current session, and not those from the SDK client, or for other sessions */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 8aabd709ab..0feb944b38 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -68,7 +68,6 @@ import im.vector.matrix.android.internal.network.token.AccessTokenProvider import im.vector.matrix.android.internal.network.token.HomeserverAccessTokenProvider import im.vector.matrix.android.internal.session.call.CallEventProcessor import im.vector.matrix.android.internal.session.download.DownloadProgressInterceptor -import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.homeserver.DefaultHomeServerCapabilitiesService import im.vector.matrix.android.internal.session.identity.DefaultIdentityService import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager @@ -293,10 +292,6 @@ internal abstract class SessionModule { @Binds abstract fun bindNetworkConnectivityChecker(checker: DefaultNetworkConnectivityChecker): NetworkConnectivityChecker - @Binds - @IntoSet - abstract fun bindGroupSummaryUpdater(updater: GroupSummaryUpdater): SessionLifecycleObserver - @Binds @IntoSet abstract fun bindEventRedactionProcessor(processor: RedactionEventProcessor): EventInsertLiveProcessor diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index 7c5de5b137..ee43441453 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -18,7 +18,9 @@ package im.vector.matrix.android.internal.session.group import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntity +import im.vector.matrix.android.internal.database.query.getOrCreate import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.network.executeRequest @@ -28,11 +30,14 @@ import im.vector.matrix.android.internal.session.group.model.GroupUsers import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.awaitTransaction import org.greenrobot.eventbus.EventBus +import timber.log.Timber import javax.inject.Inject internal interface GetGroupDataTask : Task { - - data class Params(val groupId: String) + sealed class Params { + object FetchAllActive : Params() + data class FetchWithIds(val groupIds: List) : Params() + } } internal class DefaultGetGroupDataTask @Inject constructor( @@ -41,44 +46,64 @@ internal class DefaultGetGroupDataTask @Inject constructor( private val eventBus: EventBus ) : GetGroupDataTask { + private data class GroupData( + val groupId: String, + val groupSummary: GroupSummaryResponse, + val groupRooms: GroupRooms, + val groupUsers: GroupUsers + ) + override suspend fun execute(params: GetGroupDataTask.Params) { - val groupId = params.groupId - val groupSummary = executeRequest(eventBus) { - apiCall = groupAPI.getSummary(groupId) + val groupIds = when (params) { + is GetGroupDataTask.Params.FetchAllActive -> { + getActiveGroupIds() + } + is GetGroupDataTask.Params.FetchWithIds -> { + params.groupIds + } } - val groupRooms = executeRequest(eventBus) { - apiCall = groupAPI.getRooms(groupId) + Timber.v("Fetch data for group with ids: ${groupIds.joinToString(";")}") + val data = groupIds.map { groupId -> + val groupSummary = executeRequest(eventBus) { + apiCall = groupAPI.getSummary(groupId) + } + val groupRooms = executeRequest(eventBus) { + apiCall = groupAPI.getRooms(groupId) + } + val groupUsers = executeRequest(eventBus) { + apiCall = groupAPI.getUsers(groupId) + } + GroupData(groupId, groupSummary, groupRooms, groupUsers) } - val groupUsers = executeRequest(eventBus) { - apiCall = groupAPI.getUsers(groupId) - } - insertInDb(groupSummary, groupRooms, groupUsers, groupId) + insertInDb(data) } - private suspend fun insertInDb(groupSummary: GroupSummaryResponse, - groupRooms: GroupRooms, - groupUsers: GroupUsers, - groupId: String) { + private fun getActiveGroupIds(): List { + return monarchy.fetchAllMappedSync( + { realm -> + GroupEntity.where(realm, Membership.activeMemberships()) + }, + { it.groupId } + ) + } + + private suspend fun insertInDb(groupDataList: List) { monarchy .awaitTransaction { realm -> - val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst() - ?: realm.createObject(GroupSummaryEntity::class.java, groupId) + groupDataList.forEach { groupData -> - groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: "" - val name = groupSummary.profile?.name - groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name - groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: "" + val groupSummaryEntity = GroupSummaryEntity.getOrCreate(realm, groupData.groupId) - groupSummaryEntity.roomIds.clear() - groupRooms.rooms.mapTo(groupSummaryEntity.roomIds) { it.roomId } + groupSummaryEntity.avatarUrl = groupData.groupSummary.profile?.avatarUrl ?: "" + val name = groupData.groupSummary.profile?.name + groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupData.groupId else name + groupSummaryEntity.shortDescription = groupData.groupSummary.profile?.shortDescription ?: "" - groupSummaryEntity.userIds.clear() - groupUsers.users.mapTo(groupSummaryEntity.userIds) { it.userId } + groupSummaryEntity.roomIds.clear() + groupData.groupRooms.rooms.mapTo(groupSummaryEntity.roomIds) { it.roomId } - groupSummaryEntity.membership = when (groupSummary.user?.membership) { - Membership.JOIN.value -> Membership.JOIN - Membership.INVITE.value -> Membership.INVITE - else -> Membership.LEAVE + groupSummaryEntity.userIds.clear() + groupData.groupUsers.users.mapTo(groupSummaryEntity.userIds) { it.userId } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt index 6c7b5b2a8b..a9e77a73d0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt @@ -16,6 +16,20 @@ package im.vector.matrix.android.internal.session.group +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.group.Group +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith -internal class DefaultGroup(override val groupId: String) : Group +internal class DefaultGroup(override val groupId: String, + private val taskExecutor: TaskExecutor, + private val getGroupDataTask: GetGroupDataTask) : Group { + + override fun fetchGroupData(callback: MatrixCallback): Cancelable { + val params = GetGroupDataTask.Params.FetchWithIds(listOf(groupId)) + return getGroupDataTask.configureWith(params) { + this.callback = callback + }.executeBy(taskExecutor) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt index af73b896f4..4dd162276f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt @@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields import im.vector.matrix.android.internal.database.query.where @@ -33,10 +34,15 @@ import io.realm.Realm import io.realm.RealmQuery import javax.inject.Inject -internal class DefaultGroupService @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : GroupService { +internal class DefaultGroupService @Inject constructor(@SessionDatabase private val monarchy: Monarchy, + private val groupFactory: GroupFactory) : GroupService { override fun getGroup(groupId: String): Group? { - return null + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + GroupEntity.where(realm, groupId).findFirst()?.let { + groupFactory.create(groupId) + } + } } override fun getGroupSummary(groupId: String): GroupSummary? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index bb33212f9c..f025040c39 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -35,7 +35,6 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - val groupIds: List, override val lastFailureMessage: String? = null ) : SessionWorkerParams @@ -48,14 +47,11 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - val results = params.groupIds.map { groupId -> - runCatching { fetchGroupData(groupId) } - } - val isSuccessful = results.none { it.isFailure } - return if (isSuccessful) Result.success() else Result.retry() - } - - private suspend fun fetchGroupData(groupId: String) { - getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) + return runCatching { + getGroupDataTask.execute(GetGroupDataTask.Params.FetchAllActive) + }.fold( + { Result.success() }, + { Result.retry() } + ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupFactory.kt new file mode 100644 index 0000000000..a5046d45d4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupFactory.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session.group + +import im.vector.matrix.android.api.session.group.Group +import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.task.TaskExecutor +import javax.inject.Inject + +internal interface GroupFactory { + fun create(groupId: String): Group +} + +@SessionScope +internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask, + private val taskExecutor: TaskExecutor) : + GroupFactory { + + override fun create(groupId: String): Group { + return DefaultGroup( + groupId = groupId, + taskExecutor = taskExecutor, + getGroupDataTask = getGroupDataTask + ) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt index b48c6a96e8..bf960f061f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt @@ -21,6 +21,8 @@ import dagger.Module import dagger.Provides import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.session.room.DefaultRoomFactory +import im.vector.matrix.android.internal.session.room.RoomFactory import retrofit2.Retrofit @Module @@ -36,6 +38,9 @@ internal abstract class GroupModule { } } + @Binds + abstract fun bindGroupFactory(factory: DefaultGroupFactory): GroupFactory + @Binds abstract fun bindGetGroupDataTask(task: DefaultGetGroupDataTask): GetGroupDataTask diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt deleted file mode 100644 index 52268ab7de..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.matrix.android.internal.session.group - -import androidx.work.ExistingWorkPolicy -import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.internal.database.RealmLiveEntityObserver -import im.vector.matrix.android.internal.database.awaitTransaction -import im.vector.matrix.android.internal.database.model.GroupEntity -import im.vector.matrix.android.internal.database.model.GroupSummaryEntity -import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.di.SessionDatabase -import im.vector.matrix.android.internal.di.SessionId -import im.vector.matrix.android.internal.di.WorkManagerProvider -import im.vector.matrix.android.internal.worker.WorkerParamsFactory -import io.realm.OrderedCollectionChangeSet -import io.realm.RealmResults -import kotlinx.coroutines.launch -import javax.inject.Inject - -private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" - -internal class GroupSummaryUpdater @Inject constructor( - private val workManagerProvider: WorkManagerProvider, - @SessionId private val sessionId: String, - @SessionDatabase private val monarchy: Monarchy) - : RealmLiveEntityObserver(monarchy.realmConfiguration) { - - override val query = Monarchy.Query { GroupEntity.where(it) } - - override fun onChange(results: RealmResults) { - /* - // `insertions` for new groups and `changes` to handle left groups - val modifiedGroupEntity = (changeSet.insertions + changeSet.changes) - .asSequence() - .mapNotNull { results[it] } - - fetchGroupsData(modifiedGroupEntity - .filter { it.membership == Membership.JOIN || it.membership == Membership.INVITE } - .map { it.groupId } - .toList()) - - modifiedGroupEntity - .filter { it.membership == Membership.LEAVE } - .map { it.groupId } - .toList() - .also { - observerScope.launch { - deleteGroups(it) - } - } - */ - } - - private fun fetchGroupsData(groupIds: List) { - val getGroupDataWorkerParams = GetGroupDataWorker.Params(sessionId, groupIds) - - val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams) - - val getGroupWork = workManagerProvider.matrixOneTimeWorkRequestBuilder() - .setInputData(workData) - .setConstraints(WorkManagerProvider.workConstraints) - .build() - - workManagerProvider.workManager - .beginUniqueWork(GET_GROUP_DATA_WORKER, ExistingWorkPolicy.APPEND, getGroupWork) - .enqueue() - } - - /** - * Delete the GroupSummaryEntity of left groups - */ - private suspend fun deleteGroups(groupIds: List) = awaitTransaction(monarchy.realmConfiguration) { realm -> - GroupSummaryEntity.where(realm, groupIds) - .findAll() - .deleteAllFromRealm() - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt index 392db0a73c..e3d4eae575 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt @@ -19,6 +19,8 @@ package im.vector.matrix.android.internal.session.sync import im.vector.matrix.android.R import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.internal.database.model.GroupEntity +import im.vector.matrix.android.internal.database.model.GroupSummaryEntity +import im.vector.matrix.android.internal.database.query.getOrCreate import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService import im.vector.matrix.android.internal.session.mapWithProgress @@ -64,29 +66,33 @@ internal class GroupSyncHandler @Inject constructor() { handleLeftGroup(realm, it.key) } } - - /** Note: [im.vector.matrix.android.internal.session.group.GroupSummaryUpdater] is observing changes */ realm.insertOrUpdate(groups) } private fun handleJoinedGroup(realm: Realm, groupId: String): GroupEntity { val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + val groupSummaryEntity = GroupSummaryEntity.getOrCreate(realm, groupId) groupEntity.membership = Membership.JOIN + groupSummaryEntity.membership = Membership.JOIN return groupEntity } private fun handleInvitedGroup(realm: Realm, groupId: String): GroupEntity { val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + val groupSummaryEntity = GroupSummaryEntity.getOrCreate(realm, groupId) groupEntity.membership = Membership.INVITE + groupSummaryEntity.membership = Membership.INVITE return groupEntity } private fun handleLeftGroup(realm: Realm, groupId: String): GroupEntity { val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + val groupSummaryEntity = GroupSummaryEntity.getOrCreate(realm, groupId) groupEntity.membership = Membership.LEAVE + groupSummaryEntity.membership = Membership.LEAVE return groupEntity } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt index 5e8ef5a608..0769895d38 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt @@ -16,23 +16,34 @@ package im.vector.matrix.android.internal.session.sync +import androidx.work.ExistingPeriodicWorkPolicy import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.R import im.vector.matrix.android.api.pushrules.PushRuleService import im.vector.matrix.android.api.pushrules.RuleScope import im.vector.matrix.android.internal.crypto.DefaultCryptoService import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.di.SessionId +import im.vector.matrix.android.internal.di.WorkManagerProvider import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService +import im.vector.matrix.android.internal.session.group.GetGroupDataWorker import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask import im.vector.matrix.android.internal.session.reportSubtask +import im.vector.matrix.android.internal.session.sync.model.GroupsSyncResponse import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.util.awaitTransaction +import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber +import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.system.measureTimeMillis +private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" + internal class SyncResponseHandler @Inject constructor(@SessionDatabase private val monarchy: Monarchy, + @SessionId private val sessionId: String, + private val workManagerProvider: WorkManagerProvider, private val roomSyncHandler: RoomSyncHandler, private val userAccountDataSyncHandler: UserAccountDataSyncHandler, private val groupSyncHandler: GroupSyncHandler, @@ -109,10 +120,39 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private checkPushRules(it, isInitialSync) userAccountDataSyncHandler.synchronizeWithServerIfNeeded(it.invite) } + syncResponse.groups?.also { + scheduleGroupDataFetchingIfNeeded(it) + } + Timber.v("On sync completed") cryptoSyncHandler.onSyncCompleted(syncResponse) } + /** + * At the moment we don't get any group data through the sync, so we poll where every hour. + You can also force to refetch group data using [Group] API. + */ + private fun scheduleGroupDataFetchingIfNeeded(groupsSyncResponse: GroupsSyncResponse) { + val groupIds = ArrayList() + groupIds.addAll(groupsSyncResponse.join.keys) + groupIds.addAll(groupsSyncResponse.invite.keys) + if (groupIds.isEmpty()) { + Timber.v("No new groups to fetch data for.") + return + } + Timber.v("There are ${groupIds.size} new groups to fetch data for.") + val getGroupDataWorkerParams = GetGroupDataWorker.Params(sessionId) + val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams) + + val getGroupWork = workManagerProvider.matrixPeriodicWorkRequestBuilder(1, TimeUnit.HOURS) + .setInputData(workData) + .setConstraints(WorkManagerProvider.workConstraints) + .build() + + workManagerProvider.workManager + .enqueueUniquePeriodicWork(GET_GROUP_DATA_WORKER, ExistingPeriodicWorkPolicy.REPLACE, getGroupWork) + } + private suspend fun checkPushRules(roomsSyncResponse: RoomsSyncResponse, isInitialSync: Boolean) { Timber.v("[PushRules] --> checkPushRules") if (isInitialSync) { diff --git a/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt index f14583c5d5..b4a65d67c8 100644 --- a/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt @@ -23,6 +23,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.NoOpMatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.group.groupSummaryQueryParams @@ -88,6 +89,8 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro private fun handleSelectGroup(action: GroupListAction.SelectGroup) = withState { state -> if (state.selectedGroup?.groupId != action.groupSummary.groupId) { + // We take care of refreshing group data when selecting to be sure we get all the rooms and users + session.getGroup(action.groupSummary.groupId)?.fetchGroupData(NoOpMatrixCallback()) setState { copy(selectedGroup = action.groupSummary) } } }