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