mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 11:59:12 +03:00
Merge pull request #5869 from vector-im/feature/mna/PSF-884-live-location-aggregation
[Live location sharing] Improve aggregation process of events
This commit is contained in:
commit
b5a0c944d1
25 changed files with 414 additions and 152 deletions
1
changelog.d/5862.wip
Normal file
1
changelog.d/5862.wip
Normal file
|
@ -0,0 +1 @@
|
|||
[Live location sharing] Improve aggregation process of events
|
|
@ -15,9 +15,12 @@
|
|||
*/
|
||||
package org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||
|
||||
data class EventAnnotationsSummary(
|
||||
val reactionsSummary: List<ReactionAggregatedSummary> = emptyList(),
|
||||
val editSummary: EditAggregatedSummary? = null,
|
||||
val pollResponseSummary: PollResponseAggregatedSummary? = null,
|
||||
val referencesAggregatedSummary: ReferencesAggregatedSummary? = null
|
||||
val referencesAggregatedSummary: ReferencesAggregatedSummary? = null,
|
||||
val liveLocationShareAggregatedSummary: LiveLocationShareAggregatedSummary? = null,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.room.model.livelocation
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
|
||||
/**
|
||||
* Aggregation info concerning a live location share.
|
||||
*/
|
||||
data class LiveLocationShareAggregatedSummary(
|
||||
val isActive: Boolean?,
|
||||
val endOfLiveTimestampMillis: Long?,
|
||||
val lastLocationDataContent: MessageBeaconLocationDataContent?,
|
||||
)
|
|
@ -14,25 +14,28 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.model.livelocation
|
||||
package org.matrix.android.sdk.api.session.room.model.message
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
/**
|
||||
* Content of the state event of type
|
||||
* [EventType.STATE_ROOM_BEACON_INFO][org.matrix.android.sdk.api.session.events.model.EventType.STATE_ROOM_BEACON_INFO]
|
||||
*
|
||||
* It contains general info related to a live location share.
|
||||
* Locations are sent in a different message related to the state event.
|
||||
* See [MessageBeaconLocationDataContent][org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent]
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LiveLocationBeaconContent(
|
||||
data class MessageBeaconInfoContent(
|
||||
/**
|
||||
* Local message type, not from server
|
||||
*/
|
||||
@Transient
|
||||
override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION_STATE,
|
||||
override val msgType: String = MessageType.MSGTYPE_BEACON_INFO,
|
||||
|
||||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
|
@ -54,26 +57,16 @@ data class LiveLocationBeaconContent(
|
|||
/**
|
||||
* Beacon creation timestamp.
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampAsMilliseconds: Long? = null,
|
||||
@Json(name = "m.ts") val timestampAsMilliseconds: Long? = null,
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
|
||||
@Json(name = "m.ts") val timestampMillis: Long? = null,
|
||||
/**
|
||||
* Live location asset type.
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset = LocationAsset(LocationAssetType.SELF),
|
||||
@Json(name = "m.asset") val locationAsset: LocationAsset? = null,
|
||||
|
||||
/**
|
||||
* Client side tracking of the last location
|
||||
*/
|
||||
var lastLocationContent: MessageLiveLocationContent? = null,
|
||||
|
||||
/**
|
||||
* Client side tracking of whether the beacon has timed out.
|
||||
*/
|
||||
var hasTimedOut: Boolean = false
|
||||
) : MessageContent {
|
||||
|
||||
fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds
|
||||
fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
|
||||
|
||||
fun getBestLocationAsset() = locationAsset ?: unstableLocationAsset
|
||||
}
|
|
@ -21,13 +21,21 @@ import com.squareup.moshi.JsonClass
|
|||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
/**
|
||||
* Content of the state event of type
|
||||
* [EventType.BEACON_LOCATION_DATA][org.matrix.android.sdk.api.session.events.model.EventType.BEACON_LOCATION_DATA]
|
||||
*
|
||||
* It contains location data related to a live location share.
|
||||
* It is related to the state event that originally started the live.
|
||||
* See [MessageBeaconInfoContent][org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent]
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageLiveLocationContent(
|
||||
data class MessageBeaconLocationDataContent(
|
||||
/**
|
||||
* Local message type, not from server
|
||||
*/
|
||||
@Transient
|
||||
override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION,
|
||||
override val msgType: String = MessageType.MSGTYPE_BEACON_LOCATION_DATA,
|
||||
|
||||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
|
@ -42,11 +50,11 @@ data class MessageLiveLocationContent(
|
|||
/**
|
||||
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampAsMilliseconds: Long? = null,
|
||||
@Json(name = "m.ts") val timestampAsMilliseconds: Long? = null
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
|
||||
@Json(name = "m.ts") val timestampMillis: Long? = null
|
||||
) : MessageContent {
|
||||
|
||||
fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo
|
||||
|
||||
fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds
|
||||
fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
|
||||
}
|
|
@ -49,8 +49,8 @@ data class MessageLocationContent(
|
|||
/**
|
||||
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null,
|
||||
@Json(name = "m.ts") val ts: Long? = null,
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTimestampMillis: Long? = null,
|
||||
@Json(name = "m.ts") val timestampMillis: Long? = null,
|
||||
@Json(name = "org.matrix.msc1767.text") val unstableText: String? = null,
|
||||
@Json(name = "m.text") val text: String? = null,
|
||||
/**
|
||||
|
@ -66,7 +66,7 @@ data class MessageLocationContent(
|
|||
|
||||
fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo
|
||||
|
||||
fun getBestTs() = ts ?: unstableTs
|
||||
fun getBestTimestampMillis() = timestampMillis ?: unstableTimestampMillis
|
||||
|
||||
fun getBestText() = text ?: unstableText
|
||||
|
||||
|
|
|
@ -41,6 +41,6 @@ object MessageType {
|
|||
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
||||
|
||||
// Fake message types for live location events to be able to inherit them from MessageContent
|
||||
const val MSGTYPE_LIVE_LOCATION_STATE = "org.matrix.android.sdk.livelocation.state"
|
||||
const val MSGTYPE_LIVE_LOCATION = "org.matrix.android.sdk.livelocation"
|
||||
const val MSGTYPE_BEACON_INFO = "org.matrix.android.sdk.beacon.info"
|
||||
const val MSGTYPE_BEACON_LOCATION_DATA = "org.matrix.android.sdk.beacon.location.data"
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.isSticker
|
|||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
|
||||
|
@ -139,7 +139,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
|||
return when (root.getClearType()) {
|
||||
EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>()
|
||||
in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>()
|
||||
in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<LiveLocationBeaconContent>()
|
||||
in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageBeaconInfoContent>()
|
||||
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo023
|
|||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo024
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo025
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo026
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo027
|
||||
import org.matrix.android.sdk.internal.util.Normalizer
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
@ -58,7 +59,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
override fun equals(other: Any?) = other is RealmSessionStoreMigration
|
||||
override fun hashCode() = 1000
|
||||
|
||||
val schemaVersion = 26L
|
||||
val schemaVersion = 27L
|
||||
|
||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||
Timber.d("Migrating Realm Session from $oldVersion to $newVersion")
|
||||
|
@ -89,5 +90,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
if (oldVersion < 24) MigrateSessionTo024(realm).perform()
|
||||
if (oldVersion < 25) MigrateSessionTo025(realm).perform()
|
||||
if (oldVersion < 26) MigrateSessionTo026(realm).perform()
|
||||
if (oldVersion < 27) MigrateSessionTo027(realm).perform()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,8 +56,10 @@ internal object EventAnnotationsSummaryMapper {
|
|||
},
|
||||
pollResponseSummary = annotationsSummary.pollResponseSummary?.let {
|
||||
PollResponseAggregatedSummaryEntityMapper.map(it)
|
||||
},
|
||||
liveLocationShareAggregatedSummary = annotationsSummary.liveLocationShareAggregatedSummary?.let {
|
||||
LiveLocationShareAggregatedSummaryMapper.map(it)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.mapper
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
|
||||
internal object LiveLocationShareAggregatedSummaryMapper {
|
||||
|
||||
fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary {
|
||||
return LiveLocationShareAggregatedSummary(
|
||||
isActive = entity.isActive,
|
||||
endOfLiveTimestampMillis = entity.endOfLiveTimestampMillis,
|
||||
lastLocationDataContent = ContentMapper.map(entity.lastLocationContent).toModel<MessageBeaconLocationDataContent>()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.migration
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import io.realm.FieldAttribute
|
||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
||||
|
||||
/**
|
||||
* Migrating to:
|
||||
* Live location sharing aggregated summary
|
||||
*/
|
||||
internal class MigrateSessionTo027(realm: DynamicRealm) : RealmMigrator(realm, 27) {
|
||||
|
||||
override fun doMigrate(realm: DynamicRealm) {
|
||||
val liveLocationSummaryEntity = realm.schema.get("LiveLocationShareAggregatedSummaryEntity")
|
||||
?: realm.schema.create("LiveLocationShareAggregatedSummaryEntity")
|
||||
.addField(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, String::class.java, FieldAttribute.REQUIRED)
|
||||
.addField(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, String::class.java, FieldAttribute.REQUIRED)
|
||||
.addField(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, Boolean::class.java)
|
||||
.setNullable(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true)
|
||||
.addField(LiveLocationShareAggregatedSummaryEntityFields.END_OF_LIVE_TIMESTAMP_MILLIS, Long::class.java)
|
||||
.setNullable(LiveLocationShareAggregatedSummaryEntityFields.END_OF_LIVE_TIMESTAMP_MILLIS, true)
|
||||
.addField(LiveLocationShareAggregatedSummaryEntityFields.LAST_LOCATION_CONTENT, String::class.java)
|
||||
?: return
|
||||
|
||||
realm.schema.get("EventAnnotationsSummaryEntity")
|
||||
?.addRealmObjectField(EventAnnotationsSummaryEntityFields.LIVE_LOCATION_SHARE_AGGREGATED_SUMMARY.`$`, liveLocationSummaryEntity)
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model
|
|||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import timber.log.Timber
|
||||
|
||||
internal open class EventAnnotationsSummaryEntity(
|
||||
|
@ -27,7 +28,8 @@ internal open class EventAnnotationsSummaryEntity(
|
|||
var reactionsSummary: RealmList<ReactionAggregatedSummaryEntity> = RealmList(),
|
||||
var editSummary: EditAggregatedSummaryEntity? = null,
|
||||
var referencesSummaryEntity: ReferencesAggregatedSummaryEntity? = null,
|
||||
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null
|
||||
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null,
|
||||
var liveLocationShareAggregatedSummary: LiveLocationShareAggregatedSummaryEntity? = null,
|
||||
) : RealmObject() {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
|
||||
|
@ -47,6 +48,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
|
|||
EditAggregatedSummaryEntity::class,
|
||||
EditionOfEvent::class,
|
||||
PollResponseAggregatedSummaryEntity::class,
|
||||
LiveLocationShareAggregatedSummaryEntity::class,
|
||||
ReferencesAggregatedSummaryEntity::class,
|
||||
PushRulesEntity::class,
|
||||
PushRuleEntity::class,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.livelocation
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
/**
|
||||
* Aggregation info concerning a live location share.
|
||||
*/
|
||||
internal open class LiveLocationShareAggregatedSummaryEntity(
|
||||
/**
|
||||
* Event id of the event that started the live.
|
||||
*/
|
||||
@PrimaryKey
|
||||
var eventId: String = "",
|
||||
|
||||
var roomId: String = "",
|
||||
|
||||
var isActive: Boolean? = null,
|
||||
|
||||
var endOfLiveTimestampMillis: Long? = null,
|
||||
|
||||
/**
|
||||
* For now we persist this as a JSON for greater flexibility
|
||||
* @see [org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent]
|
||||
*/
|
||||
var lastLocationContent: String? = null,
|
||||
) : RealmObject() {
|
||||
companion object
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.EventAnnotationsSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
|
||||
|
||||
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
|
||||
realm: Realm,
|
||||
roomId: String,
|
||||
eventId: String,
|
||||
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
|
||||
return realm.where<LiveLocationShareAggregatedSummaryEntity>()
|
||||
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, roomId)
|
||||
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId)
|
||||
}
|
||||
|
||||
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.create(
|
||||
realm: Realm,
|
||||
roomId: String,
|
||||
eventId: String,
|
||||
): LiveLocationShareAggregatedSummaryEntity {
|
||||
val obj = realm.createObject(LiveLocationShareAggregatedSummaryEntity::class.java, eventId).apply {
|
||||
this.roomId = roomId
|
||||
}
|
||||
val annotationSummary = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId = roomId, eventId = eventId)
|
||||
annotationSummary.liveLocationShareAggregatedSummary = obj
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.getOrCreate(
|
||||
realm: Realm,
|
||||
roomId: String,
|
||||
eventId: String,
|
||||
): LiveLocationShareAggregatedSummaryEntity {
|
||||
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
?: LiveLocationShareAggregatedSummaryEntity.create(realm, roomId, eventId)
|
||||
}
|
|
@ -33,9 +33,10 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
|||
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||
|
@ -91,7 +92,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
// EventType.KEY_VERIFICATION_READY,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.ENCRYPTED
|
||||
) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + EventType.BEACON_LOCATION_DATA
|
||||
) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + EventType.STATE_ROOM_BEACON_INFO + EventType.BEACON_LOCATION_DATA
|
||||
|
||||
override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean {
|
||||
return allowedTypes.contains(eventType)
|
||||
|
@ -106,12 +107,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
}
|
||||
val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
|
||||
when (event.type) {
|
||||
EventType.REACTION -> {
|
||||
EventType.REACTION -> {
|
||||
// we got a reaction!!
|
||||
Timber.v("###REACTION in room $roomId , reaction eventID ${event.eventId}")
|
||||
handleReaction(realm, event, roomId, isLocalEcho)
|
||||
}
|
||||
EventType.MESSAGE -> {
|
||||
EventType.MESSAGE -> {
|
||||
if (event.unsignedData?.relations?.annotations != null) {
|
||||
Timber.v("###REACTION Aggregation in room $roomId for event ${event.eventId}")
|
||||
handleInitialAggregatedRelations(realm, event, roomId, event.unsignedData.relations.annotations)
|
||||
|
@ -137,7 +138,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
EventType.KEY_VERIFICATION_START,
|
||||
EventType.KEY_VERIFICATION_MAC,
|
||||
EventType.KEY_VERIFICATION_READY,
|
||||
EventType.KEY_VERIFICATION_KEY -> {
|
||||
EventType.KEY_VERIFICATION_KEY -> {
|
||||
Timber.v("## SAS REF in room $roomId for event ${event.eventId}")
|
||||
event.content.toModel<MessageRelationContent>()?.relatesTo?.let {
|
||||
if (it.type == RelationType.REFERENCE && it.eventId != null) {
|
||||
|
@ -146,7 +147,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
EventType.ENCRYPTED -> {
|
||||
EventType.ENCRYPTED -> {
|
||||
// Relation type is in clear
|
||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
||||
if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE ||
|
||||
|
@ -189,8 +190,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
}
|
||||
}
|
||||
in EventType.BEACON_LOCATION_DATA -> {
|
||||
event.content.toModel<MessageLiveLocationContent>(catchError = true)?.let {
|
||||
liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho)
|
||||
event.getClearContent().toModel<MessageBeaconLocationDataContent>(catchError = true)?.let {
|
||||
liveLocationAggregationProcessor.handleBeaconLocationData(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +214,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
// }
|
||||
// }
|
||||
}
|
||||
EventType.REDACTION -> {
|
||||
EventType.REDACTION -> {
|
||||
val eventToPrune = event.redacts?.let { EventEntity.where(realm, eventId = it).findFirst() }
|
||||
?: return
|
||||
when (eventToPrune.type) {
|
||||
|
@ -233,7 +234,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
in EventType.POLL_START -> {
|
||||
in EventType.POLL_START -> {
|
||||
val content: MessagePollContent? = event.content.toModel()
|
||||
if (content?.relatesTo?.type == RelationType.REPLACE) {
|
||||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
||||
|
@ -241,22 +242,22 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
handleReplace(realm, event, content, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
in EventType.POLL_RESPONSE -> {
|
||||
in EventType.POLL_RESPONSE -> {
|
||||
event.content.toModel<MessagePollResponseContent>(catchError = true)?.let {
|
||||
handleResponse(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
in EventType.POLL_END -> {
|
||||
in EventType.POLL_END -> {
|
||||
event.content.toModel<MessageEndPollContent>(catchError = true)?.let {
|
||||
handleEndPoll(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
in EventType.BEACON_LOCATION_DATA -> {
|
||||
event.content.toModel<MessageLiveLocationContent>(catchError = true)?.let {
|
||||
liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho)
|
||||
in EventType.STATE_ROOM_BEACON_INFO -> {
|
||||
event.content.toModel<MessageBeaconInfoContent>(catchError = true)?.let {
|
||||
liveLocationAggregationProcessor.handleBeaconInfo(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
else -> Timber.v("UnHandled event ${event.eventId}")
|
||||
else -> Timber.v("UnHandled event ${event.eventId}")
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t, "## Should not happen ")
|
||||
|
@ -325,7 +326,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
totalVotes = 0,
|
||||
winnerVoteCount = 0,
|
||||
)
|
||||
.toContent())
|
||||
.toContent()
|
||||
)
|
||||
}
|
||||
|
||||
val txId = event.unsignedData?.transactionId
|
||||
|
@ -729,11 +731,13 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
EventType.KEY_VERIFICATION_READY,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.KEY_VERIFICATION_MAC -> currentState.toState(VerificationState.WAITING)
|
||||
EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(if (event.senderId == userId) {
|
||||
VerificationState.CANCELED_BY_ME
|
||||
} else {
|
||||
VerificationState.CANCELED_BY_OTHER
|
||||
})
|
||||
EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(
|
||||
if (event.senderId == userId) {
|
||||
VerificationState.CANCELED_BY_ME
|
||||
} else {
|
||||
VerificationState.CANCELED_BY_OTHER
|
||||
}
|
||||
)
|
||||
EventType.KEY_VERIFICATION_DONE -> currentState.toState(VerificationState.DONE)
|
||||
else -> VerificationState.REQUEST
|
||||
}
|
||||
|
|
|
@ -17,69 +17,78 @@
|
|||
package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.extensions.orTrue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : LiveLocationAggregationProcessor {
|
||||
|
||||
override fun handleLiveLocation(realm: Realm, event: Event, content: MessageLiveLocationContent, roomId: String, isLocalEcho: Boolean) {
|
||||
val locationSenderId = event.senderId ?: return
|
||||
|
||||
// We shouldn't process local echos
|
||||
if (isLocalEcho) {
|
||||
override fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
|
||||
if (event.senderId.isNullOrEmpty() || isLocalEcho) {
|
||||
return
|
||||
}
|
||||
|
||||
// A beacon info state event has to be sent before sending location
|
||||
// TODO handle missing check of m_relatesTo field
|
||||
var beaconInfoEntity: CurrentStateEventEntity? = null
|
||||
val eventTypesIterator = EventType.STATE_ROOM_BEACON_INFO.iterator()
|
||||
while (beaconInfoEntity == null && eventTypesIterator.hasNext()) {
|
||||
beaconInfoEntity = CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, eventTypesIterator.next())
|
||||
}
|
||||
|
||||
if (beaconInfoEntity == null) {
|
||||
Timber.v("## LIVE LOCATION. There is not any beacon info which should be emitted before sending location updates")
|
||||
return
|
||||
}
|
||||
val beaconInfoContent = ContentMapper.map(beaconInfoEntity.root?.content)?.toModel<LiveLocationBeaconContent>(catchError = true)
|
||||
if (beaconInfoContent == null) {
|
||||
Timber.v("## LIVE LOCATION. Beacon info content is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if live location is ended
|
||||
if (!beaconInfoContent.isLive.orFalse()) {
|
||||
Timber.v("## LIVE LOCATION. Beacon info is not live anymore")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if beacon info is outdated
|
||||
if (isBeaconInfoOutdated(beaconInfoContent, content)) {
|
||||
Timber.v("## LIVE LOCATION. Beacon info has timeout")
|
||||
beaconInfoContent.hasTimedOut = true
|
||||
val targetEventId = if (content.isLive.orTrue()) {
|
||||
event.eventId
|
||||
} else {
|
||||
beaconInfoContent.lastLocationContent = content
|
||||
// when live is set to false, we use the id of the event that should have been replaced
|
||||
event.unsignedData?.replacesState
|
||||
}
|
||||
|
||||
beaconInfoEntity.root?.content = ContentMapper.map(beaconInfoContent.toContent())
|
||||
if (targetEventId.isNullOrEmpty()) {
|
||||
Timber.w("no target event id found for the beacon content")
|
||||
return
|
||||
}
|
||||
|
||||
val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
|
||||
realm = realm,
|
||||
roomId = roomId,
|
||||
eventId = targetEventId
|
||||
)
|
||||
|
||||
Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
|
||||
|
||||
aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
|
||||
aggregatedSummary.isActive = content.isLive
|
||||
}
|
||||
|
||||
private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent,
|
||||
liveLocationContent: MessageLiveLocationContent): Boolean {
|
||||
val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0
|
||||
val liveLocationEventTime = liveLocationContent.getBestTimestampAsMilliseconds() ?: 0
|
||||
val timeout = beaconInfoContent.timeout ?: 0
|
||||
return liveLocationEventTime - beaconInfoStartTime > timeout
|
||||
override fun handleBeaconLocationData(realm: Realm, event: Event, content: MessageBeaconLocationDataContent, roomId: String, isLocalEcho: Boolean) {
|
||||
if (event.senderId.isNullOrEmpty() || isLocalEcho) {
|
||||
return
|
||||
}
|
||||
|
||||
val targetEventId = content.relatesTo?.eventId
|
||||
|
||||
if (targetEventId.isNullOrEmpty()) {
|
||||
Timber.w("no target event id found for the live location content")
|
||||
return
|
||||
}
|
||||
|
||||
val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
|
||||
realm = realm,
|
||||
roomId = roomId,
|
||||
eventId = targetEventId
|
||||
)
|
||||
val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
|
||||
val currentLocationTimestamp = ContentMapper
|
||||
.map(aggregatedSummary.lastLocationContent)
|
||||
.toModel<MessageBeaconLocationDataContent>()
|
||||
?.getBestTimestampMillis()
|
||||
?: 0
|
||||
|
||||
if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
|
||||
Timber.d("updating last location of the summary of id=$targetEventId")
|
||||
aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Long.isMoreRecentThan(timestamp: Long) = this > timestamp
|
||||
}
|
||||
|
|
|
@ -18,12 +18,23 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
|
|||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
|
||||
internal interface LiveLocationAggregationProcessor {
|
||||
fun handleLiveLocation(realm: Realm,
|
||||
event: Event,
|
||||
content: MessageLiveLocationContent,
|
||||
roomId: String,
|
||||
isLocalEcho: Boolean)
|
||||
fun handleBeaconInfo(
|
||||
realm: Realm,
|
||||
event: Event,
|
||||
content: MessageBeaconInfoContent,
|
||||
roomId: String,
|
||||
isLocalEcho: Boolean,
|
||||
)
|
||||
|
||||
fun handleBeaconLocationData(
|
||||
realm: Realm,
|
||||
event: Event,
|
||||
content: MessageBeaconLocationDataContent,
|
||||
roomId: String,
|
||||
isLocalEcho: Boolean,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -37,13 +37,13 @@ import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
|
|||
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||
|
@ -71,7 +71,6 @@ import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
|||
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -124,7 +123,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
newBodyAutoMarkdown: Boolean,
|
||||
msgType: String,
|
||||
compatibilityText: String): Event {
|
||||
return createMessageEvent(roomId,
|
||||
return createMessageEvent(
|
||||
roomId,
|
||||
MessageTextContent(
|
||||
msgType = msgType,
|
||||
body = compatibilityText,
|
||||
|
@ -132,7 +132,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
newContent = createTextContent(newBodyText, newBodyAutoMarkdown)
|
||||
.toMessageTextContent(msgType)
|
||||
.toContent()
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPollContent(question: String,
|
||||
|
@ -188,7 +189,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = localId,
|
||||
type = EventType.POLL_RESPONSE.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
}
|
||||
|
||||
fun createPollEvent(roomId: String,
|
||||
|
@ -204,7 +206,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = localId,
|
||||
type = EventType.POLL_START.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
}
|
||||
|
||||
fun createEndPollEvent(roomId: String,
|
||||
|
@ -223,7 +226,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = localId,
|
||||
type = EventType.POLL_END.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
}
|
||||
|
||||
fun createLocationEvent(roomId: String,
|
||||
|
@ -238,7 +242,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
body = geoUri,
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableLocationAsset = LocationAsset(type = assetType),
|
||||
unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
|
||||
unstableTimestampMillis = System.currentTimeMillis(),
|
||||
unstableText = geoUri
|
||||
)
|
||||
return createMessageEvent(roomId, content)
|
||||
|
@ -250,14 +254,14 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
longitude: Double,
|
||||
uncertainty: Double?): Event {
|
||||
val geoUri = buildGeoUri(latitude, longitude, uncertainty)
|
||||
val content = MessageLiveLocationContent(
|
||||
val content = MessageBeaconLocationDataContent(
|
||||
body = geoUri,
|
||||
relatesTo = RelationDefaultContent(
|
||||
type = RelationType.REFERENCE,
|
||||
eventId = beaconInfoEventId
|
||||
),
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableTimestampAsMilliseconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
|
||||
unstableTimestampMillis = System.currentTimeMillis(),
|
||||
)
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
return Event(
|
||||
|
@ -267,7 +271,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = localId,
|
||||
type = EventType.BEACON_LOCATION_DATA.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
}
|
||||
|
||||
fun createReplaceTextOfReply(roomId: String,
|
||||
|
@ -297,7 +302,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
//
|
||||
val replyFallback = buildReplyFallback(body, originalEvent.root.senderId ?: "", newBodyText)
|
||||
|
||||
return createMessageEvent(roomId,
|
||||
return createMessageEvent(
|
||||
roomId,
|
||||
MessageTextContent(
|
||||
msgType = msgType,
|
||||
body = compatibilityText,
|
||||
|
@ -309,7 +315,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
formattedBody = replyFormatted
|
||||
)
|
||||
.toContent()
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun createMediaEvent(roomId: String,
|
||||
|
@ -341,7 +348,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = localId,
|
||||
type = EventType.REACTION,
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createImageEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?): Event {
|
||||
|
@ -532,8 +540,10 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
content.toThreadTextContent(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
||||
msgType = msgType)
|
||||
.toContent())
|
||||
msgType = msgType
|
||||
)
|
||||
.toContent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun dummyOriginServerTs(): Long {
|
||||
|
@ -582,7 +592,9 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
relatesTo = generateReplyRelationContent(
|
||||
eventId = eventId,
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
showInThread = showInThread))
|
||||
showInThread = showInThread
|
||||
)
|
||||
)
|
||||
return createMessageEvent(roomId, content)
|
||||
}
|
||||
|
||||
|
@ -605,7 +617,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
eventId = it,
|
||||
isFallingBack = showInThread,
|
||||
// False when is a rich reply from within a thread, and true when is a reply that should be visible from threads
|
||||
inReplyTo = ReplyToContent(eventId = eventId))
|
||||
inReplyTo = ReplyToContent(eventId = eventId)
|
||||
)
|
||||
} ?: RelationDefaultContent(null, null, ReplyToContent(eventId = eventId))
|
||||
|
||||
private fun buildFormattedReply(permalink: String, userLink: String, userId: String, bodyFormatted: String, newBodyFormatted: String): String {
|
||||
|
@ -740,13 +753,15 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
.toThreadTextContent(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
||||
msgType = MessageType.MSGTYPE_TEXT)
|
||||
msgType = MessageType.MSGTYPE_TEXT
|
||||
)
|
||||
)
|
||||
} else {
|
||||
createFormattedTextEvent(
|
||||
roomId,
|
||||
markdownParser.parse(quoteText, force = true, advanced = autoMarkdown),
|
||||
MessageType.MSGTYPE_TEXT)
|
||||
MessageType.MSGTYPE_TEXT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
|
@ -192,7 +192,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
|
||||
override suspend fun stopLiveLocation(userId: String) {
|
||||
getLiveLocationBeaconInfo(userId, true)?.let { beaconInfoStateEvent ->
|
||||
beaconInfoStateEvent.getClearContent()?.toModel<LiveLocationBeaconContent>()?.let { content ->
|
||||
beaconInfoStateEvent.getClearContent()?.toModel<MessageBeaconInfoContent>()?.let { content ->
|
||||
val updatedContent = content.copy(isLive = false).toContent()
|
||||
|
||||
beaconInfoStateEvent.stateKey?.let {
|
||||
|
@ -217,7 +217,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
}
|
||||
.firstOrNull { beaconInfoEvent ->
|
||||
!filterOnlyLive ||
|
||||
beaconInfoEvent.getClearContent()?.toModel<LiveLocationBeaconContent>()?.isLive.orFalse()
|
||||
beaconInfoEvent.getClearContent()?.toModel<MessageBeaconInfoContent>()?.isLive.orFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
|
|||
import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import javax.inject.Inject
|
||||
|
||||
class LiveLocationMessageItemFactory @Inject constructor(
|
||||
|
@ -34,19 +34,20 @@ class LiveLocationMessageItemFactory @Inject constructor(
|
|||
) {
|
||||
|
||||
fun create(
|
||||
liveLocationContent: LiveLocationBeaconContent,
|
||||
beaconInfoContent: MessageBeaconInfoContent,
|
||||
highlight: Boolean,
|
||||
attributes: AbsMessageItem.Attributes,
|
||||
): VectorEpoxyModel<*>? {
|
||||
// TODO handle location received and stopped states
|
||||
return when {
|
||||
isLiveRunning(liveLocationContent) -> buildStartLiveItem(highlight, attributes)
|
||||
else -> null
|
||||
isLiveRunning(beaconInfoContent) -> buildStartLiveItem(highlight, attributes)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLiveRunning(liveLocationContent: LiveLocationBeaconContent): Boolean {
|
||||
return liveLocationContent.isLive.orFalse() && liveLocationContent.hasTimedOut.not()
|
||||
private fun isLiveRunning(beaconInfoContent: MessageBeaconInfoContent): Boolean {
|
||||
// TODO when we will use aggregatedSummary, check if the live has timed out as well
|
||||
return beaconInfoContent.isLive.orFalse()
|
||||
}
|
||||
|
||||
private fun buildStartLiveItem(
|
||||
|
|
|
@ -98,8 +98,8 @@ import org.matrix.android.sdk.api.session.events.model.RelationType
|
|||
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.isThread
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEmoteContent
|
||||
|
@ -207,15 +207,15 @@ class MessageItemFactory @Inject constructor(
|
|||
is MessageAudioContent -> buildAudioContent(params, messageContent, informationData, highlight, attributes)
|
||||
is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessageLocationContent -> {
|
||||
is MessageLocationContent -> {
|
||||
if (vectorPreferences.labsRenderLocationsInTimeline()) {
|
||||
buildLocationItem(messageContent, informationData, highlight, attributes)
|
||||
} else {
|
||||
buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
|
||||
}
|
||||
}
|
||||
is LiveLocationBeaconContent -> liveLocationMessageItemFactory.create(messageContent, highlight, attributes)
|
||||
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessageBeaconInfoContent -> liveLocationMessageItemFactory.create(messageContent, highlight, attributes)
|
||||
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
}
|
||||
return messageItem?.apply {
|
||||
layout(informationData.messageLayout.layoutRes)
|
||||
|
|
|
@ -59,12 +59,12 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
|
|||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_STICKER_LOCAL,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_LIVE_LOCATION_STATE,
|
||||
MessageType.MSGTYPE_BEACON_INFO,
|
||||
)
|
||||
private val MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE = setOf(
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_LIVE_LOCATION_STATE,
|
||||
MessageType.MSGTYPE_BEACON_INFO,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -151,8 +151,8 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
|
|||
|
||||
private fun MessageContent?.shouldAddMessageOverlay(): Boolean {
|
||||
return when {
|
||||
this == null || msgType == MessageType.MSGTYPE_LIVE_LOCATION_STATE -> false
|
||||
msgType == MessageType.MSGTYPE_LOCATION -> vectorPreferences.labsRenderLocationsInTimeline()
|
||||
this == null || msgType == MessageType.MSGTYPE_BEACON_INFO -> false
|
||||
msgType == MessageType.MSGTYPE_LOCATION -> vectorPreferences.labsRenderLocationsInTimeline()
|
||||
else -> msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.session.Session
|
|||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import timber.log.Timber
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
|
@ -96,10 +96,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
|
|||
}
|
||||
|
||||
private suspend fun sendLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
|
||||
val beaconContent = LiveLocationBeaconContent(
|
||||
val beaconContent = MessageBeaconInfoContent(
|
||||
timeout = roomArgs.durationMillis,
|
||||
isLive = true,
|
||||
unstableTimestampAsMilliseconds = clock.epochMillis()
|
||||
unstableTimestampMillis = clock.epochMillis()
|
||||
).toContent()
|
||||
|
||||
val stateKey = session.myUserId
|
||||
|
|
Loading…
Reference in a new issue