From a3539153ef9eaf14c7a42067fc6bfb9e5850cb21 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Nov 2018 17:39:07 +0100 Subject: [PATCH] Quick sync group management (WIP) --- .idea/caches/build_file_checksums.ser | Bin 661 -> 659 bytes .../java/im/vector/matrix/rx/RxSession.kt | 5 ++ .../matrix/android/api/session/Session.kt | 3 +- .../matrix/android/api/session/group/Group.kt | 5 ++ .../android/api/session/group/GroupService.kt | 11 +++ .../api/session/group/model/GroupSummary.kt | 8 +++ .../internal/database/mapper/GroupMapper.kt | 19 +++++ .../database/mapper/GroupSummaryMapper.kt | 21 ++++++ .../internal/database/model/GroupEntity.kt | 22 ++++++ .../database/model/GroupSummaryEntity.kt | 14 ++++ .../database/query/GroupEntityQueries.kt | 20 ++++++ .../query/GroupSummaryEntityQueries.kt | 16 +++++ .../rest/model/group/GroupsSyncResponse.java | 1 - .../internal/session/DefaultSession.kt | 24 ++++++- .../android/internal/session/SessionModule.kt | 11 +++ .../internal/session/group/DefaultGroup.kt | 7 ++ .../session/group/DefaultGroupService.kt | 26 +++++++ .../session/group/GetGroupSummaryRequest.kt | 68 ++++++++++++++++++ .../internal/session/group/GroupAPI.kt | 21 ++++++ .../internal/session/group/GroupModule.kt | 23 ++++++ .../session/group/GroupSummaryUpdater.kt | 63 ++++++++++++++++ .../session/group/model/GroupProfile.kt | 33 +++++++++ .../group/model/GroupSummaryResponse.kt | 30 ++++++++ .../group/model/GroupSummaryRoomsSection.kt | 16 +++++ .../session/group/model/GroupSummaryUser.kt | 21 ++++++ .../group/model/GroupSummaryUsersSection.kt | 20 ++++++ .../room/timeline/DefaultTimelineHolder.kt | 2 +- .../internal/session/sync/GroupSyncHandler.kt | 66 +++++++++++++++++ .../internal/session/sync/SyncModule.kt | 6 +- .../session/sync/SyncResponseHandler.kt | 6 +- 30 files changed, 582 insertions(+), 6 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/GroupService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupMapper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupProfile.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryRoomsSection.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUser.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUsersSection.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index de32038819dde67045bd9c7d21ccdc5e3240e1d4..d04576cac8db44fa39154a4e05bc03b178281c5c 100644 GIT binary patch delta 65 zcmV-H0KWf~1(OAkm;~fW+!>Lahz=(&RdZ!>b1!FMZf0^}lLZ1clOzGZ6Mxpspp`7!v>h diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index 0361860c..a9889c8e 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -1,6 +1,7 @@ package im.vector.matrix.rx import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import io.reactivex.Observable @@ -10,6 +11,10 @@ class RxSession(private val session: Session) { return session.liveRoomSummaries().asObservable() } + fun liveGroupSummaries(): Observable> { + return session.liveGroupSummaries().asObservable() + } + } fun Session.rx(): RxSession { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 38210279..c06e76ea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -1,10 +1,11 @@ package im.vector.matrix.android.api.session import android.support.annotation.MainThread +import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.auth.data.SessionParams -interface Session : RoomService { +interface Session : RoomService, GroupService { val sessionParams: SessionParams 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 new file mode 100644 index 00000000..7657173c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/Group.kt @@ -0,0 +1,5 @@ +package im.vector.matrix.android.api.session.group + +interface Group { + val groupId: String +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/GroupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/GroupService.kt new file mode 100644 index 00000000..769d354e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/GroupService.kt @@ -0,0 +1,11 @@ +package im.vector.matrix.android.api.session.group + +import android.arch.lifecycle.LiveData +import im.vector.matrix.android.api.session.group.model.GroupSummary + +interface GroupService { + + fun getGroup(groupId: String): Group? + + fun liveGroupSummaries(): LiveData> +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt new file mode 100644 index 00000000..115dbb07 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/group/model/GroupSummary.kt @@ -0,0 +1,8 @@ +package im.vector.matrix.android.api.session.group.model + +data class GroupSummary( + val groupId: String, + val displayName: String = "", + val shortDescription: String = "", + val avatarUrl: String = "" +) \ No newline at end of file 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 new file mode 100644 index 00000000..9587d3d4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupMapper.kt @@ -0,0 +1,19 @@ +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 + + +object GroupMapper { + + internal fun map(groupEntity: GroupEntity): Group { + return DefaultGroup( + groupEntity.groupId + ) + } +} + +fun GroupEntity.asDomain(): Group { + return GroupMapper.map(this) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt new file mode 100644 index 00000000..46241950 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/GroupSummaryMapper.kt @@ -0,0 +1,21 @@ +package im.vector.matrix.android.internal.database.mapper + +import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.matrix.android.internal.database.model.GroupSummaryEntity + + +object GroupSummaryMapper { + + internal fun map(roomSummaryEntity: GroupSummaryEntity): GroupSummary { + return GroupSummary( + roomSummaryEntity.groupId, + roomSummaryEntity.displayName, + roomSummaryEntity.shortDescription, + roomSummaryEntity.avatarUrl + ) + } +} + +fun GroupSummaryEntity.asDomain(): GroupSummary { + return GroupSummaryMapper.map(this) +} \ No newline at end of file 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 new file mode 100644 index 00000000..d88d5ac7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupEntity.kt @@ -0,0 +1,22 @@ +package im.vector.matrix.android.internal.database.model + +import im.vector.matrix.android.api.session.room.model.MyMembership +import io.realm.RealmObject +import io.realm.annotations.Ignore +import io.realm.annotations.PrimaryKey +import kotlin.properties.Delegates + +open class GroupEntity(@PrimaryKey var groupId: String = "" + +) : RealmObject() { + + private var membershipStr: String = MyMembership.NONE.name + + @delegate:Ignore + var membership: MyMembership by Delegates.observable(MyMembership.valueOf(membershipStr)) { _, _, newValue -> + membershipStr = newValue.name + } + + companion object + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt new file mode 100644 index 00000000..45eb0e1c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/GroupSummaryEntity.kt @@ -0,0 +1,14 @@ +package im.vector.matrix.android.internal.database.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +open class GroupSummaryEntity(@PrimaryKey var groupId: String = "", + var displayName: String = "", + var shortDescription: String = "", + var avatarUrl: String = "" +) : RealmObject() { + + companion object + +} \ No newline at end of file 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 new file mode 100644 index 00000000..4d6df4cb --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupEntityQueries.kt @@ -0,0 +1,20 @@ +package im.vector.matrix.android.internal.database.query + +import im.vector.matrix.android.api.session.room.model.MyMembership +import im.vector.matrix.android.internal.database.model.GroupEntity +import im.vector.matrix.android.internal.database.model.GroupEntityFields +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where + +fun GroupEntity.Companion.where(realm: Realm, roomId: String): RealmQuery { + return realm.where().equalTo(GroupEntityFields.GROUP_ID, roomId) +} + +fun GroupEntity.Companion.where(realm: Realm, membership: MyMembership? = null): RealmQuery { + val query = realm.where() + if (membership != null) { + query.equalTo(GroupEntityFields.MEMBERSHIP_STR, membership.name) + } + return query +} 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 new file mode 100644 index 00000000..41a4690b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/GroupSummaryEntityQueries.kt @@ -0,0 +1,16 @@ +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 io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where + +fun GroupSummaryEntity.Companion.where(realm: Realm, groupId: String? = null): RealmQuery { + val query = realm.where() + if (groupId != null) { + query.equalTo(GroupSummaryEntityFields.GROUP_ID, groupId) + } + return query +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/rest/model/group/GroupsSyncResponse.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/rest/model/group/GroupsSyncResponse.java index 424aa30f..60970bb0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/rest/model/group/GroupsSyncResponse.java +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/rest/model/group/GroupsSyncResponse.java @@ -16,7 +16,6 @@ */ package im.vector.matrix.android.internal.legacy.rest.model.group; -import java.io.Serializable; import java.util.Map; /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index f4cc538a..9a894a3b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -4,10 +4,15 @@ import android.arch.lifecycle.LiveData import android.os.Looper import android.support.annotation.MainThread import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.group.Group +import im.vector.matrix.android.api.session.group.GroupService +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.internal.auth.data.SessionParams +import im.vector.matrix.android.internal.session.group.GroupModule +import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.RoomModule import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.sync.SyncModule @@ -28,7 +33,9 @@ class DefaultSession(override val sessionParams: SessionParams) : Session, KoinC private lateinit var scope: Scope private val roomSummaryObserver by inject() + private val groupSummaryUpdater by inject() private val roomService by inject() + private val groupService by inject() private val syncThread by inject() private var isOpen = false @@ -40,9 +47,11 @@ class DefaultSession(override val sessionParams: SessionParams) : Session, KoinC val sessionModule = SessionModule(sessionParams) val syncModule = SyncModule() val roomModule = RoomModule() - StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule, roomModule)) + val groupModule = GroupModule() + StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule, roomModule, groupModule)) scope = getKoin().getOrCreateScope(SCOPE) roomSummaryObserver.start() + groupSummaryUpdater.start() syncThread.start() } @@ -52,6 +61,7 @@ class DefaultSession(override val sessionParams: SessionParams) : Session, KoinC checkIsMainThread() assert(isOpen) syncThread.kill() + groupSummaryUpdater.dispose() roomSummaryObserver.dispose() scope.close() isOpen = false @@ -89,6 +99,18 @@ class DefaultSession(override val sessionParams: SessionParams) : Session, KoinC roomService.saveLastSelectedRoom(roomSummary) } + // GROUP SERVICE + + override fun getGroup(groupId: String): Group? { + assert(isOpen) + return groupService.getGroup(groupId) + } + + override fun liveGroupSummaries(): LiveData> { + assert(isOpen) + return groupService.liveGroupSummaries() + } + // Private methods ***************************************************************************** private fun checkIsMainThread() { 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 afb84d76..08a9f25d 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 @@ -1,8 +1,11 @@ package im.vector.matrix.android.internal.session import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.auth.data.SessionParams +import im.vector.matrix.android.internal.session.group.DefaultGroupService +import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.DefaultRoomService import im.vector.matrix.android.internal.session.room.RoomAvatarResolver import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater @@ -58,6 +61,14 @@ class SessionModule(private val sessionParams: SessionParams) : Module { DefaultRoomService(get()) as RoomService } + scope(DefaultSession.SCOPE) { + GroupSummaryUpdater(get(), get()) + } + + scope(DefaultSession.SCOPE) { + DefaultGroupService(get()) as GroupService + } + }.invoke() 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 new file mode 100644 index 00000000..c49d828d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroup.kt @@ -0,0 +1,7 @@ +package im.vector.matrix.android.internal.session.group + +import im.vector.matrix.android.api.session.group.Group + +class DefaultGroup(override val groupId: String) : Group { + +} \ No newline at end of file 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 new file mode 100644 index 00000000..bae90048 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt @@ -0,0 +1,26 @@ +package im.vector.matrix.android.internal.session.group + +import android.arch.lifecycle.LiveData +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.group.Group +import im.vector.matrix.android.api.session.group.GroupService +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.GroupSummaryEntity +import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields +import im.vector.matrix.android.internal.database.query.where + +class DefaultGroupService(private val monarchy: Monarchy) : GroupService { + + override fun getGroup(groupId: String): Group? { + return null + } + + override fun liveGroupSummaries(): LiveData> { + return monarchy.findAllMappedWithChanges( + { realm -> GroupSummaryEntity.where(realm).isNotEmpty(GroupSummaryEntityFields.DISPLAY_NAME) }, + { it.asDomain() } + ) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt new file mode 100644 index 00000000..4d73e733 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupSummaryRequest.kt @@ -0,0 +1,68 @@ +package im.vector.matrix.android.internal.session.group + +import arrow.core.Either +import arrow.core.flatMap +import arrow.core.leftIfNull +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.database.model.GroupSummaryEntity +import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse +import im.vector.matrix.android.internal.util.CancelableCoroutine +import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import io.realm.kotlin.createObject +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class GetGroupSummaryRequest( + private val groupAPI: GroupAPI, + private val monarchy: Monarchy, + private val coroutineDispatchers: MatrixCoroutineDispatchers +) { + + fun execute(groupId: String, + callback: MatrixCallback + ): Cancelable { + val job = GlobalScope.launch(coroutineDispatchers.main) { + val groupOrFailure = execute(groupId) + groupOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) }) + } + return CancelableCoroutine(job) + } + + private suspend fun execute(groupId: String) = withContext(coroutineDispatchers.io) { + + return@withContext executeRequest { + apiCall = groupAPI.getSummary(groupId) + }.leftIfNull { + Failure.Unknown(RuntimeException("GroupSummary shouldn't be null")) + }.flatMap { groupSummary -> + try { + insertInDb(groupSummary, groupId) + Either.right(groupSummary) + } catch (exception: Exception) { + Either.Left(Failure.Unknown(exception)) + } + } + } + + private fun insertInDb(groupSummary: GroupSummaryResponse, groupId: String) { + monarchy.runTransactionSync { realm -> + val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst() + ?: realm.createObject(groupId) + + groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: "" + val name = groupSummary.profile?.name + groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name + groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: "" + + + } + } + + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt new file mode 100644 index 00000000..1fe5bc45 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupAPI.kt @@ -0,0 +1,21 @@ +package im.vector.matrix.android.internal.session.group + +import im.vector.matrix.android.internal.network.NetworkConstants +import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse +import kotlinx.coroutines.Deferred +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Path + +interface GroupAPI { + + /** + * Request a group summary + * + * @param groupId the group id + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "groups/{groupId}/summary") + fun getSummary(@Path("groupId") groupId: String): Deferred> + + +} \ No newline at end of file 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 new file mode 100644 index 00000000..9334a267 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt @@ -0,0 +1,23 @@ +package im.vector.matrix.android.internal.session.group + +import im.vector.matrix.android.internal.session.DefaultSession +import org.koin.dsl.context.ModuleDefinition +import org.koin.dsl.module.Module +import org.koin.dsl.module.module +import retrofit2.Retrofit + +class GroupModule : Module { + + override fun invoke(): ModuleDefinition = module(override = true) { + + scope(DefaultSession.SCOPE) { + val retrofit: Retrofit = get() + retrofit.create(GroupAPI::class.java) + } + + scope(DefaultSession.SCOPE) { + GetGroupSummaryRequest(get(), get(), get()) + } + + }.invoke() +} \ No newline at end of file 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 new file mode 100644 index 00000000..74b0bb5e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt @@ -0,0 +1,63 @@ +package im.vector.matrix.android.internal.session.group + +import android.arch.lifecycle.Observer +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.group.Group +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.query.where +import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse +import timber.log.Timber +import java.util.concurrent.atomic.AtomicBoolean + +internal class GroupSummaryUpdater(private val monarchy: Monarchy, + private val getGroupSummaryRequest: GetGroupSummaryRequest +) : Observer> { + + private var isStarted = AtomicBoolean(false) + private val liveResults = monarchy.findAllManagedWithChanges { GroupEntity.where(it) } + + fun start() { + if (isStarted.compareAndSet(false, true)) { + liveResults.observeForever(this) + } + } + + fun dispose() { + if (isStarted.compareAndSet(true, false)) { + liveResults.removeObserver(this) + } + } + + // PRIVATE + + override fun onChanged(changeSet: Monarchy.ManagedChangeSet?) { + if (changeSet == null) { + return + } + val groups = changeSet.realmResults.map { it.asDomain() } + val indexesToUpdate = changeSet.orderedCollectionChangeSet.changes + changeSet.orderedCollectionChangeSet.insertions + updateGroupList(groups, indexesToUpdate) + } + + + private fun updateGroupList(groups: List, indexes: IntArray) { + indexes.forEach { + val group = groups[it] + try { + updateGroup(group) + } catch (e: Exception) { + Timber.e(e, "An error occured when updating room summaries") + } + } + } + + private fun updateGroup(group: Group?) { + if (group == null) { + return + } + getGroupSummaryRequest.execute(group.groupId, object : MatrixCallback {}) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupProfile.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupProfile.kt new file mode 100644 index 00000000..315ea07f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupProfile.kt @@ -0,0 +1,33 @@ +package im.vector.matrix.android.internal.session.group.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents a community profile in the server responses. + */ +@JsonClass(generateAdapter = true) +data class GroupProfile( + + @Json(name = "short_description") val shortDescription: String? = null, + + /** + * Tell whether the group is public. + */ + @Json(name = "is_public") val isPublic: Boolean? = null, + + /** + * The URL for the group's avatar. May be nil. + */ + @Json(name = "avatar_url") val avatarUrl: String? = null, + + /** + * The group's name. + */ + @Json(name = "name") val name: String? = null, + + /** + * The optional HTML formatted string used to described the group. + */ + @Json(name = "long_description") val longDescription: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryResponse.kt new file mode 100644 index 00000000..e135983e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryResponse.kt @@ -0,0 +1,30 @@ +package im.vector.matrix.android.internal.session.group.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the summary of a community in the server response. + */ +@JsonClass(generateAdapter = true) +data class GroupSummaryResponse( + /** + * The group profile. + */ + @Json(name = "profile") val profile: GroupProfile? = null, + + /** + * The group users. + */ + @Json(name = "users_section") val usersSection: GroupSummaryUsersSection? = null, + + /** + * The current user status. + */ + @Json(name = "user") val user: GroupSummaryUser? = null, + + /** + * The rooms linked to the community. + */ + @Json(name = "rooms_section") val roomsSection: GroupSummaryRoomsSection? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryRoomsSection.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryRoomsSection.kt new file mode 100644 index 00000000..6800dfc0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryRoomsSection.kt @@ -0,0 +1,16 @@ +package im.vector.matrix.android.internal.session.group.model + +import com.squareup.moshi.Json + +/** + * This class represents the community rooms in a group summary response. + */ +data class GroupSummaryRoomsSection( + + @Json(name = "total_room_count_estimate") val totalRoomCountEstimate: Int? = null, + + @Json(name = "rooms") val rooms: List = emptyList() + + // @TODO: Check the meaning and the usage of these categories. This dictionary is empty FTM. + //public Map categories; +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUser.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUser.kt new file mode 100644 index 00000000..9398014d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUser.kt @@ -0,0 +1,21 @@ +package im.vector.matrix.android.internal.session.group.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the current user status in a group summary response. + */ +@JsonClass(generateAdapter = true) +data class GroupSummaryUser( + + /** + * The current user membership in this community. + */ + @Json(name = "membership") val membership: String? = null, + + /** + * Tell whether the user published this community on his profile. + */ + @Json(name = "is_publicised") val isPublicised: Boolean? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUsersSection.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUsersSection.kt new file mode 100644 index 00000000..2e10f204 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/model/GroupSummaryUsersSection.kt @@ -0,0 +1,20 @@ +package im.vector.matrix.android.internal.session.group.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + + +/** + * This class represents the community members in a group summary response. + */ + +@JsonClass(generateAdapter = true) +data class GroupSummaryUsersSection( + + @Json(name = "total_user_count_estimate") val totalUserCountEstimate: Int, + + @Json(name = "users") val users: List = emptyList() + + // @TODO: Check the meaning and the usage of these roles. This dictionary is empty FTM. + //public Map roles; +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt index b76ec47f..ee4157e7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt @@ -55,7 +55,7 @@ class DefaultTimelineHolder(private val roomId: String, .setEnablePlaceholders(false) .setPageSize(PAGE_SIZE) .setInitialLoadSizeHint(PAGE_SIZE) - .setPrefetchDistance(10) + .setPrefetchDistance(20) .build() val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback) 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 new file mode 100644 index 00000000..bf3163a3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt @@ -0,0 +1,66 @@ +package im.vector.matrix.android.internal.session.sync + +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.room.model.MyMembership +import im.vector.matrix.android.internal.database.model.GroupEntity +import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.legacy.rest.model.group.GroupsSyncResponse +import im.vector.matrix.android.internal.legacy.rest.model.group.InvitedGroupSync +import io.realm.Realm + + +internal class GroupSyncHandler(private val monarchy: Monarchy) { + + sealed class HandlingStrategy { + data class JOINED(val data: Map) : HandlingStrategy() + data class INVITED(val data: Map) : HandlingStrategy() + data class LEFT(val data: Map) : HandlingStrategy() + } + + fun handle(roomsSyncResponse: GroupsSyncResponse) { + monarchy.runTransactionSync { realm -> + handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.JOINED(roomsSyncResponse.join)) + handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.INVITED(roomsSyncResponse.invite)) + handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.LEFT(roomsSyncResponse.leave)) + } + } + + // PRIVATE METHODS ***************************************************************************** + + private fun handleGroupSync(realm: Realm, handlingStrategy: HandlingStrategy) { + val groups = when (handlingStrategy) { + is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedGroup(realm, it.key) } + is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedGroup(realm, it.key) } + is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftGroup(realm, it.key) } + } + realm.insertOrUpdate(groups) + } + + private fun handleJoinedGroup(realm: Realm, + groupId: String): GroupEntity { + + val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + groupEntity.membership = MyMembership.JOINED + return groupEntity + } + + private fun handleInvitedGroup(realm: Realm, + groupId: String): GroupEntity { + + val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + groupEntity.membership = MyMembership.INVITED + return groupEntity + + } + + // TODO : handle it + private fun handleLeftGroup(realm: Realm, + groupId: String): GroupEntity { + + val groupEntity = GroupEntity.where(realm, groupId).findFirst() ?: GroupEntity(groupId) + groupEntity.membership = MyMembership.LEFT + return groupEntity + } + + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt index 204bb8d0..b8cdf8b4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt @@ -29,12 +29,16 @@ class SyncModule : Module { RoomSyncHandler(get(), get(), get()) } + scope(DefaultSession.SCOPE) { + GroupSyncHandler(get()) + } + scope(DefaultSession.SCOPE) { UserAccountDataSyncHandler(get()) } scope(DefaultSession.SCOPE) { - SyncResponseHandler(get(), get()) + SyncResponseHandler(get(), get(), get()) } scope(DefaultSession.SCOPE) { 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 c86e8b6e..a41ec41f 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 @@ -4,7 +4,8 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse import timber.log.Timber internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, - private val userAccountDataSyncHandler: UserAccountDataSyncHandler) { + private val userAccountDataSyncHandler: UserAccountDataSyncHandler, + private val groupSyncHandler: GroupSyncHandler) { fun handleResponse(syncResponse: SyncResponse?, fromToken: String?, isCatchingUp: Boolean) { if (syncResponse == null) { @@ -14,6 +15,9 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, if (syncResponse.rooms != null) { roomSyncHandler.handle(syncResponse.rooms) } + if (syncResponse.groups != null) { + groupSyncHandler.handle(syncResponse.groups) + } if (syncResponse.accountData != null) { userAccountDataSyncHandler.handle(syncResponse.accountData) }