Skip to content

Feature/aris/presence #4090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4090.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handle Presence support, for Direct Message room
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions library/ui-styles/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
3 changes: 3 additions & 0 deletions library/ui-styles/src/main/res/values/theme_dark.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
3 changes: 3 additions & 0 deletions library/ui-styles/src/main/res/values/theme_light.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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
Expand Down Expand Up @@ -76,6 +77,7 @@ interface Session :
TermsService,
EventService,
ProfileService,
PresenceService,
PushRuleService,
PushersService,
SyncStatusService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>()
}
Original file line number Diff line number Diff line change
@@ -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.api.session.presence.model.PresenceEnum
import org.matrix.android.sdk.api.session.presence.model.UserPresence

/**
* 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 statusMsg the status message to attach to this state
*/
suspend fun setMyPresence(presence: PresenceEnum, statusMsg: String? = null)

/**
* Fetch the given user's presence state.
* @param userId the userId whose presence state to get.
*/
suspend fun fetchPresence(userId: String): UserPresence

// TODO Add live data (of Flow) of the presence of a userId
}
Original file line number Diff line number Diff line change
@@ -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.api.session.presence.model

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@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 }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.model

data class UserPresence(
val lastActiveAgo: Long? = null,
val statusMessage: String? = null,
val isCurrentlyActive: Boolean? = null,
val presence: PresenceEnum = PresenceEnum.OFFLINE
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package org.matrix.android.sdk.api.session.room.model

import org.matrix.android.sdk.api.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
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.matrix.android.sdk.api.session.room.model

import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.presence.model.UserPresence
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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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) {
Expand Down Expand Up @@ -338,4 +341,27 @@ 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)
?.addField(UserPresenceEntityFields.DISPLAY_NAME, 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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "",
Expand All @@ -40,6 +41,11 @@ internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String =
membershipStr = value.name
}

var userPresenceEntity: UserPresenceEntity? = null
set(value) {
if (value != field) field = value
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it make sense to test != for Realm object, @BillCarsonFr WDYT?

Copy link
Contributor Author

@ariskotsomitopoulos ariskotsomitopoulos Oct 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe it does not make sense, but there are other implementations like this in RoomSummaryEntity:

var latestPreviewableEvent: TimelineEventEntity? = null set(value) { if (value != field) field = value }

var userDrafts: UserDraftsEntity? = null set(value) { if (value != field) field = value }

So if we decide to remove it let's remove it from the other places too, if you agree


fun toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)

companion object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "",
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,6 +65,7 @@ import io.realm.annotations.RealmModule
WellknownIntegrationManagerConfigEntity::class,
RoomAccountDataEntity::class,
SpaceChildSummaryEntity::class,
SpaceParentSummaryEntity::class
SpaceParentSummaryEntity::class,
UserPresenceEntity::class
])
internal class SessionRealmModule
Loading