mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +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
|
||||
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_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>
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
<item name="vctr_markdown_block_background_color">@android:color/black</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 -->
|
||||
<item name="vctr_header_background">?vctr_system</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_spoiler_background_color">#FF000000</item>
|
||||
|
||||
<!-- Presence Indicator colors -->
|
||||
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_light</item>
|
||||
|
||||
<!-- Some alias -->
|
||||
<item name="vctr_header_background">?vctr_system</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.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.presence.PresenceService
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
||||
|
@ -75,6 +76,7 @@ interface Session :
|
|||
TermsService,
|
||||
EventService,
|
||||
ProfileService,
|
||||
PresenceService,
|
||||
PushRuleService,
|
||||
PushersService,
|
||||
SyncStatusService,
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.util.JsonDict
|
|||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.session.presence.model.PresenceContent
|
||||
import timber.log.Timber
|
||||
|
||||
typealias Content = JsonDict
|
||||
|
@ -305,3 +306,7 @@ fun Event.isReply(): Boolean {
|
|||
fun Event.isEdition(): Boolean {
|
||||
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
||||
}
|
||||
|
||||
fun Event.getPresenceContent(): PresenceContent? {
|
||||
return content.toModel<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
|
||||
|
||||
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||
|
||||
/**
|
||||
* Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content
|
||||
*/
|
||||
data class RoomMemberSummary constructor(
|
||||
val membership: Membership,
|
||||
val userId: String,
|
||||
val userPresence: UserPresence? = null,
|
||||
val displayName: String? = null,
|
||||
val avatarUrl: String? = null
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
|||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||
|
||||
/**
|
||||
* This class holds some data of a room.
|
||||
|
@ -38,6 +39,7 @@ data class RoomSummary(
|
|||
val joinRules: RoomJoinRules? = null,
|
||||
val isDirect: Boolean = false,
|
||||
val directUserId: String? = null,
|
||||
val directUserPresence: UserPresence? = null,
|
||||
val joinedMembersCount: Int? = 0,
|
||||
val invitedMembersCount: Int? = 0,
|
||||
val latestPreviewableEvent: TimelineEvent? = null,
|
||||
|
|
|
@ -35,19 +35,21 @@ import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityField
|
|||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntityFields
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.query.process
|
||||
import timber.log.Timber
|
||||
|
||||
internal object RealmSessionStoreMigration : RealmMigration {
|
||||
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 17L
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 18L
|
||||
|
||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||
Timber.v("Migrating Realm Session from $oldVersion to $newVersion")
|
||||
|
@ -69,6 +71,7 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
|||
if (oldVersion <= 14) migrateTo15(realm)
|
||||
if (oldVersion <= 15) migrateTo16(realm)
|
||||
if (oldVersion <= 16) migrateTo17(realm)
|
||||
if (oldVersion <= 17) migrateTo18(realm)
|
||||
}
|
||||
|
||||
private fun migrateTo1(realm: DynamicRealm) {
|
||||
|
@ -338,4 +341,26 @@ internal object RealmSessionStoreMigration : RealmMigration {
|
|||
realm.schema.get("EventInsertEntity")
|
||||
?.addField(EventInsertEntityFields.CAN_BE_PROCESSED, Boolean::class.java)
|
||||
}
|
||||
|
||||
private fun migrateTo18(realm: DynamicRealm) {
|
||||
Timber.d("Step 17 -> 18")
|
||||
realm.schema.create("UserPresenceEntity")
|
||||
?.addField(UserPresenceEntityFields.USER_ID, String::class.java)
|
||||
?.addPrimaryKey(UserPresenceEntityFields.USER_ID)
|
||||
?.setRequired(UserPresenceEntityFields.USER_ID, true)
|
||||
?.addField(UserPresenceEntityFields.PRESENCE_STR, String::class.java)
|
||||
?.addField(UserPresenceEntityFields.LAST_ACTIVE_AGO, Long::class.java)
|
||||
?.setNullable(UserPresenceEntityFields.LAST_ACTIVE_AGO, true)
|
||||
?.addField(UserPresenceEntityFields.STATUS_MESSAGE, String::class.java)
|
||||
?.addField(UserPresenceEntityFields.IS_CURRENTLY_ACTIVE, Boolean::class.java)
|
||||
?.setNullable(UserPresenceEntityFields.IS_CURRENTLY_ACTIVE, true)
|
||||
?.addField(UserPresenceEntityFields.AVATAR_URL, String::class.java)
|
||||
|
||||
val userPresenceEntity = realm.schema.get("UserPresenceEntity") ?: return
|
||||
realm.schema.get("RoomSummaryEntity")
|
||||
?.addRealmObjectField(RoomSummaryEntityFields.DIRECT_USER_PRESENCE.`$`, userPresenceEntity)
|
||||
|
||||
realm.schema.get("RoomMemberSummaryEntity")
|
||||
?.addRealmObjectField(RoomMemberSummaryEntityFields.USER_PRESENCE_ENTITY.`$`, userPresenceEntity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@ package org.matrix.android.sdk.internal.database.mapper
|
|||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.toUserPresence
|
||||
|
||||
internal object RoomMemberSummaryMapper {
|
||||
|
||||
fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary {
|
||||
return RoomMemberSummary(
|
||||
userId = roomMemberSummaryEntity.userId,
|
||||
userPresence = roomMemberSummaryEntity.userPresenceEntity?.toUserPresence(),
|
||||
avatarUrl = roomMemberSummaryEntity.avatarUrl,
|
||||
displayName = roomMemberSummaryEntity.displayName,
|
||||
membership = roomMemberSummaryEntity.membership
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
|||
import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.toUserPresence
|
||||
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -48,6 +49,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||
joinRules = roomSummaryEntity.joinRules,
|
||||
isDirect = roomSummaryEntity.isDirect,
|
||||
directUserId = roomSummaryEntity.directUserId,
|
||||
directUserPresence = roomSummaryEntity.directUserPresence?.toUserPresence(),
|
||||
latestPreviewableEvent = latestEvent,
|
||||
joinedMembersCount = roomSummaryEntity.joinedMembersCount,
|
||||
invitedMembersCount = roomSummaryEntity.invitedMembersCount,
|
||||
|
|
|
@ -21,6 +21,7 @@ import io.realm.annotations.Index
|
|||
import io.realm.annotations.PrimaryKey
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "",
|
||||
@Index var userId: String = "",
|
||||
|
@ -40,6 +41,12 @@ internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String =
|
|||
membershipStr = value.name
|
||||
}
|
||||
|
||||
var userPresenceEntity: UserPresenceEntity? = null
|
||||
set(value) {
|
||||
if (value != field) field = value
|
||||
}
|
||||
|
||||
fun getBestName() = displayName?.takeIf { it.isNotBlank() } ?: userId
|
||||
fun toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||
|
||||
companion object
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
internal open class RoomSummaryEntity(
|
||||
@PrimaryKey var roomId: String = "",
|
||||
|
@ -204,6 +205,11 @@ internal open class RoomSummaryEntity(
|
|||
if (value != field) field = value
|
||||
}
|
||||
|
||||
var directUserPresence: UserPresenceEntity? = null
|
||||
set(value) {
|
||||
if (value != field) field = value
|
||||
}
|
||||
|
||||
var hasFailedSending: Boolean = false
|
||||
set(value) {
|
||||
if (value != field) field = value
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.database.model
|
||||
|
||||
import io.realm.annotations.RealmModule
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
/**
|
||||
* Realm module for Session
|
||||
|
@ -64,6 +65,7 @@ import io.realm.annotations.RealmModule
|
|||
WellknownIntegrationManagerConfigEntity::class,
|
||||
RoomAccountDataEntity::class,
|
||||
SpaceChildSummaryEntity::class,
|
||||
SpaceParentSummaryEntity::class
|
||||
SpaceParentSummaryEntity::class,
|
||||
UserPresenceEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -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 org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery<RoomMemberSummaryEntity> {
|
||||
val query = realm
|
||||
|
@ -32,3 +33,13 @@ internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: Strin
|
|||
}
|
||||
return query
|
||||
}
|
||||
|
||||
internal fun RoomMemberSummaryEntity.Companion.updateUserPresence(realm: Realm, userId: String, userPresenceEntity: UserPresenceEntity) {
|
||||
realm.where<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 org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
|
||||
val query = realm.where<RoomSummaryEntity>()
|
||||
|
@ -67,3 +68,11 @@ internal fun RoomSummaryEntity.Companion.isDirect(realm: Realm, roomId: String):
|
|||
.findAll()
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
internal fun RoomSummaryEntity.Companion.updateDirectUserPresence(realm: Realm, directUserId: String, userPresenceEntity: UserPresenceEntity) {
|
||||
RoomSummaryEntity.where(realm)
|
||||
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||
.equalTo(RoomSummaryEntityFields.DIRECT_USER_ID, directUserId)
|
||||
.findFirst()
|
||||
?.directUserPresence = userPresenceEntity
|
||||
}
|
||||
|
|
|
@ -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.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.presence.PresenceService
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
|
||||
|
@ -127,6 +128,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val callSignalingService: Lazy<CallSignalingService>,
|
||||
private val spaceService: Lazy<SpaceService>,
|
||||
private val openIdService: Lazy<OpenIdService>,
|
||||
private val presenceService: Lazy<PresenceService>,
|
||||
@UnauthenticatedWithCertificate
|
||||
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
||||
) : Session,
|
||||
|
@ -145,6 +147,7 @@ internal class DefaultSession @Inject constructor(
|
|||
SecureStorageService by secureStorageService.get(),
|
||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||
ProfileService by profileService.get(),
|
||||
PresenceService by presenceService.get(),
|
||||
AccountService by accountService.get() {
|
||||
|
||||
override val sharedSecretStorageService: SharedSecretStorageService
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.matrix.android.sdk.internal.session.identity.IdentityModule
|
|||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerModule
|
||||
import org.matrix.android.sdk.internal.session.media.MediaModule
|
||||
import org.matrix.android.sdk.internal.session.openid.OpenIdModule
|
||||
import org.matrix.android.sdk.internal.session.presence.di.PresenceModule
|
||||
import org.matrix.android.sdk.internal.session.profile.ProfileModule
|
||||
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
||||
import org.matrix.android.sdk.internal.session.pushers.PushersModule
|
||||
|
@ -94,7 +95,8 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
|
|||
CallModule::class,
|
||||
SearchModule::class,
|
||||
ThirdPartyModule::class,
|
||||
SpaceModule::class
|
||||
SpaceModule::class,
|
||||
PresenceModule::class
|
||||
]
|
||||
)
|
||||
@SessionScope
|
||||
|
|
|
@ -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.internal.database.model.RoomMemberSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
|
||||
internal object RoomMemberEntityFactory {
|
||||
|
||||
fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberSummaryEntity {
|
||||
fun create(roomId: String, userId: String, roomMember: RoomMemberContent, presence: UserPresenceEntity?): RoomMemberSummaryEntity {
|
||||
val primaryKey = "${roomId}_$userId"
|
||||
return RoomMemberSummaryEntity(
|
||||
primaryKey = primaryKey,
|
||||
|
@ -31,6 +32,7 @@ internal object RoomMemberEntityFactory {
|
|||
avatarUrl = roomMember.avatarUrl
|
||||
).apply {
|
||||
membership = roomMember.membership
|
||||
userPresenceEntity = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ import io.realm.Realm
|
|||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
|
@ -47,7 +50,13 @@ internal class RoomMemberEventHandler @Inject constructor(
|
|||
if (roomMember == null) {
|
||||
return false
|
||||
}
|
||||
val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember)
|
||||
val roomMemberEntity = RoomMemberEntityFactory.create(
|
||||
roomId,
|
||||
userId,
|
||||
roomMember,
|
||||
// When an update is happening, insertOrUpdate replace existing values with null if they are not provided,
|
||||
// but we want to preserve presence record value and not replace it with null
|
||||
getExistingPresenceState(realm, roomId, userId))
|
||||
realm.insertOrUpdate(roomMemberEntity)
|
||||
if (roomMember.membership.isActive()) {
|
||||
val userEntity = UserEntityFactory.create(userId, roomMember)
|
||||
|
@ -60,7 +69,15 @@ internal class RoomMemberEventHandler @Inject constructor(
|
|||
if (mxId != null && mxId != myUserId) {
|
||||
aggregator?.directChatsToCheck?.put(roomId, mxId)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the already existing presence state for a specific user & room in order NOT to be replaced in RoomMemberSummaryEntity
|
||||
* by NULL value.
|
||||
*/
|
||||
|
||||
private fun getExistingPresenceState(realm: Realm, roomId: String, userId: String): UserPresenceEntity? {
|
||||
return RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst()?.userPresenceEntity
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,13 @@ import org.matrix.android.sdk.internal.database.query.latestEvent
|
|||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendState
|
|||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.database.query.whereRoomId
|
||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.util.Debouncer
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
|
|||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.sync.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
|
||||
internal class DefaultTimelineService @AssistedInject constructor(
|
||||
|
|
|
@ -33,6 +33,12 @@ import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
|
|||
import org.matrix.android.sdk.internal.session.initsync.ProgressReporter
|
||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
||||
import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.CryptoSyncHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.GroupSyncHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.PresenceSyncHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.SyncResponsePostTreatmentAggregatorHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.UserAccountDataSyncHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomSyncHandler
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
|
@ -55,7 +61,9 @@ internal class SyncResponseHandler @Inject constructor(
|
|||
private val cryptoService: DefaultCryptoService,
|
||||
private val tokenStore: SyncTokenStore,
|
||||
private val processEventForPushTask: ProcessEventForPushTask,
|
||||
private val pushRuleService: PushRuleService) {
|
||||
private val pushRuleService: PushRuleService,
|
||||
private val presenceSyncHandler: PresenceSyncHandler
|
||||
) {
|
||||
|
||||
suspend fun handleResponse(syncResponse: SyncResponse,
|
||||
fromToken: String?,
|
||||
|
@ -118,6 +126,13 @@ internal class SyncResponseHandler @Inject constructor(
|
|||
}.also {
|
||||
Timber.v("Finish handling accountData in $it ms")
|
||||
}
|
||||
|
||||
measureTimeMillis {
|
||||
Timber.v("Handle Presence")
|
||||
presenceSyncHandler.handle(realm,syncResponse.presence)
|
||||
}.also {
|
||||
Timber.v("Finish handling Presence in $it ms")
|
||||
}
|
||||
tokenStore.saveToken(realm, syncResponse.nextBatch)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
|
@ -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,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,9 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixPatterns
|
||||
import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
import org.matrix.android.sdk.internal.session.sync.model.accountdata.toMutable
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.Realm
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler.room
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
@ -23,6 +23,8 @@ import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
|
|||
import org.matrix.android.sdk.internal.database.query.createUnmanaged
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler.room
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler.room
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.createObject
|
||||
|
@ -62,6 +62,9 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
|||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
||||
import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent
|
||||
import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
import org.matrix.android.sdk.internal.session.sync.initialSyncStrategy
|
||||
import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler
|
||||
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
||||
import timber.log.Timber
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler.room
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.sync
|
||||
package org.matrix.android.sdk.internal.session.sync.handler.room
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
|
@ -28,8 +28,8 @@ import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityField
|
|||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
|
||||
import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.RoomTagHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomTagHandler
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler,
|
||||
|
|
|
@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.session.user.accountdata
|
|||
import androidx.lifecycle.LiveData
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.UserAccountDataSyncHandler
|
||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent
|
||||
import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource
|
||||
import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.widget.TextView
|
|||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.ui.views.PresenceStateImageView
|
||||
import im.vector.app.core.ui.views.ShieldImageView
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
|
||||
|
@ -31,6 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem<ProfileMatrixItem.Holde
|
|||
open class Holder : VectorEpoxyHolder() {
|
||||
val titleView by bind<TextView>(R.id.matrixItemTitle)
|
||||
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 avatarDecorationImageView by bind<ShieldImageView>(R.id.matrixItemAvatarDecoration)
|
||||
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.roomToolbarTitleView.text = roomSummary.displayName
|
||||
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
|
||||
|
||||
renderSubTitle(typingMessage, roomSummary.topic)
|
||||
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
|
||||
views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,12 +32,14 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
|
|||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import im.vector.app.core.ui.views.PresenceStateImageView
|
||||
import im.vector.app.core.ui.views.ShieldImageView
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_room)
|
||||
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||
|
@ -53,6 +55,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
|
||||
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
||||
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
@EpoxyAttribute var userPresence: UserPresence? = null
|
||||
@EpoxyAttribute var showPresence: Boolean = false
|
||||
@EpoxyAttribute var izPublic: Boolean = false
|
||||
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
||||
|
@ -83,6 +87,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||
renderSelection(holder, showSelected)
|
||||
holder.typingView.setTextOrHide(typingMessage)
|
||||
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
||||
holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
|
@ -117,6 +122,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
|
||||
val roomAvatarPublicDecorationImageView by bind<ImageView>(R.id.roomAvatarPublicDecorationImageView)
|
||||
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
|
||||
val roomAvatarPresenceImageView by bind<PresenceStateImageView>(R.id.roomAvatarPresenceImageView)
|
||||
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
|
||||
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
||||
.izPublic(roomSummary.isPublic)
|
||||
.showPresence(roomSummary.isDirect)
|
||||
.userPresence(roomSummary.directUserPresence)
|
||||
.matrixItem(roomSummary.toMatrixItem())
|
||||
.lastEventTime(latestEventTime)
|
||||
.typingMessage(typingMessage)
|
||||
|
|
|
@ -219,6 +219,7 @@ class RoomProfileFragment @Inject constructor(
|
|||
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
|
||||
headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel)
|
||||
views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
|
||||
headerViews.roomProfilePresenceImageView.render(it.isDirect, it.directUserPresence)
|
||||
}
|
||||
}
|
||||
roomProfileController.setData(state)
|
||||
|
|
|
@ -17,23 +17,29 @@
|
|||
package im.vector.app.features.roomprofile.members
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.dividerItem
|
||||
import im.vector.app.core.epoxy.profiles.buildProfileSection
|
||||
import im.vector.app.core.epoxy.profiles.profileMatrixItem
|
||||
import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPresence
|
||||
import im.vector.app.core.extensions.join
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import me.gujun.android.span.span
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.internal.session.presence.model.UserPresence
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomMemberListController @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val roomMemberSummaryFilter: RoomMemberSummaryFilter
|
||||
) : TypedEpoxyController<RoomMemberListViewState>() {
|
||||
|
||||
|
@ -84,17 +90,10 @@ class RoomMemberListController @Inject constructor(
|
|||
buildProfileSection(
|
||||
stringProvider.getString(powerLevelCategory.titleRes)
|
||||
)
|
||||
|
||||
filteredRoomMemberList.join(
|
||||
each = { _, roomMember ->
|
||||
profileMatrixItem {
|
||||
id(roomMember.userId)
|
||||
matrixItem(roomMember.toMatrixItem())
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
|
||||
clickListener {
|
||||
host.callback?.onRoomMemberClicked(roomMember)
|
||||
}
|
||||
}
|
||||
buildPresence(roomMember, powerLevelCategory, host, data, roomMember.userPresence)
|
||||
},
|
||||
between = { _, roomMemberBefore ->
|
||||
dividerItem {
|
||||
|
@ -123,6 +122,33 @@ class RoomMemberListController @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun buildPresence(roomMember: RoomMemberSummary,
|
||||
powerLevelCategory: RoomMemberListCategories,
|
||||
host: RoomMemberListController,
|
||||
data: RoomMemberListViewState,
|
||||
userPresence: UserPresence?
|
||||
) {
|
||||
val powerLabel = stringProvider.getString(powerLevelCategory.titleRes)
|
||||
|
||||
profileMatrixItemWithPresence {
|
||||
id(roomMember.userId)
|
||||
matrixItem(roomMember.toMatrixItem())
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
|
||||
clickListener {
|
||||
host.callback?.onRoomMemberClicked(roomMember)
|
||||
}
|
||||
userPresence(userPresence)
|
||||
powerLevelLabel(
|
||||
span {
|
||||
span(powerLabel) {
|
||||
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildThreePidInvites(data: RoomMemberListViewState) {
|
||||
val host = this
|
||||
data.threePidInvites()
|
||||
|
|
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
|
||||
android:id="@+id/roomToolbarDecorationImageView"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="17dp"
|
||||
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_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="20dp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
tools:ignore="MissingConstraints"
|
||||
tools:src="@drawable/ic_presence_offline"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomToolbarTitleView"
|
||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
|
@ -65,8 +81,10 @@
|
|||
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
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_goneMarginStart="7dp"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@sample/rooms.json/data/name" />
|
||||
|
||||
<TextView
|
||||
|
@ -74,9 +92,8 @@
|
|||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
|
@ -85,7 +102,8 @@
|
|||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
|
||||
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>
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://tinyurl.com/PresenceListInRooms -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
@ -28,19 +29,35 @@
|
|||
|
||||
<im.vector.app.core.ui.views.ShieldImageView
|
||||
android:id="@+id/matrixItemAvatarDecoration"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="16dp"
|
||||
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_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="16dp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
tools:ignore="MissingConstraints"
|
||||
tools:src="@drawable/ic_presence_offline"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/matrixItemTitle"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
|
@ -48,9 +65,10 @@
|
|||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
||||
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_goneMarginEnd="80dp"
|
||||
app:layout_goneMarginStart="7dp"
|
||||
tools:text="@sample/users.json/data/displayName" />
|
||||
|
||||
<TextView
|
||||
|
@ -58,7 +76,7 @@
|
|||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
|
|
|
@ -84,6 +84,21 @@
|
|||
app:layout_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="28dp"
|
||||
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" />
|
||||
|
||||
<!-- Margin bottom does not work, so I use space -->
|
||||
|
|
|
@ -30,14 +30,33 @@
|
|||
app:layout_constraintVertical_chainStyle="spread_inside"
|
||||
tools:src="@sample/user_round_avatars" />
|
||||
|
||||
<im.vector.app.core.ui.views.ShieldImageView
|
||||
android:id="@+id/memberProfileDecorationImageView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
||||
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||
android:id="@+id/memberProfilePresenceImageView"
|
||||
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_constraintCircleAngle="135"
|
||||
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
|
||||
android:id="@+id/memberProfileNameView"
|
||||
|
@ -48,7 +67,7 @@
|
|||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/memberProfileIdView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/memberProfileDecorationImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/memberProfileAvatarView"
|
||||
tools:text="@sample/users.json/data/displayName" />
|
||||
|
||||
|
|
|
@ -20,25 +20,43 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/room_round_avatars" />
|
||||
|
||||
<im.vector.app.core.ui.views.ShieldImageView
|
||||
android:id="@+id/roomProfileDecorationImageView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
<im.vector.app.core.ui.views.PresenceStateImageView
|
||||
android:id="@+id/roomProfilePresenceImageView"
|
||||
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/roomProfileAvatarView"
|
||||
app:layout_constraintCircleAngle="135"
|
||||
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
|
||||
android:id="@+id/roomProfileNameView"
|
||||
style="@style/Widget.Vector.TextView.Title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/roomProfileAliasView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/roomProfileDecorationImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomProfileAvatarView"
|
||||
tools:text="@sample/rooms.json/data/name" />
|
||||
|
||||
|
|
|
@ -3404,6 +3404,9 @@
|
|||
<string name="a11y_view_read_receipts">View read receipts</string>
|
||||
<string name="a11y_public_room">Public room</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_explore_room_state">Explore Room State</string>
|
||||
|
|
Loading…
Reference in a new issue