mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 12:00:03 +03:00
* 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
This commit is contained in:
parent
58b69b1fe4
commit
9ab59a543d
66 changed files with 1031 additions and 67 deletions
|
@ -23,3 +23,6 @@ vector.debugPrivateData=false
|
||||||
# httpLogLevel values: NONE, BASIC, HEADERS, BODY
|
# httpLogLevel values: NONE, BASIC, HEADERS, BODY
|
||||||
vector.httpLogLevel=BASIC
|
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
|
|
@ -133,4 +133,8 @@
|
||||||
<color name="vctr_voice_message_toast_background_light">@color/palette_black_900</color>
|
<color name="vctr_voice_message_toast_background_light">@color/palette_black_900</color>
|
||||||
<color name="vctr_voice_message_toast_background_dark">@color/palette_gray_400</color>
|
<color name="vctr_voice_message_toast_background_dark">@color/palette_gray_400</color>
|
||||||
|
|
||||||
|
<!-- Presence Indicator colors -->
|
||||||
|
<attr name="vctr_presence_indicator_offline" format="color" />
|
||||||
|
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
|
||||||
|
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
<item name="vctr_markdown_block_background_color">@android:color/black</item>
|
<item name="vctr_markdown_block_background_color">@android:color/black</item>
|
||||||
<item name="vctr_spoiler_background_color">#FFFFFFFF</item>
|
<item name="vctr_spoiler_background_color">#FFFFFFFF</item>
|
||||||
|
|
||||||
|
<!-- Presence Indicator colors -->
|
||||||
|
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_dark</item>
|
||||||
|
|
||||||
<!-- Some alias -->
|
<!-- Some alias -->
|
||||||
<item name="vctr_header_background">?vctr_system</item>
|
<item name="vctr_header_background">?vctr_system</item>
|
||||||
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
<item name="vctr_markdown_block_background_color">#FFEEEEEE</item>
|
<item name="vctr_markdown_block_background_color">#FFEEEEEE</item>
|
||||||
<item name="vctr_spoiler_background_color">#FF000000</item>
|
<item name="vctr_spoiler_background_color">#FF000000</item>
|
||||||
|
|
||||||
|
<!-- Presence Indicator colors -->
|
||||||
|
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_light</item>
|
||||||
|
|
||||||
<!-- Some alias -->
|
<!-- Some alias -->
|
||||||
<item name="vctr_header_background">?vctr_system</item>
|
<item name="vctr_header_background">?vctr_system</item>
|
||||||
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
||||||
|
|
|
@ -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.media.MediaService
|
||||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
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.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.profile.ProfileService
|
||||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||||
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
||||||
|
@ -75,6 +76,7 @@ interface Session :
|
||||||
TermsService,
|
TermsService,
|
||||||
EventService,
|
EventService,
|
||||||
ProfileService,
|
ProfileService,
|
||||||
|
PresenceService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService,
|
PushersService,
|
||||||
SyncStatusService,
|
SyncStatusService,
|
||||||
|
|
|
@ -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.algorithms.olm.OlmDecryptionResult
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
|
import org.matrix.android.sdk.internal.session.presence.model.PresenceContent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
typealias Content = JsonDict
|
typealias Content = JsonDict
|
||||||
|
@ -305,3 +306,7 @@ fun Event.isReply(): Boolean {
|
||||||
fun Event.isEdition(): Boolean {
|
fun Event.isEdition(): Boolean {
|
||||||
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Event.getPresenceContent(): PresenceContent? {
|
||||||
|
return content.toModel<PresenceContent>()
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -16,12 +16,15 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.model
|
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
|
* Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content
|
||||||
*/
|
*/
|
||||||
data class RoomMemberSummary constructor(
|
data class RoomMemberSummary constructor(
|
||||||
val membership: Membership,
|
val membership: Membership,
|
||||||
val userId: String,
|
val userId: String,
|
||||||
|
val userPresence: UserPresence? = null,
|
||||||
val displayName: String? = null,
|
val displayName: String? = null,
|
||||||
val avatarUrl: String? = null
|
val avatarUrl: String? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
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.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds some data of a room.
|
* This class holds some data of a room.
|
||||||
|
@ -38,6 +39,7 @@ data class RoomSummary(
|
||||||
val joinRules: RoomJoinRules? = null,
|
val joinRules: RoomJoinRules? = null,
|
||||||
val isDirect: Boolean = false,
|
val isDirect: Boolean = false,
|
||||||
val directUserId: String? = null,
|
val directUserId: String? = null,
|
||||||
|
val directUserPresence: UserPresence? = null,
|
||||||
val joinedMembersCount: Int? = 0,
|
val joinedMembersCount: Int? = 0,
|
||||||
val invitedMembersCount: Int? = 0,
|
val invitedMembersCount: Int? = 0,
|
||||||
val latestPreviewableEvent: TimelineEvent? = null,
|
val latestPreviewableEvent: TimelineEvent? = null,
|
||||||
|
|
|
@ -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.PreviewUrlCacheEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
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.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.RoomMembersLoadStatusType
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
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.RoomTagEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntityFields
|
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.SpaceParentSummaryEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
|
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.di.MoshiProvider
|
||||||
import org.matrix.android.sdk.internal.query.process
|
import org.matrix.android.sdk.internal.query.process
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal object RealmSessionStoreMigration : RealmMigration {
|
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) {
|
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||||
Timber.v("Migrating Realm Session from $oldVersion to $newVersion")
|
Timber.v("Migrating Realm Session from $oldVersion to $newVersion")
|
||||||
|
@ -69,6 +71,7 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
||||||
if (oldVersion <= 14) migrateTo15(realm)
|
if (oldVersion <= 14) migrateTo15(realm)
|
||||||
if (oldVersion <= 15) migrateTo16(realm)
|
if (oldVersion <= 15) migrateTo16(realm)
|
||||||
if (oldVersion <= 16) migrateTo17(realm)
|
if (oldVersion <= 16) migrateTo17(realm)
|
||||||
|
if (oldVersion <= 17) migrateTo18(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo1(realm: DynamicRealm) {
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
|
@ -338,4 +341,26 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
||||||
realm.schema.get("EventInsertEntity")
|
realm.schema.get("EventInsertEntity")
|
||||||
?.addField(EventInsertEntityFields.CAN_BE_PROCESSED, Boolean::class.java)
|
?.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.api.session.room.model.RoomMemberSummary
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.toUserPresence
|
||||||
|
|
||||||
internal object RoomMemberSummaryMapper {
|
internal object RoomMemberSummaryMapper {
|
||||||
|
|
||||||
fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary {
|
fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary {
|
||||||
return RoomMemberSummary(
|
return RoomMemberSummary(
|
||||||
userId = roomMemberSummaryEntity.userId,
|
userId = roomMemberSummaryEntity.userId,
|
||||||
|
userPresence = roomMemberSummaryEntity.userPresenceEntity?.toUserPresence(),
|
||||||
avatarUrl = roomMemberSummaryEntity.avatarUrl,
|
avatarUrl = roomMemberSummaryEntity.avatarUrl,
|
||||||
displayName = roomMemberSummaryEntity.displayName,
|
displayName = roomMemberSummaryEntity.displayName,
|
||||||
membership = roomMemberSummaryEntity.membership
|
membership = roomMemberSummaryEntity.membership
|
||||||
|
|
|
@ -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.SpaceParentInfo
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
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.RoomSummaryEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.toUserPresence
|
||||||
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
||||||
joinRules = roomSummaryEntity.joinRules,
|
joinRules = roomSummaryEntity.joinRules,
|
||||||
isDirect = roomSummaryEntity.isDirect,
|
isDirect = roomSummaryEntity.isDirect,
|
||||||
directUserId = roomSummaryEntity.directUserId,
|
directUserId = roomSummaryEntity.directUserId,
|
||||||
|
directUserPresence = roomSummaryEntity.directUserPresence?.toUserPresence(),
|
||||||
latestPreviewableEvent = latestEvent,
|
latestPreviewableEvent = latestEvent,
|
||||||
joinedMembersCount = roomSummaryEntity.joinedMembersCount,
|
joinedMembersCount = roomSummaryEntity.joinedMembersCount,
|
||||||
invitedMembersCount = roomSummaryEntity.invitedMembersCount,
|
invitedMembersCount = roomSummaryEntity.invitedMembersCount,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import io.realm.annotations.Index
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
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 = "",
|
internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "",
|
||||||
@Index var userId: String = "",
|
@Index var userId: String = "",
|
||||||
|
@ -40,6 +41,12 @@ internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String =
|
||||||
membershipStr = value.name
|
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)
|
fun toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
|
|
@ -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.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
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.api.session.room.model.tag.RoomTag
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||||
|
|
||||||
internal open class RoomSummaryEntity(
|
internal open class RoomSummaryEntity(
|
||||||
@PrimaryKey var roomId: String = "",
|
@PrimaryKey var roomId: String = "",
|
||||||
|
@ -204,6 +205,11 @@ internal open class RoomSummaryEntity(
|
||||||
if (value != field) field = value
|
if (value != field) field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var directUserPresence: UserPresenceEntity? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
var hasFailedSending: Boolean = false
|
var hasFailedSending: Boolean = false
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value != field) field = value
|
if (value != field) field = value
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.database.model
|
package org.matrix.android.sdk.internal.database.model
|
||||||
|
|
||||||
import io.realm.annotations.RealmModule
|
import io.realm.annotations.RealmModule
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Realm module for Session
|
* Realm module for Session
|
||||||
|
@ -64,6 +65,7 @@ import io.realm.annotations.RealmModule
|
||||||
WellknownIntegrationManagerConfigEntity::class,
|
WellknownIntegrationManagerConfigEntity::class,
|
||||||
RoomAccountDataEntity::class,
|
RoomAccountDataEntity::class,
|
||||||
SpaceChildSummaryEntity::class,
|
SpaceChildSummaryEntity::class,
|
||||||
SpaceParentSummaryEntity::class
|
SpaceParentSummaryEntity::class,
|
||||||
|
UserPresenceEntity::class
|
||||||
])
|
])
|
||||||
internal class SessionRealmModule
|
internal class SessionRealmModule
|
||||||
|
|
|
@ -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)
|
|
@ -21,6 +21,7 @@ import io.realm.RealmQuery
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
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.RoomMemberSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||||
|
|
||||||
internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery<RoomMemberSummaryEntity> {
|
internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery<RoomMemberSummaryEntity> {
|
||||||
val query = realm
|
val query = realm
|
||||||
|
@ -32,3 +33,13 @@ internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: Strin
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun RoomMemberSummaryEntity.Companion.updateUserPresence(realm: Realm, userId: String, userPresenceEntity: UserPresenceEntity) {
|
||||||
|
realm.where<RoomMemberSummaryEntity>()
|
||||||
|
.equalTo(RoomMemberSummaryEntityFields.USER_ID, userId)
|
||||||
|
.isNull(RoomMemberSummaryEntityFields.USER_PRESENCE_ENTITY.`$`)
|
||||||
|
.findAll()
|
||||||
|
.map {
|
||||||
|
it.userPresenceEntity = userPresenceEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import io.realm.kotlin.createObject
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
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.RoomSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||||
|
|
||||||
internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
|
internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
|
||||||
val query = realm.where<RoomSummaryEntity>()
|
val query = realm.where<RoomSummaryEntity>()
|
||||||
|
@ -67,3 +68,11 @@ internal fun RoomSummaryEntity.Companion.isDirect(realm: Realm, roomId: String):
|
||||||
.findAll()
|
.findAll()
|
||||||
.isNotEmpty()
|
.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
|
||||||
|
}
|
||||||
|
|
|
@ -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<UserPresenceEntity> {
|
||||||
|
return realm
|
||||||
|
.where<UserPresenceEntity>()
|
||||||
|
.equalTo(UserPresenceEntityFields.USER_ID, userId)
|
||||||
|
}
|
|
@ -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.media.MediaService
|
||||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
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.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.profile.ProfileService
|
||||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||||
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
||||||
|
@ -127,6 +128,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
private val callSignalingService: Lazy<CallSignalingService>,
|
private val callSignalingService: Lazy<CallSignalingService>,
|
||||||
private val spaceService: Lazy<SpaceService>,
|
private val spaceService: Lazy<SpaceService>,
|
||||||
private val openIdService: Lazy<OpenIdService>,
|
private val openIdService: Lazy<OpenIdService>,
|
||||||
|
private val presenceService: Lazy<PresenceService>,
|
||||||
@UnauthenticatedWithCertificate
|
@UnauthenticatedWithCertificate
|
||||||
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
||||||
) : Session,
|
) : Session,
|
||||||
|
@ -145,6 +147,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
SecureStorageService by secureStorageService.get(),
|
SecureStorageService by secureStorageService.get(),
|
||||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||||
ProfileService by profileService.get(),
|
ProfileService by profileService.get(),
|
||||||
|
PresenceService by presenceService.get(),
|
||||||
AccountService by accountService.get() {
|
AccountService by accountService.get() {
|
||||||
|
|
||||||
override val sharedSecretStorageService: SharedSecretStorageService
|
override val sharedSecretStorageService: SharedSecretStorageService
|
||||||
|
|
|
@ -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.integrationmanager.IntegrationManagerModule
|
||||||
import org.matrix.android.sdk.internal.session.media.MediaModule
|
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.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.profile.ProfileModule
|
||||||
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
||||||
import org.matrix.android.sdk.internal.session.pushers.PushersModule
|
import org.matrix.android.sdk.internal.session.pushers.PushersModule
|
||||||
|
@ -94,7 +95,8 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||||
CallModule::class,
|
CallModule::class,
|
||||||
SearchModule::class,
|
SearchModule::class,
|
||||||
ThirdPartyModule::class,
|
ThirdPartyModule::class,
|
||||||
SpaceModule::class
|
SpaceModule::class,
|
||||||
|
PresenceModule::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@SessionScope
|
@SessionScope
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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?
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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))
|
||||||
|
}
|
|
@ -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<GetPresenceTask.Params, GetPresenceResponse> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<SetPresenceTask.Params, Any> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.api.session.room.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||||
|
|
||||||
internal object RoomMemberEntityFactory {
|
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"
|
val primaryKey = "${roomId}_$userId"
|
||||||
return RoomMemberSummaryEntity(
|
return RoomMemberSummaryEntity(
|
||||||
primaryKey = primaryKey,
|
primaryKey = primaryKey,
|
||||||
|
@ -31,6 +32,7 @@ internal object RoomMemberEntityFactory {
|
||||||
avatarUrl = roomMember.avatarUrl
|
avatarUrl = roomMember.avatarUrl
|
||||||
).apply {
|
).apply {
|
||||||
membership = roomMember.membership
|
membership = roomMember.membership
|
||||||
|
userPresenceEntity = presence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.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.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||||
|
@ -47,7 +50,13 @@ internal class RoomMemberEventHandler @Inject constructor(
|
||||||
if (roomMember == null) {
|
if (roomMember == null) {
|
||||||
return false
|
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)
|
realm.insertOrUpdate(roomMemberEntity)
|
||||||
if (roomMember.membership.isActive()) {
|
if (roomMember.membership.isActive()) {
|
||||||
val userEntity = UserEntityFactory.create(userId, roomMember)
|
val userEntity = UserEntityFactory.create(userId, roomMember)
|
||||||
|
@ -60,7 +69,15 @@ internal class RoomMemberEventHandler @Inject constructor(
|
||||||
if (mxId != null && mxId != myUserId) {
|
if (mxId != null && mxId != myUserId) {
|
||||||
aggregator?.directChatsToCheck?.put(roomId, mxId)
|
aggregator?.directChatsToCheck?.put(roomId, mxId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
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.handler.room.RoomFullyReadHandler
|
||||||
import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler
|
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
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 timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
|
@ -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.where
|
||||||
import org.matrix.android.sdk.internal.database.query.whereRoomId
|
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.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.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.util.Debouncer
|
import org.matrix.android.sdk.internal.util.Debouncer
|
||||||
|
|
|
@ -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.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
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.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.TaskExecutor
|
||||||
|
|
||||||
internal class DefaultTimelineService @AssistedInject constructor(
|
internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
|
|
|
@ -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.ProgressReporter
|
||||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
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.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.util.awaitTransaction
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -55,7 +61,9 @@ internal class SyncResponseHandler @Inject constructor(
|
||||||
private val cryptoService: DefaultCryptoService,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val tokenStore: SyncTokenStore,
|
private val tokenStore: SyncTokenStore,
|
||||||
private val processEventForPushTask: ProcessEventForPushTask,
|
private val processEventForPushTask: ProcessEventForPushTask,
|
||||||
private val pushRuleService: PushRuleService) {
|
private val pushRuleService: PushRuleService,
|
||||||
|
private val presenceSyncHandler: PresenceSyncHandler
|
||||||
|
) {
|
||||||
|
|
||||||
suspend fun handleResponse(syncResponse: SyncResponse,
|
suspend fun handleResponse(syncResponse: SyncResponse,
|
||||||
fromToken: String?,
|
fromToken: String?,
|
||||||
|
@ -118,6 +126,13 @@ internal class SyncResponseHandler @Inject constructor(
|
||||||
}.also {
|
}.also {
|
||||||
Timber.v("Finish handling accountData in $it ms")
|
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)
|
tokenStore.saveToken(realm, syncResponse.nextBatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.crypto.MXCryptoError
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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 io.realm.Realm
|
||||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,9 +14,11 @@
|
||||||
* limitations under the License.
|
* 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.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.sync.model.accountdata.toMutable
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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 com.zhuinden.monarchy.Monarchy
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.Realm
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.createUnmanaged
|
||||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
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 timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.Realm
|
||||||
import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity
|
import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.Realm
|
||||||
import io.realm.kotlin.createObject
|
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.PaginationDirection
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
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.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.session.sync.parsing.RoomSyncAccountDataHandler
|
||||||
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.Realm
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.Realm
|
||||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
|
@ -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.model.RoomEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
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.room.read.FullyReadContent
|
||||||
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.session.sync.RoomTagHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomTagHandler
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler,
|
internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler,
|
||||||
|
|
|
@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.session.user.accountdata
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
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.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.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
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.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.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
|
|
|
@ -23,6 +23,7 @@ import android.widget.TextView
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
import im.vector.app.core.ui.views.ShieldImageView
|
import im.vector.app.core.ui.views.ShieldImageView
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
|
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
|
||||||
|
@ -31,6 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem<ProfileMatrixItem.Holde
|
||||||
open class Holder : VectorEpoxyHolder() {
|
open class Holder : VectorEpoxyHolder() {
|
||||||
val titleView by bind<TextView>(R.id.matrixItemTitle)
|
val titleView by bind<TextView>(R.id.matrixItemTitle)
|
||||||
val subtitleView by bind<TextView>(R.id.matrixItemSubtitle)
|
val subtitleView by bind<TextView>(R.id.matrixItemSubtitle)
|
||||||
|
val presenceImageView by bind<PresenceStateImageView>(R.id.matrixItemPresenceImageView)
|
||||||
val avatarImageView by bind<ImageView>(R.id.matrixItemAvatar)
|
val avatarImageView by bind<ImageView>(R.id.matrixItemAvatar)
|
||||||
val avatarDecorationImageView by bind<ShieldImageView>(R.id.matrixItemAvatarDecoration)
|
val avatarDecorationImageView by bind<ShieldImageView>(R.id.matrixItemAvatarDecoration)
|
||||||
val editableView by bind<View>(R.id.matrixItemEditable)
|
val editableView by bind<View>(R.id.matrixItemEditable)
|
||||||
|
|
|
@ -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<ProfileMatrixItemWithPresence.Holder>() {
|
||||||
|
|
||||||
|
@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<TextView>(R.id.matrixItemPowerLevelLabel)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1430,9 +1430,9 @@ class RoomDetailFragment @Inject constructor(
|
||||||
views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
|
views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
|
||||||
views.roomToolbarTitleView.text = roomSummary.displayName
|
views.roomToolbarTitleView.text = roomSummary.displayName
|
||||||
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
|
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
|
||||||
|
|
||||||
renderSubTitle(typingMessage, roomSummary.topic)
|
renderSubTitle(typingMessage, roomSummary.topic)
|
||||||
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
|
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
|
||||||
|
views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,14 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.app.core.epoxy.onClick
|
import im.vector.app.core.epoxy.onClick
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
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.core.ui.views.ShieldImageView
|
||||||
import im.vector.app.features.displayname.getBestName
|
import im.vector.app.features.displayname.getBestName
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.themes.ThemeUtils
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_room)
|
@EpoxyModelClass(layout = R.layout.item_room)
|
||||||
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
|
@ -53,6 +55,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
|
||||||
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
||||||
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||||
|
@EpoxyAttribute var userPresence: UserPresence? = null
|
||||||
|
@EpoxyAttribute var showPresence: Boolean = false
|
||||||
@EpoxyAttribute var izPublic: Boolean = false
|
@EpoxyAttribute var izPublic: Boolean = false
|
||||||
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||||
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
||||||
|
@ -83,6 +87,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
renderSelection(holder, showSelected)
|
renderSelection(holder, showSelected)
|
||||||
holder.typingView.setTextOrHide(typingMessage)
|
holder.typingView.setTextOrHide(typingMessage)
|
||||||
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
||||||
|
holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbind(holder: Holder) {
|
override fun unbind(holder: Holder) {
|
||||||
|
@ -117,6 +122,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
|
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
|
||||||
val roomAvatarPublicDecorationImageView by bind<ImageView>(R.id.roomAvatarPublicDecorationImageView)
|
val roomAvatarPublicDecorationImageView by bind<ImageView>(R.id.roomAvatarPublicDecorationImageView)
|
||||||
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
|
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
|
||||||
|
val roomAvatarPresenceImageView by bind<PresenceStateImageView>(R.id.roomAvatarPresenceImageView)
|
||||||
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
|
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,8 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
||||||
// We do not display shield in the room list anymore
|
// We do not display shield in the room list anymore
|
||||||
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
||||||
.izPublic(roomSummary.isPublic)
|
.izPublic(roomSummary.isPublic)
|
||||||
|
.showPresence(roomSummary.isDirect)
|
||||||
|
.userPresence(roomSummary.directUserPresence)
|
||||||
.matrixItem(roomSummary.toMatrixItem())
|
.matrixItem(roomSummary.toMatrixItem())
|
||||||
.lastEventTime(latestEventTime)
|
.lastEventTime(latestEventTime)
|
||||||
.typingMessage(typingMessage)
|
.typingMessage(typingMessage)
|
||||||
|
|
|
@ -219,6 +219,7 @@ class RoomProfileFragment @Inject constructor(
|
||||||
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
|
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
|
||||||
headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel)
|
headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel)
|
||||||
views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
|
views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
|
||||||
|
headerViews.roomProfilePresenceImageView.render(it.isDirect, it.directUserPresence)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roomProfileController.setData(state)
|
roomProfileController.setData(state)
|
||||||
|
|
|
@ -17,23 +17,29 @@
|
||||||
package im.vector.app.features.roomprofile.members
|
package im.vector.app.features.roomprofile.members
|
||||||
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.dividerItem
|
import im.vector.app.core.epoxy.dividerItem
|
||||||
import im.vector.app.core.epoxy.profiles.buildProfileSection
|
import im.vector.app.core.epoxy.profiles.buildProfileSection
|
||||||
import im.vector.app.core.epoxy.profiles.profileMatrixItem
|
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.extensions.join
|
||||||
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
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.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
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.RoomMemberSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent
|
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.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomMemberListController @Inject constructor(
|
class RoomMemberListController @Inject constructor(
|
||||||
private val avatarRenderer: AvatarRenderer,
|
private val avatarRenderer: AvatarRenderer,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
private val roomMemberSummaryFilter: RoomMemberSummaryFilter
|
private val roomMemberSummaryFilter: RoomMemberSummaryFilter
|
||||||
) : TypedEpoxyController<RoomMemberListViewState>() {
|
) : TypedEpoxyController<RoomMemberListViewState>() {
|
||||||
|
|
||||||
|
@ -84,17 +90,10 @@ class RoomMemberListController @Inject constructor(
|
||||||
buildProfileSection(
|
buildProfileSection(
|
||||||
stringProvider.getString(powerLevelCategory.titleRes)
|
stringProvider.getString(powerLevelCategory.titleRes)
|
||||||
)
|
)
|
||||||
|
|
||||||
filteredRoomMemberList.join(
|
filteredRoomMemberList.join(
|
||||||
each = { _, roomMember ->
|
each = { _, roomMember ->
|
||||||
profileMatrixItem {
|
buildPresence(roomMember, powerLevelCategory, host, data, roomMember.userPresence)
|
||||||
id(roomMember.userId)
|
|
||||||
matrixItem(roomMember.toMatrixItem())
|
|
||||||
avatarRenderer(host.avatarRenderer)
|
|
||||||
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
|
|
||||||
clickListener {
|
|
||||||
host.callback?.onRoomMemberClicked(roomMember)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
between = { _, roomMemberBefore ->
|
between = { _, roomMemberBefore ->
|
||||||
dividerItem {
|
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) {
|
private fun buildThreePidInvites(data: RoomMemberListViewState) {
|
||||||
val host = this
|
val host = this
|
||||||
data.threePidInvites()
|
data.threePidInvites()
|
||||||
|
|
24
vector/src/main/res/drawable/ic_presence_offline.xml
Normal file
24
vector/src/main/res/drawable/ic_presence_offline.xml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="11.89dp"
|
||||||
|
android:height="12dp"
|
||||||
|
android:viewportWidth="11.89"
|
||||||
|
android:viewportHeight="12"
|
||||||
|
>
|
||||||
|
|
||||||
|
<group>
|
||||||
|
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.8857 6C11.8857 9.31371 9.225 12 5.94286 12C2.66071 12 0 9.31371 0 6C0 2.68629 2.66071 0 5.94286 0C9.225 0 11.8857 2.68629 11.8857 6Z"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:pathData="M0 0V12H11.8857V0"
|
||||||
|
android:fillColor="?vctr_presence_indicator_offline"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</vector>
|
24
vector/src/main/res/drawable/ic_presence_online.xml
Normal file
24
vector/src/main/res/drawable/ic_presence_online.xml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="11.89dp"
|
||||||
|
android:height="12dp"
|
||||||
|
android:viewportWidth="11.89"
|
||||||
|
android:viewportHeight="12"
|
||||||
|
>
|
||||||
|
|
||||||
|
<group>
|
||||||
|
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.8857 6C11.8857 9.31371 9.225 12 5.94286 12C2.66071 12 0 9.31371 0 6C0 2.68629 2.66071 0 5.94286 0C9.225 0 11.8857 2.68629 11.8857 6Z"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:pathData="M0 0V12H11.8857V0"
|
||||||
|
android:fillColor="#0DBD8B"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -45,19 +45,35 @@
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.ShieldImageView
|
<im.vector.app.core.ui.views.ShieldImageView
|
||||||
android:id="@+id/roomToolbarDecorationImageView"
|
android:id="@+id/roomToolbarDecorationImageView"
|
||||||
android:layout_width="20dp"
|
android:layout_width="17dp"
|
||||||
android:layout_height="20dp"
|
android:layout_height="17dp"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/roomToolbarTitleView"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/roomToolbarTitleView" />
|
||||||
|
|
||||||
|
|
||||||
|
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
|
android:id="@+id/roomToolbarPresenceImageView"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:background="@drawable/background_circle"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
|
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
|
||||||
app:layout_constraintCircleAngle="135"
|
app:layout_constraintCircleAngle="135"
|
||||||
app:layout_constraintCircleRadius="20dp"
|
app:layout_constraintCircleRadius="20dp"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_presence_offline"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomToolbarTitleView"
|
android:id="@+id/roomToolbarTitleView"
|
||||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
@ -65,8 +81,10 @@
|
||||||
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
|
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
|
app:layout_constraintStart_toEndOf="@+id/roomToolbarDecorationImageView"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_goneMarginStart="7dp"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
tools:text="@sample/rooms.json/data/name" />
|
tools:text="@sample/rooms.json/data/name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -74,9 +92,8 @@
|
||||||
style="@style/Widget.Vector.TextView.Body"
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="7dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?vctr_content_primary"
|
android:textColor="?vctr_content_primary"
|
||||||
|
@ -85,7 +102,8 @@
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
|
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
|
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
|
||||||
tools:text="@sample/rooms.json/data/topic" />
|
tools:text="@sample/rooms.json/data/topic"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://tinyurl.com/PresenceListInRooms -->
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
@ -28,19 +29,35 @@
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.ShieldImageView
|
<im.vector.app.core.ui.views.ShieldImageView
|
||||||
android:id="@+id/matrixItemAvatarDecoration"
|
android:id="@+id/matrixItemAvatarDecoration"
|
||||||
android:layout_width="20dp"
|
android:layout_width="16dp"
|
||||||
android:layout_height="20dp"
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/matrixItemTitle"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/matrixItemAvatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/matrixItemTitle" />
|
||||||
|
|
||||||
|
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
|
android:id="@+id/matrixItemPresenceImageView"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:background="@drawable/background_circle"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintCircle="@+id/matrixItemAvatar"
|
app:layout_constraintCircle="@+id/matrixItemAvatar"
|
||||||
app:layout_constraintCircleAngle="135"
|
app:layout_constraintCircleAngle="135"
|
||||||
app:layout_constraintCircleRadius="16dp"
|
app:layout_constraintCircleRadius="16dp"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_presence_offline"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixItemTitle"
|
android:id="@+id/matrixItemTitle"
|
||||||
style="@style/Widget.Vector.TextView.Subtitle"
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
@ -48,9 +65,10 @@
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel"
|
app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel"
|
||||||
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
app:layout_constraintStart_toEndOf="@+id/matrixItemAvatarDecoration"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_goneMarginEnd="80dp"
|
app:layout_goneMarginEnd="80dp"
|
||||||
|
app:layout_goneMarginStart="7dp"
|
||||||
tools:text="@sample/users.json/data/displayName" />
|
tools:text="@sample/users.json/data/displayName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -58,7 +76,7 @@
|
||||||
style="@style/Widget.Vector.TextView.Body"
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="7dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
|
|
@ -84,6 +84,21 @@
|
||||||
app:layout_constraintCircleAngle="135"
|
app:layout_constraintCircleAngle="135"
|
||||||
app:layout_constraintCircleRadius="28dp"
|
app:layout_constraintCircleRadius="28dp"
|
||||||
tools:ignore="MissingConstraints"
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
|
android:id="@+id/roomAvatarPresenceImageView"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:background="@drawable/background_circle"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintCircle="@id/roomAvatarContainer"
|
||||||
|
app:layout_constraintCircleAngle="135"
|
||||||
|
app:layout_constraintCircleRadius="28dp"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_presence_offline"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<!-- Margin bottom does not work, so I use space -->
|
<!-- Margin bottom does not work, so I use space -->
|
||||||
|
|
|
@ -30,14 +30,33 @@
|
||||||
app:layout_constraintVertical_chainStyle="spread_inside"
|
app:layout_constraintVertical_chainStyle="spread_inside"
|
||||||
tools:src="@sample/user_round_avatars" />
|
tools:src="@sample/user_round_avatars" />
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.ShieldImageView
|
|
||||||
android:id="@+id/memberProfileDecorationImageView"
|
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
android:layout_width="48dp"
|
android:id="@+id/memberProfilePresenceImageView"
|
||||||
android:layout_height="48dp"
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:background="@drawable/background_circle"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="3dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintCircle="@+id/memberProfileAvatarView"
|
app:layout_constraintCircle="@+id/memberProfileAvatarView"
|
||||||
app:layout_constraintCircleAngle="135"
|
app:layout_constraintCircleAngle="135"
|
||||||
app:layout_constraintCircleRadius="64dp"
|
app:layout_constraintCircleRadius="64dp"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_presence_offline"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<im.vector.app.core.ui.views.ShieldImageView
|
||||||
|
android:id="@+id/memberProfileDecorationImageView"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/memberProfileNameView"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/memberProfileNameView"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/memberProfileNameView"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/memberProfileNameView"
|
android:id="@+id/memberProfileNameView"
|
||||||
|
@ -48,7 +67,7 @@
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/memberProfileIdView"
|
app:layout_constraintBottom_toTopOf="@+id/memberProfileIdView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toEndOf="@+id/memberProfileDecorationImageView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/memberProfileAvatarView"
|
app:layout_constraintTop_toBottomOf="@+id/memberProfileAvatarView"
|
||||||
tools:text="@sample/users.json/data/displayName" />
|
tools:text="@sample/users.json/data/displayName" />
|
||||||
|
|
||||||
|
|
|
@ -20,25 +20,43 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@sample/room_round_avatars" />
|
tools:src="@sample/room_round_avatars" />
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.ShieldImageView
|
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||||
android:id="@+id/roomProfileDecorationImageView"
|
android:id="@+id/roomProfilePresenceImageView"
|
||||||
android:layout_width="48dp"
|
android:layout_width="26dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="26dp"
|
||||||
|
android:background="@drawable/background_circle"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:padding="3dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintCircle="@+id/roomProfileAvatarView"
|
app:layout_constraintCircle="@+id/roomProfileAvatarView"
|
||||||
app:layout_constraintCircleAngle="135"
|
app:layout_constraintCircleAngle="135"
|
||||||
app:layout_constraintCircleRadius="64dp"
|
app:layout_constraintCircleRadius="64dp"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:src="@drawable/ic_presence_offline"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<im.vector.app.core.ui.views.ShieldImageView
|
||||||
|
android:id="@+id/roomProfileDecorationImageView"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/roomProfileNameView"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/roomProfileNameView"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/roomProfileNameView"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/roomProfileNameView"
|
android:id="@+id/roomProfileNameView"
|
||||||
style="@style/Widget.Vector.TextView.Title"
|
style="@style/Widget.Vector.TextView.Title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/roomProfileAliasView"
|
app:layout_constraintBottom_toTopOf="@+id/roomProfileAliasView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toEndOf="@+id/roomProfileDecorationImageView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/roomProfileAvatarView"
|
app:layout_constraintTop_toBottomOf="@+id/roomProfileAvatarView"
|
||||||
tools:text="@sample/rooms.json/data/name" />
|
tools:text="@sample/rooms.json/data/name" />
|
||||||
|
|
||||||
|
|
|
@ -3404,6 +3404,9 @@
|
||||||
<string name="a11y_view_read_receipts">View read receipts</string>
|
<string name="a11y_view_read_receipts">View read receipts</string>
|
||||||
<string name="a11y_public_room">Public room</string>
|
<string name="a11y_public_room">Public room</string>
|
||||||
<string name="a11y_public_space">Public space</string>
|
<string name="a11y_public_space">Public space</string>
|
||||||
|
<string name="a11y_presence_online">Online</string>
|
||||||
|
<string name="a11y_presence_offline">Offline</string>
|
||||||
|
<string name="a11y_presence_unavailable">Unavailable</string>
|
||||||
|
|
||||||
<string name="dev_tools_menu_name">Dev Tools</string>
|
<string name="dev_tools_menu_name">Dev Tools</string>
|
||||||
<string name="dev_tools_explore_room_state">Explore Room State</string>
|
<string name="dev_tools_explore_room_state">Explore Room State</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue