From 9ab59a543d15483d19a1b0720e7a9e4e2b5c1a4d Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 17 Sep 2021 17:14:11 +0300 Subject: [PATCH] * Implement Presence Service: - Get Presence Status - Set Presence Status * Integrate presence in room details screen * Integrate presence in room people's view * Update UI to support presence * Fix bug when insertOrUpdate was called on RoomMemberEventHandler and override the correct presence value in RoomMemberSummaryEntity * Improve performance on updateUserPresence in RoomMemberSummaryEntity entity * Remarks & linter fixes * Disable presence when there is no m.presence events. In some servers like matrix.org is disabled atm. * Enhance UI Presence on DM room lists to support dark/light theme * Restore missing lines in gradle.properties to speed up debugging --- gradle.properties | 3 + .../ui-styles/src/main/res/values/colors.xml | 4 ++ .../src/main/res/values/theme_dark.xml | 3 + .../src/main/res/values/theme_light.xml | 3 + .../matrix/android/sdk/api/session/Session.kt | 2 + .../sdk/api/session/events/model/Event.kt | 5 ++ .../api/session/presence/PresenceService.kt | 41 +++++++++++++ .../session/room/model/RoomMemberSummary.kt | 3 + .../sdk/api/session/room/model/RoomSummary.kt | 2 + .../database/RealmSessionStoreMigration.kt | 27 ++++++++- .../mapper/RoomMemberSummaryMapper.kt | 2 + .../database/mapper/RoomSummaryMapper.kt | 2 + .../database/model/RoomMemberSummaryEntity.kt | 7 +++ .../database/model/RoomSummaryEntity.kt | 6 ++ .../database/model/SessionRealmModule.kt | 4 +- .../model/presence/UserPresenceEntity.kt | 49 +++++++++++++++ .../database/query/RoomMemberEntityQueries.kt | 11 ++++ .../query/RoomSummaryEntityQueries.kt | 9 +++ .../query/UserPresenceEntityQueries.kt | 29 +++++++++ .../sdk/internal/session/DefaultSession.kt | 3 + .../sdk/internal/session/SessionComponent.kt | 4 +- .../internal/session/presence/PresenceAPI.kt | 43 +++++++++++++ .../session/presence/di/PresenceModule.kt | 53 ++++++++++++++++ .../presence/model/GetPresenceResponse.kt | 32 ++++++++++ .../session/presence/model/PresenceContent.kt | 32 ++++++++++ .../session/presence/model/PresenceEnum.kt | 41 +++++++++++++ .../session/presence/model/SetPresenceBody.kt | 27 +++++++++ .../session/presence/model/UserPresence.kt | 23 +++++++ .../service/DefaultPresenceService.kt | 36 +++++++++++ .../presence/service/task/GetPresenceTask.kt | 42 +++++++++++++ .../presence/service/task/SetPresenceTask.kt | 47 +++++++++++++++ .../membership/RoomMemberEntityFactory.kt | 4 +- .../room/membership/RoomMemberEventHandler.kt | 21 ++++++- .../session/room/read/SetReadMarkersTask.kt | 6 +- .../session/room/timeline/DefaultTimeline.kt | 2 +- .../room/timeline/DefaultTimelineService.kt | 2 +- .../session/sync/SyncResponseHandler.kt | 17 +++++- .../SyncResponsePostTreatmentAggregator.kt | 4 +- .../sync/{ => handler}/CryptoSyncHandler.kt | 2 +- .../sync/{ => handler}/GroupSyncHandler.kt | 2 +- .../sync/handler/PresenceSyncHandler.kt | 60 +++++++++++++++++++ ...cResponsePostTreatmentAggregatorHandler.kt | 8 ++- .../UserAccountDataSyncHandler.kt | 2 +- .../{ => handler/room}/ReadReceiptHandler.kt | 4 +- .../room}/RoomFullyReadHandler.kt | 2 +- .../{ => handler/room}/RoomSyncHandler.kt | 5 +- .../sync/{ => handler/room}/RoomTagHandler.kt | 2 +- .../room}/RoomTypingUsersHandler.kt | 2 +- .../parsing/RoomSyncAccountDataHandler.kt | 4 +- .../DefaultSessionAccountDataService.kt | 6 +- .../core/epoxy/profiles/ProfileMatrixItem.kt | 2 + .../profiles/ProfileMatrixItemWithPresence.kt | 44 ++++++++++++++ .../core/ui/views/PresenceStateImageView.kt | 54 +++++++++++++++++ .../home/room/detail/RoomDetailFragment.kt | 2 +- .../home/room/list/RoomSummaryItem.kt | 6 ++ .../home/room/list/RoomSummaryItemFactory.kt | 2 + .../roomprofile/RoomProfileFragment.kt | 1 + .../members/RoomMemberListController.kt | 44 +++++++++++--- .../main/res/drawable/ic_presence_offline.xml | 24 ++++++++ .../main/res/drawable/ic_presence_online.xml | 24 ++++++++ .../main/res/layout/fragment_room_detail.xml | 34 ++++++++--- .../res/layout/item_profile_matrix_item.xml | 30 ++++++++-- vector/src/main/res/layout/item_room.xml | 15 +++++ .../view_stub_room_member_profile_header.xml | 31 ++++++++-- .../layout/view_stub_room_profile_header.xml | 32 +++++++--- vector/src/main/res/values/strings.xml | 3 + 66 files changed, 1031 insertions(+), 67 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/presence/PresenceService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/presence/UserPresenceEntity.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserPresenceEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/PresenceAPI.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/di/PresenceModule.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/GetPresenceResponse.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceContent.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceEnum.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/SetPresenceBody.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/UserPresence.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/DefaultPresenceService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/GetPresenceTask.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/SetPresenceTask.kt rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler}/CryptoSyncHandler.kt (98%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler}/GroupSyncHandler.kt (98%) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/PresenceSyncHandler.kt rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler}/SyncResponsePostTreatmentAggregatorHandler.kt (90%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler}/UserAccountDataSyncHandler.kt (99%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler/room}/ReadReceiptHandler.kt (96%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler/room}/RoomFullyReadHandler.kt (95%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler/room}/RoomSyncHandler.kt (98%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler/room}/RoomTagHandler.kt (95%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/{ => handler/room}/RoomTypingUsersHandler.kt (96%) create mode 100644 vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPresence.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/views/PresenceStateImageView.kt create mode 100644 vector/src/main/res/drawable/ic_presence_offline.xml create mode 100644 vector/src/main/res/drawable/ic_presence_online.xml diff --git a/gradle.properties b/gradle.properties index 98d561815b..23538c5285 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,3 +23,6 @@ vector.debugPrivateData=false # httpLogLevel values: NONE, BASIC, HEADERS, BODY vector.httpLogLevel=BASIC +# Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above +#vector.debugPrivateData=true +#vector.httpLogLevel=BODY \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml index b58829bb81..03d1ff69db 100644 --- a/library/ui-styles/src/main/res/values/colors.xml +++ b/library/ui-styles/src/main/res/values/colors.xml @@ -133,4 +133,8 @@ @color/palette_black_900 @color/palette_gray_400 + + + @color/palette_gray_100 + @color/palette_gray_450 diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml index f83953a527..d07e3c5297 100644 --- a/library/ui-styles/src/main/res/values/theme_dark.xml +++ b/library/ui-styles/src/main/res/values/theme_dark.xml @@ -42,6 +42,9 @@ @android:color/black #FFFFFFFF + + @color/vctr_presence_indicator_offline_dark + ?vctr_system ?vctr_content_quinary diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml index cd5e17d607..14ec372f28 100644 --- a/library/ui-styles/src/main/res/values/theme_light.xml +++ b/library/ui-styles/src/main/res/values/theme_light.xml @@ -42,6 +42,9 @@ #FFEEEEEE #FF000000 + + @color/vctr_presence_indicator_offline_light + ?vctr_system ?vctr_content_quinary diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index bde68da9d7..67c7cfa383 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS import org.matrix.android.sdk.api.session.media.MediaService import org.matrix.android.sdk.api.session.openid.OpenIdService import org.matrix.android.sdk.api.session.permalinks.PermalinkService +import org.matrix.android.sdk.api.session.presence.PresenceService import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.pushers.PushersService import org.matrix.android.sdk.api.session.room.RoomDirectoryService @@ -75,6 +76,7 @@ interface Session : TermsService, EventService, ProfileService, + PresenceService, PushRuleService, PushersService, SyncStatusService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 96b44ce8c9..169f90dbca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import org.matrix.android.sdk.internal.di.MoshiProvider +import org.matrix.android.sdk.internal.session.presence.model.PresenceContent import timber.log.Timber typealias Content = JsonDict @@ -305,3 +306,7 @@ fun Event.isReply(): Boolean { fun Event.isEdition(): Boolean { return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null } + +fun Event.getPresenceContent(): PresenceContent? { + return content.toModel() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/presence/PresenceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/presence/PresenceService.kt new file mode 100644 index 0000000000..2316eec3b7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/presence/PresenceService.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence + +import org.matrix.android.sdk.internal.session.presence.model.GetPresenceResponse +import org.matrix.android.sdk.internal.session.presence.model.PresenceEnum + +/** + * This interface defines methods for handling user presence information. + */ + +interface PresenceService { + + /** + * Update the presence status for the current user + * @param presence the new presence state + * @param message the status message to attach to this state + */ + suspend fun setMyPresence(presence: PresenceEnum, message: String? = null) + + /** + * Fetch the given user's presence state. + * @param userId the userId whose presence state to get. + */ + suspend fun fetchPresence(userId: String): GetPresenceResponse +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomMemberSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomMemberSummary.kt index fba3a1dd71..87036dfdd5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomMemberSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomMemberSummary.kt @@ -16,12 +16,15 @@ package org.matrix.android.sdk.api.session.room.model +import org.matrix.android.sdk.internal.session.presence.model.UserPresence + /** * Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content */ data class RoomMemberSummary constructor( val membership: Membership, val userId: String, + val userPresence: UserPresence? = null, val displayName: String? = null, val avatarUrl: String? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt index cae4775e71..d43eefbebe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.internal.session.presence.model.UserPresence /** * This class holds some data of a room. @@ -38,6 +39,7 @@ data class RoomSummary( val joinRules: RoomJoinRules? = null, val isDirect: Boolean = false, val directUserId: String? = null, + val directUserPresence: UserPresence? = null, val joinedMembersCount: Int? = 0, val invitedMembersCount: Int? = 0, val latestPreviewableEvent: TimelineEvent? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index aa96ca5e1a..f082f19368 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -35,19 +35,21 @@ import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityField import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields import org.matrix.android.sdk.internal.database.model.RoomEntityFields +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntityFields import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntityFields import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntityFields import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.query.process import timber.log.Timber internal object RealmSessionStoreMigration : RealmMigration { - const val SESSION_STORE_SCHEMA_VERSION = 17L + const val SESSION_STORE_SCHEMA_VERSION = 18L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.v("Migrating Realm Session from $oldVersion to $newVersion") @@ -69,6 +71,7 @@ internal object RealmSessionStoreMigration : RealmMigration { if (oldVersion <= 14) migrateTo15(realm) if (oldVersion <= 15) migrateTo16(realm) if (oldVersion <= 16) migrateTo17(realm) + if (oldVersion <= 17) migrateTo18(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -338,4 +341,26 @@ internal object RealmSessionStoreMigration : RealmMigration { realm.schema.get("EventInsertEntity") ?.addField(EventInsertEntityFields.CAN_BE_PROCESSED, Boolean::class.java) } + + private fun migrateTo18(realm: DynamicRealm) { + Timber.d("Step 17 -> 18") + realm.schema.create("UserPresenceEntity") + ?.addField(UserPresenceEntityFields.USER_ID, String::class.java) + ?.addPrimaryKey(UserPresenceEntityFields.USER_ID) + ?.setRequired(UserPresenceEntityFields.USER_ID, true) + ?.addField(UserPresenceEntityFields.PRESENCE_STR, String::class.java) + ?.addField(UserPresenceEntityFields.LAST_ACTIVE_AGO, Long::class.java) + ?.setNullable(UserPresenceEntityFields.LAST_ACTIVE_AGO, true) + ?.addField(UserPresenceEntityFields.STATUS_MESSAGE, String::class.java) + ?.addField(UserPresenceEntityFields.IS_CURRENTLY_ACTIVE, Boolean::class.java) + ?.setNullable(UserPresenceEntityFields.IS_CURRENTLY_ACTIVE, true) + ?.addField(UserPresenceEntityFields.AVATAR_URL, String::class.java) + + val userPresenceEntity = realm.schema.get("UserPresenceEntity") ?: return + realm.schema.get("RoomSummaryEntity") + ?.addRealmObjectField(RoomSummaryEntityFields.DIRECT_USER_PRESENCE.`$`, userPresenceEntity) + + realm.schema.get("RoomMemberSummaryEntity") + ?.addRealmObjectField(RoomMemberSummaryEntityFields.USER_PRESENCE_ENTITY.`$`, userPresenceEntity) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomMemberSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomMemberSummaryMapper.kt index 2365a39567..efd9b68011 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomMemberSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomMemberSummaryMapper.kt @@ -18,12 +18,14 @@ package org.matrix.android.sdk.internal.database.mapper import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.presence.toUserPresence internal object RoomMemberSummaryMapper { fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary { return RoomMemberSummary( userId = roomMemberSummaryEntity.userId, + userPresence = roomMemberSummaryEntity.userPresenceEntity?.toUserPresence(), avatarUrl = roomMemberSummaryEntity.avatarUrl, displayName = roomMemberSummaryEntity.displayName, membership = roomMemberSummaryEntity.membership diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 0cf431c340..5900ef6b76 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.presence.toUserPresence import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker import javax.inject.Inject @@ -48,6 +49,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa joinRules = roomSummaryEntity.joinRules, isDirect = roomSummaryEntity.isDirect, directUserId = roomSummaryEntity.directUserId, + directUserPresence = roomSummaryEntity.directUserPresence?.toUserPresence(), latestPreviewableEvent = latestEvent, joinedMembersCount = roomSummaryEntity.joinedMembersCount, invitedMembersCount = roomSummaryEntity.invitedMembersCount, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt index 75771ff12c..a3a38aafca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt @@ -21,6 +21,7 @@ import io.realm.annotations.Index import io.realm.annotations.PrimaryKey import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.util.MatrixItem +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "", @Index var userId: String = "", @@ -40,6 +41,12 @@ internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = membershipStr = value.name } + var userPresenceEntity: UserPresenceEntity? = null + set(value) { + if (value != field) field = value + } + + fun getBestName() = displayName?.takeIf { it.isNotBlank() } ?: userId fun toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 64dc08e827..88b8886936 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity internal open class RoomSummaryEntity( @PrimaryKey var roomId: String = "", @@ -204,6 +205,11 @@ internal open class RoomSummaryEntity( if (value != field) field = value } + var directUserPresence: UserPresenceEntity? = null + set(value) { + if (value != field) field = value + } + var hasFailedSending: Boolean = false set(value) { if (value != field) field = value diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt index 19472e21d9..c090777972 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.database.model import io.realm.annotations.RealmModule +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity /** * Realm module for Session @@ -64,6 +65,7 @@ import io.realm.annotations.RealmModule WellknownIntegrationManagerConfigEntity::class, RoomAccountDataEntity::class, SpaceChildSummaryEntity::class, - SpaceParentSummaryEntity::class + SpaceParentSummaryEntity::class, + UserPresenceEntity::class ]) internal class SessionRealmModule diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/presence/UserPresenceEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/presence/UserPresenceEntity.kt new file mode 100644 index 0000000000..64a2cdb6dc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/presence/UserPresenceEntity.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.internal.session.presence.model.PresenceEnum +import org.matrix.android.sdk.internal.session.presence.model.UserPresence + +internal open class UserPresenceEntity(@PrimaryKey var userId: String = "", + var lastActiveAgo: Long? = null, + var statusMessage: String? = null, + var isCurrentlyActive: Boolean? = null, + var avatarUrl: String? = null +) : RealmObject() { + + var presence: PresenceEnum + get() { + return PresenceEnum.valueOf(presenceStr) + } + set(value) { + presenceStr = value.name + } + + private var presenceStr: String = PresenceEnum.UNAVAILABLE.name + + companion object +} + +internal fun UserPresenceEntity.toUserPresence() = + UserPresence( + lastActiveAgo, + statusMessage, + isCurrentlyActive, + presence) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt index a19a9cf725..1ea06b9dfb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt @@ -21,6 +21,7 @@ import io.realm.RealmQuery import io.realm.kotlin.where import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery { val query = realm @@ -32,3 +33,13 @@ internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: Strin } return query } + +internal fun RoomMemberSummaryEntity.Companion.updateUserPresence(realm: Realm, userId: String, userPresenceEntity: UserPresenceEntity) { + realm.where() + .equalTo(RoomMemberSummaryEntityFields.USER_ID, userId) + .isNull(RoomMemberSummaryEntityFields.USER_PRESENCE_ENTITY.`$`) + .findAll() + .map { + it.userPresenceEntity = userPresenceEntity + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt index 5294f849af..d1b05a4932 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt @@ -23,6 +23,7 @@ import io.realm.kotlin.createObject import io.realm.kotlin.where import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery { val query = realm.where() @@ -67,3 +68,11 @@ internal fun RoomSummaryEntity.Companion.isDirect(realm: Realm, roomId: String): .findAll() .isNotEmpty() } + +internal fun RoomSummaryEntity.Companion.updateDirectUserPresence(realm: Realm, directUserId: String, userPresenceEntity: UserPresenceEntity) { + RoomSummaryEntity.where(realm) + .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) + .equalTo(RoomSummaryEntityFields.DIRECT_USER_ID, directUserId) + .findFirst() + ?.directUserPresence = userPresenceEntity +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserPresenceEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserPresenceEntityQueries.kt new file mode 100644 index 0000000000..22790b6f4f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserPresenceEntityQueries.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database.query + +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntityFields + +internal fun UserPresenceEntity.Companion.where(realm: Realm, userId: String): RealmQuery { + return realm + .where() + .equalTo(UserPresenceEntityFields.USER_ID, userId) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index d41bf8a702..5d3579cc8b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -45,6 +45,7 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS import org.matrix.android.sdk.api.session.media.MediaService import org.matrix.android.sdk.api.session.openid.OpenIdService import org.matrix.android.sdk.api.session.permalinks.PermalinkService +import org.matrix.android.sdk.api.session.presence.PresenceService import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.pushers.PushersService import org.matrix.android.sdk.api.session.room.RoomDirectoryService @@ -127,6 +128,7 @@ internal class DefaultSession @Inject constructor( private val callSignalingService: Lazy, private val spaceService: Lazy, private val openIdService: Lazy, + private val presenceService: Lazy, @UnauthenticatedWithCertificate private val unauthenticatedWithCertificateOkHttpClient: Lazy ) : Session, @@ -145,6 +147,7 @@ internal class DefaultSession @Inject constructor( SecureStorageService by secureStorageService.get(), HomeServerCapabilitiesService by homeServerCapabilitiesService.get(), ProfileService by profileService.get(), + PresenceService by presenceService.get(), AccountService by accountService.get() { override val sharedSecretStorageService: SharedSecretStorageService diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 71031a4614..568703b9cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -42,6 +42,7 @@ import org.matrix.android.sdk.internal.session.identity.IdentityModule import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerModule import org.matrix.android.sdk.internal.session.media.MediaModule import org.matrix.android.sdk.internal.session.openid.OpenIdModule +import org.matrix.android.sdk.internal.session.presence.di.PresenceModule import org.matrix.android.sdk.internal.session.profile.ProfileModule import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker import org.matrix.android.sdk.internal.session.pushers.PushersModule @@ -94,7 +95,8 @@ import org.matrix.android.sdk.internal.util.system.SystemModule CallModule::class, SearchModule::class, ThirdPartyModule::class, - SpaceModule::class + SpaceModule::class, + PresenceModule::class ] ) @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/PresenceAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/PresenceAPI.kt new file mode 100644 index 0000000000..53d0d5e963 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/PresenceAPI.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence + +import org.matrix.android.sdk.internal.network.NetworkConstants +import org.matrix.android.sdk.internal.session.presence.model.GetPresenceResponse +import org.matrix.android.sdk.internal.session.presence.model.SetPresenceBody +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Path + +internal interface PresenceAPI { + + /** + * Set the presence status of the current user + * Ref: https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-presence-userid-status + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "presence/{userId}/status") + suspend fun setPresence(@Path("userId") userId: String, + @Body body: SetPresenceBody) + + /** + * Get the given user's presence state. + * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-presence-userid-status + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "presence/{userId}/status") + suspend fun getPresence(@Path("userId") userId: String): GetPresenceResponse +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/di/PresenceModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/di/PresenceModule.kt new file mode 100644 index 0000000000..6b2ee76046 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/di/PresenceModule.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.di + +import dagger.Binds +import dagger.Module +import dagger.Provides +import org.matrix.android.sdk.api.session.presence.PresenceService +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.session.presence.PresenceAPI +import org.matrix.android.sdk.internal.session.presence.service.DefaultPresenceService +import org.matrix.android.sdk.internal.session.presence.service.task.DefaultGetPresenceTask +import org.matrix.android.sdk.internal.session.presence.service.task.DefaultSetPresenceTask +import org.matrix.android.sdk.internal.session.presence.service.task.GetPresenceTask +import org.matrix.android.sdk.internal.session.presence.service.task.SetPresenceTask +import retrofit2.Retrofit + +@Module +internal abstract class PresenceModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesPresenceAPI(retrofit: Retrofit): PresenceAPI { + return retrofit.create(PresenceAPI::class.java) + } + } + + @Binds + abstract fun bindPresenceService(service: DefaultPresenceService): PresenceService + + @Binds + abstract fun bindSetPresenceTask(task: DefaultSetPresenceTask): SetPresenceTask + + @Binds + abstract fun bindGetPresenceTask(task: DefaultGetPresenceTask): GetPresenceTask +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/GetPresenceResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/GetPresenceResponse.kt new file mode 100644 index 0000000000..707f0a055f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/GetPresenceResponse.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class GetPresenceResponse( + @Json(name = "presence") + val presence: PresenceEnum, + @Json(name = "last_active_ago") + val lastActiveAgo: Long? = null, + @Json(name = "status_msg") + val message: String? = null, + @Json(name = "currently_active") + val isCurrentlyActive: Boolean? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceContent.kt new file mode 100644 index 0000000000..86a8b31a91 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceContent.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing the EventType.PRESENCE event content + */ +@JsonClass(generateAdapter = true) +data class PresenceContent( + @Json(name = "presence") val presence: PresenceEnum, + @Json(name = "last_active_ago") val lastActiveAgo: Long? = null, + @Json(name = "status_msg") val statusMessage: String? = null, + @Json(name = "currently_active") val isCurrentlyActive: Boolean = false, + @Json(name = "avatar_url") val avatarUrl: String? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceEnum.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceEnum.kt new file mode 100644 index 0000000000..c6dd307e30 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/PresenceEnum.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Annotate enums with @JsonClass(generateAdapter = false) to prevent + * them from being removed/obfuscated from your code by R8/ProGuard. + */ + +@JsonClass(generateAdapter = false) +enum class PresenceEnum(val value: String) { + @Json(name = "online") + ONLINE("online"), + + @Json(name = "offline") + OFFLINE("offline"), + + @Json(name = "unavailable") + UNAVAILABLE("unavailable"); + + companion object { + fun from(s: String): PresenceEnum? = values().find { it.value == s } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/SetPresenceBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/SetPresenceBody.kt new file mode 100644 index 0000000000..0ddb140908 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/SetPresenceBody.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class SetPresenceBody( + @Json(name = "presence") + val presence: PresenceEnum, + @Json(name = "status_msg") + val message: String? +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/UserPresence.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/UserPresence.kt new file mode 100644 index 0000000000..f7cd3b3834 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/model/UserPresence.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.model + +data class UserPresence( + val lastActiveAgo: Long? = null, + val statusMessage: String? = null, + val isCurrentlyActive: Boolean? = null, + val presence: PresenceEnum = PresenceEnum.OFFLINE +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/DefaultPresenceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/DefaultPresenceService.kt new file mode 100644 index 0000000000..cb00192ef3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/DefaultPresenceService.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.service + +import org.matrix.android.sdk.api.session.presence.PresenceService +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.presence.model.PresenceEnum +import org.matrix.android.sdk.internal.session.presence.service.task.GetPresenceTask +import org.matrix.android.sdk.internal.session.presence.service.task.SetPresenceTask +import javax.inject.Inject + +internal class DefaultPresenceService @Inject constructor(@UserId private val userId: String, + private val setPresenceTask: SetPresenceTask, + private val getPresenceTask: GetPresenceTask) : PresenceService { + + override suspend fun setMyPresence(presence: PresenceEnum, message: String?) { + setPresenceTask.execute(SetPresenceTask.Params(userId, presence, message)) + } + + override suspend fun fetchPresence(userId: String) = getPresenceTask.execute(GetPresenceTask.Params(userId)) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/GetPresenceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/GetPresenceTask.kt new file mode 100644 index 0000000000..bb628dbab4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/GetPresenceTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.service.task + +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.presence.PresenceAPI +import org.matrix.android.sdk.internal.session.presence.model.GetPresenceResponse +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal abstract class GetPresenceTask : Task { + data class Params( + val userId: String + ) +} + +internal class DefaultGetPresenceTask @Inject constructor( + private val presenceAPI: PresenceAPI, + private val globalErrorReceiver: GlobalErrorReceiver +) : GetPresenceTask() { + override suspend fun execute(params: Params): GetPresenceResponse { + return executeRequest(globalErrorReceiver) { + presenceAPI.getPresence(params.userId) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/SetPresenceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/SetPresenceTask.kt new file mode 100644 index 0000000000..011ec1e41a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/presence/service/task/SetPresenceTask.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.presence.service.task + +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.presence.PresenceAPI +import org.matrix.android.sdk.internal.session.presence.model.PresenceEnum +import org.matrix.android.sdk.internal.session.presence.model.SetPresenceBody +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal abstract class SetPresenceTask : Task { + data class Params( + val userId: String, + val presence: PresenceEnum, + val message: String? + ) +} + +internal class DefaultSetPresenceTask @Inject constructor( + private val presenceAPI: PresenceAPI, + private val globalErrorReceiver: GlobalErrorReceiver +) : SetPresenceTask() { + + override suspend fun execute(params: Params): Any { + return executeRequest(globalErrorReceiver) { + val setPresenceBody = SetPresenceBody(params.presence, params.message) + presenceAPI.setPresence(params.userId, setPresenceBody) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEntityFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEntityFactory.kt index f78b5d7992..d6a04de5e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEntityFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEntityFactory.kt @@ -18,10 +18,11 @@ package org.matrix.android.sdk.internal.session.room.membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity internal object RoomMemberEntityFactory { - fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberSummaryEntity { + fun create(roomId: String, userId: String, roomMember: RoomMemberContent, presence: UserPresenceEntity?): RoomMemberSummaryEntity { val primaryKey = "${roomId}_$userId" return RoomMemberSummaryEntity( primaryKey = primaryKey, @@ -31,6 +32,7 @@ internal object RoomMemberEntityFactory { avatarUrl = roomMember.avatarUrl ).apply { membership = roomMember.membership + userPresenceEntity = presence } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEventHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEventHandler.kt index 7528f80cc2..25c124bd6b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEventHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberEventHandler.kt @@ -20,6 +20,9 @@ import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity +import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator @@ -47,7 +50,13 @@ internal class RoomMemberEventHandler @Inject constructor( if (roomMember == null) { return false } - val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember) + val roomMemberEntity = RoomMemberEntityFactory.create( + roomId, + userId, + roomMember, + // When an update is happening, insertOrUpdate replace existing values with null if they are not provided, + // but we want to preserve presence record value and not replace it with null + getExistingPresenceState(realm, roomId, userId)) realm.insertOrUpdate(roomMemberEntity) if (roomMember.membership.isActive()) { val userEntity = UserEntityFactory.create(userId, roomMember) @@ -60,7 +69,15 @@ internal class RoomMemberEventHandler @Inject constructor( if (mxId != null && mxId != myUserId) { aggregator?.directChatsToCheck?.put(roomId, mxId) } - return true } + + /** + * Get the already existing presence state for a specific user & room in order NOT to be replaced in RoomMemberSummaryEntity + * by NULL value. + */ + + private fun getExistingPresenceState(realm: Realm, roomId: String, userId: String): UserPresenceEntity? { + return RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst()?.userPresenceEntity + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt index eb48958afb..d519a4a96a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt @@ -27,13 +27,13 @@ import org.matrix.android.sdk.internal.database.query.latestEvent import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI -import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler -import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler import timber.log.Timber import javax.inject.Inject import kotlin.collections.set diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index c0e428ec85..0c917448cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -42,7 +42,7 @@ import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendState import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.whereRoomId import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask -import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.Debouncer diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index 8de36d0427..47e8f7e3a3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -37,7 +37,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask -import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler import org.matrix.android.sdk.internal.task.TaskExecutor internal class DefaultTimelineService @AssistedInject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt index 8c7401ab47..b253e3db82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt @@ -33,6 +33,12 @@ import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker import org.matrix.android.sdk.internal.session.initsync.ProgressReporter import org.matrix.android.sdk.internal.session.initsync.reportSubtask import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask +import org.matrix.android.sdk.internal.session.sync.handler.CryptoSyncHandler +import org.matrix.android.sdk.internal.session.sync.handler.GroupSyncHandler +import org.matrix.android.sdk.internal.session.sync.handler.PresenceSyncHandler +import org.matrix.android.sdk.internal.session.sync.handler.SyncResponsePostTreatmentAggregatorHandler +import org.matrix.android.sdk.internal.session.sync.handler.UserAccountDataSyncHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.RoomSyncHandler import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import timber.log.Timber @@ -55,7 +61,9 @@ internal class SyncResponseHandler @Inject constructor( private val cryptoService: DefaultCryptoService, private val tokenStore: SyncTokenStore, private val processEventForPushTask: ProcessEventForPushTask, - private val pushRuleService: PushRuleService) { + private val pushRuleService: PushRuleService, + private val presenceSyncHandler: PresenceSyncHandler + ) { suspend fun handleResponse(syncResponse: SyncResponse, fromToken: String?, @@ -118,6 +126,13 @@ internal class SyncResponseHandler @Inject constructor( }.also { Timber.v("Finish handling accountData in $it ms") } + + measureTimeMillis { + Timber.v("Handle Presence") + presenceSyncHandler.handle(realm,syncResponse.presence) + }.also { + Timber.v("Finish handling Presence in $it ms") + } tokenStore.saveToken(realm, syncResponse.nextBatch) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt index cc4ccc2e46..e2f9ba2f29 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt @@ -1,11 +1,11 @@ /* - * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt similarity index 98% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt index cec5689a82..c5ec34176c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Event diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/GroupSyncHandler.kt similarity index 98% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/GroupSyncHandler.kt index 2b054e578f..552462e25e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/GroupSyncHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler import io.realm.Realm import org.matrix.android.sdk.api.session.initsync.InitSyncStep diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/PresenceSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/PresenceSyncHandler.kt new file mode 100644 index 0000000000..b395b522c8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/PresenceSyncHandler.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.sync.handler + +import io.realm.Realm +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.getPresenceContent +import org.matrix.android.sdk.api.session.sync.model.PresenceSyncResponse +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity +import org.matrix.android.sdk.internal.database.query.updateDirectUserPresence +import org.matrix.android.sdk.internal.database.query.updateUserPresence +import javax.inject.Inject + +internal class PresenceSyncHandler @Inject constructor() { + + fun handle(realm: Realm, presenceSyncResponse: PresenceSyncResponse?) { + presenceSyncResponse?.events?.filter { event -> + event.type == EventType.PRESENCE + }?.forEach { event -> + val content = event.getPresenceContent() ?: return@forEach + val userId = event.senderId ?: return@forEach + val userPresenceEntity = UserPresenceEntity( + userId = userId, + lastActiveAgo = content.lastActiveAgo, + statusMessage = content.statusMessage, + isCurrentlyActive = content.isCurrentlyActive, + avatarUrl = content.avatarUrl + ).also { + it.presence = content.presence + } + + storePresenceToDB(realm, userPresenceEntity) + } + } + + /** + * Store user presence to DB and update Direct Rooms and Room Member Summaries accordingly + */ + private fun storePresenceToDB(realm: Realm, userPresenceEntity: UserPresenceEntity) = + realm.copyToRealmOrUpdate(userPresenceEntity)?.apply { + RoomSummaryEntity.updateDirectUserPresence(realm, userPresenceEntity.userId, this) + RoomMemberSummaryEntity.updateUserPresence(realm, userPresenceEntity.userId, this) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregatorHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/SyncResponsePostTreatmentAggregatorHandler.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregatorHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/SyncResponsePostTreatmentAggregatorHandler.kt index db1100d76c..39ae94c88a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregatorHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/SyncResponsePostTreatmentAggregatorHandler.kt @@ -1,11 +1,11 @@ /* - * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,9 +14,11 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore +import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator import org.matrix.android.sdk.internal.session.sync.model.accountdata.toMutable import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt similarity index 99% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt index a9926c70aa..3e38cd7839 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler import com.zhuinden.monarchy.Monarchy import io.realm.Realm diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt similarity index 96% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/ReadReceiptHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt index fc1a2c3870..025ee329f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler.room import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.EventType @@ -23,6 +23,8 @@ import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.query.createUnmanaged import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore +import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomFullyReadHandler.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomFullyReadHandler.kt index 3d0db212c2..b5c8a099d3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomFullyReadHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler.room import io.realm.Realm import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt similarity index 98% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 52e5b6b58d..8c4af81c99 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler.room import io.realm.Realm import io.realm.kotlin.createObject @@ -62,6 +62,9 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent +import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy +import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator +import org.matrix.android.sdk.internal.session.sync.initialSyncStrategy import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler import org.matrix.android.sdk.internal.util.computeBestChunkSize import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTagHandler.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTagHandler.kt index 8997435be4..55b15624bc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTagHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler.room import io.realm.Realm import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTypingUsersHandler.kt similarity index 96% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTypingUsersHandler.kt index 1433d89143..63db13a5b8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomTypingUsersHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync +package org.matrix.android.sdk.internal.session.sync.handler.room import io.realm.Realm import org.matrix.android.sdk.api.session.room.sender.SenderInfo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt index 6ca008c5b1..5e7bde87e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt @@ -28,8 +28,8 @@ import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityField import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.session.room.read.FullyReadContent -import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler -import org.matrix.android.sdk.internal.session.sync.RoomTagHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler +import org.matrix.android.sdk.internal.session.sync.handler.room.RoomTagHandler import javax.inject.Inject internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt index e5c338d511..2e9fe8319c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt @@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.session.user.accountdata import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.events.model.Content -import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.session.sync.handler.UserAccountDataSyncHandler +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource -import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.awaitCallback diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt index 6eb06722b9..e6451b34ef 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt @@ -23,6 +23,7 @@ import android.widget.TextView import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.ui.views.PresenceStateImageView import im.vector.app.core.ui.views.ShieldImageView @EpoxyModelClass(layout = R.layout.item_profile_matrix_item) @@ -31,6 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem(R.id.matrixItemTitle) val subtitleView by bind(R.id.matrixItemSubtitle) + val presenceImageView by bind(R.id.matrixItemPresenceImageView) val avatarImageView by bind(R.id.matrixItemAvatar) val avatarDecorationImageView by bind(R.id.matrixItemAvatarDecoration) val editableView by bind(R.id.matrixItemEditable) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPresence.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPresence.kt new file mode 100644 index 0000000000..022d3dd152 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPresence.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.core.epoxy.profiles + +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.extensions.setTextOrHide +import org.matrix.android.sdk.internal.session.presence.model.UserPresence + +@EpoxyModelClass(layout = R.layout.item_profile_matrix_item) +abstract class ProfileMatrixItemWithPresence : BaseProfileMatrixItem() { + + @EpoxyAttribute var powerLevelLabel: CharSequence? = null + @EpoxyAttribute var userPresence: UserPresence? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.powerLabel.setTextOrHide(powerLevelLabel) + holder.presenceImageView.render(userPresence = userPresence) + holder.editableView.isVisible = false + } + + class Holder : ProfileMatrixItem.Holder() { + val powerLabel by bind(R.id.matrixItemPowerLevelLabel) + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/PresenceStateImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/PresenceStateImageView.kt new file mode 100644 index 0000000000..3e1b40b3c7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/PresenceStateImageView.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 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.core.ui.views + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.isVisible +import im.vector.app.R +import org.matrix.android.sdk.internal.session.presence.model.PresenceEnum +import org.matrix.android.sdk.internal.session.presence.model.UserPresence + +/** + * Custom ImageView to dynamically render Presence state in multiple screens + */ +class PresenceStateImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatImageView(context, attrs, defStyleAttr) { + + fun render(showPresence: Boolean = true, userPresence: UserPresence?) { + isVisible = showPresence && userPresence != null + + when (userPresence?.presence) { + PresenceEnum.ONLINE -> { + setImageResource(R.drawable.ic_presence_online) + contentDescription = context.getString(R.string.a11y_presence_online) + } + PresenceEnum.UNAVAILABLE -> { + setImageResource(R.drawable.ic_presence_offline) + contentDescription = context.getString(R.string.a11y_presence_unavailable) + } + PresenceEnum.OFFLINE -> { + setImageResource(R.drawable.ic_presence_offline) + contentDescription = context.getString(R.string.a11y_presence_offline) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index b3fe6fcbf4..2307bff984 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -1430,9 +1430,9 @@ class RoomDetailFragment @Inject constructor( views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN views.roomToolbarTitleView.text = roomSummary.displayName avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView) - renderSubTitle(typingMessage, roomSummary.topic) views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel) + views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt index e4abbeb1ff..dda6ffefde 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt @@ -32,12 +32,14 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.core.ui.views.PresenceStateImageView import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.util.MatrixItem +import org.matrix.android.sdk.internal.session.presence.model.UserPresence @EpoxyModelClass(layout = R.layout.item_room) abstract class RoomSummaryItem : VectorEpoxyModel() { @@ -53,6 +55,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence @EpoxyAttribute lateinit var lastEventTime: CharSequence @EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null + @EpoxyAttribute var userPresence: UserPresence? = null + @EpoxyAttribute var showPresence: Boolean = false @EpoxyAttribute var izPublic: Boolean = false @EpoxyAttribute var unreadNotificationCount: Int = 0 @EpoxyAttribute var hasUnreadMessage: Boolean = false @@ -83,6 +87,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { renderSelection(holder, showSelected) holder.typingView.setTextOrHide(typingMessage) holder.lastEventView.isInvisible = holder.typingView.isVisible + holder.roomAvatarPresenceImageView.render(showPresence, userPresence) } override fun unbind(holder: Holder) { @@ -117,6 +122,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { val roomAvatarDecorationImageView by bind(R.id.roomAvatarDecorationImageView) val roomAvatarPublicDecorationImageView by bind(R.id.roomAvatarPublicDecorationImageView) val roomAvatarFailSendingImageView by bind(R.id.roomAvatarFailSendingImageView) + val roomAvatarPresenceImageView by bind(R.id.roomAvatarPresenceImageView) val rootView by bind(R.id.itemRoomLayout) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt index c06ab33a54..fdb7d3a323 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt @@ -124,6 +124,8 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor // We do not display shield in the room list anymore // .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel) .izPublic(roomSummary.isPublic) + .showPresence(roomSummary.isDirect) + .userPresence(roomSummary.directUserPresence) .matrixItem(roomSummary.toMatrixItem()) .lastEventTime(latestEventTime) .typingMessage(typingMessage) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 4b37d038b5..e24b558ff0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -219,6 +219,7 @@ class RoomProfileFragment @Inject constructor( avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView) headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel) views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) + headerViews.roomProfilePresenceImageView.render(it.isDirect, it.directUserPresence) } } roomProfileController.setData(state) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index 8399c9e238..9a2ee04fdc 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -17,23 +17,29 @@ package im.vector.app.features.roomprofile.members import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.epoxy.profiles.profileMatrixItem +import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPresence import im.vector.app.core.extensions.join +import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import me.gujun.android.span.span import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.internal.session.presence.model.UserPresence import javax.inject.Inject class RoomMemberListController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val stringProvider: StringProvider, + private val colorProvider: ColorProvider, private val roomMemberSummaryFilter: RoomMemberSummaryFilter ) : TypedEpoxyController() { @@ -84,17 +90,10 @@ class RoomMemberListController @Inject constructor( buildProfileSection( stringProvider.getString(powerLevelCategory.titleRes) ) + filteredRoomMemberList.join( each = { _, roomMember -> - profileMatrixItem { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) - avatarRenderer(host.avatarRenderer) - userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) - clickListener { - host.callback?.onRoomMemberClicked(roomMember) - } - } + buildPresence(roomMember, powerLevelCategory, host, data, roomMember.userPresence) }, between = { _, roomMemberBefore -> dividerItem { @@ -123,6 +122,33 @@ class RoomMemberListController @Inject constructor( } } + private fun buildPresence(roomMember: RoomMemberSummary, + powerLevelCategory: RoomMemberListCategories, + host: RoomMemberListController, + data: RoomMemberListViewState, + userPresence: UserPresence? + ) { + val powerLabel = stringProvider.getString(powerLevelCategory.titleRes) + + profileMatrixItemWithPresence { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(host.avatarRenderer) + userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) + clickListener { + host.callback?.onRoomMemberClicked(roomMember) + } + userPresence(userPresence) + powerLevelLabel( + span { + span(powerLabel) { + textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) + } + } + ) + } + } + private fun buildThreePidInvites(data: RoomMemberListViewState) { val host = this data.threePidInvites() diff --git a/vector/src/main/res/drawable/ic_presence_offline.xml b/vector/src/main/res/drawable/ic_presence_offline.xml new file mode 100644 index 0000000000..3f0dc251ce --- /dev/null +++ b/vector/src/main/res/drawable/ic_presence_offline.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/drawable/ic_presence_online.xml b/vector/src/main/res/drawable/ic_presence_online.xml new file mode 100644 index 0000000000..2184f359b2 --- /dev/null +++ b/vector/src/main/res/drawable/ic_presence_online.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml index 6c58f40bad..0115c9a66a 100644 --- a/vector/src/main/res/layout/fragment_room_detail.xml +++ b/vector/src/main/res/layout/fragment_room_detail.xml @@ -45,19 +45,35 @@ + + + + tools:ignore="MissingConstraints" + tools:src="@drawable/ic_presence_offline" + tools:visibility="visible" /> + tools:text="@sample/rooms.json/data/topic" + tools:visibility="visible" /> diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index 636113752b..cea2f62968 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -1,4 +1,5 @@ + + + + tools:ignore="MissingConstraints" + tools:src="@drawable/ic_presence_offline" + tools:visibility="visible" /> + + diff --git a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml index e7d930b070..0aff5e7a5a 100644 --- a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml +++ b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml @@ -30,14 +30,33 @@ app:layout_constraintVertical_chainStyle="spread_inside" tools:src="@sample/user_round_avatars" /> - + tools:ignore="MissingConstraints" + tools:src="@drawable/ic_presence_offline" + tools:visibility="visible" /> + + diff --git a/vector/src/main/res/layout/view_stub_room_profile_header.xml b/vector/src/main/res/layout/view_stub_room_profile_header.xml index 7308e8a207..cca97cff62 100644 --- a/vector/src/main/res/layout/view_stub_room_profile_header.xml +++ b/vector/src/main/res/layout/view_stub_room_profile_header.xml @@ -20,25 +20,43 @@ app:layout_constraintTop_toTopOf="parent" tools:src="@sample/room_round_avatars" /> - + tools:ignore="MissingConstraints" + tools:src="@drawable/ic_presence_offline" + tools:visibility="visible" /> + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index bb6f73812f..cc16b61aaa 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3404,6 +3404,9 @@ View read receipts Public room Public space + Online + Offline + Unavailable Dev Tools Explore Room State