mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Merge branch 'develop' into feature/bma/currentTimeMillis
This commit is contained in:
commit
09e628f227
2
.github/ISSUE_TEMPLATE/release.yml
vendored
2
.github/ISSUE_TEMPLATE/release.yml
vendored
@ -106,7 +106,7 @@ body:
|
||||
|
||||
https://github.com/matrix-org/matrix-android-sdk2-sample
|
||||
|
||||
- [ ] Update the dependency to the new version of the SDK2. It can take some time for MavenCentral to make the librarie available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/
|
||||
- [ ] Update the dependency to the new version of the SDK2. It can take a few minutes for MavenCentral to make the library available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/
|
||||
- [ ] Build and run the sample, you may have to fix some API break
|
||||
- [ ] Commit and push directly on `main`
|
||||
validations:
|
||||
|
@ -124,7 +124,9 @@ As a general rule, please never edit or add or remove translations to the projec
|
||||
|
||||
#### Adding new string
|
||||
|
||||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators using Weblate.
|
||||
When adding new string resources, please only add new entries in the file `value/strings.xml`. Translations will be added later by the community of translators using Weblate.
|
||||
|
||||
The file `value/strings.xml` must only contain American English (U. S. English) values, as this is the default language of the Android operating system. So for instance, please use "color" instead of "colour". Element Android will still use the language set on the system by the user, like any other Android applications which provide translations. The system language can be any other English language variants, or any other languages. Note that this is also possible to override the system language using the Element Android in-app language settings.
|
||||
|
||||
New strings can be added anywhere in the file `value/strings.xml`, not necessarily at the end of the file. Generally, it's even better to add the new strings in some dedicated section per feature, and not at the end of the file, to avoid merge conflict between 2 PR adding strings at the end of the same file.
|
||||
|
||||
|
@ -30,7 +30,7 @@ buildscript {
|
||||
|
||||
// ktlint Plugin
|
||||
plugins {
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.3.0"
|
||||
}
|
||||
|
||||
// https://github.com/jeremylong/DependencyCheck
|
||||
|
1
changelog.d/5421.bugfix
Normal file
1
changelog.d/5421.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fixes crash when accepting or receiving VOIP calls
|
1
changelog.d/5854.doc
Normal file
1
changelog.d/5854.doc
Normal file
@ -0,0 +1 @@
|
||||
Improve documentation of the project and of the SDK
|
1
changelog.d/5855.sdk
Normal file
1
changelog.d/5855.sdk
Normal file
@ -0,0 +1 @@
|
||||
- Add return type to RoomApi.sendStateEvent() to retrieve the created event id
|
1
changelog.d/5858.sdk
Normal file
1
changelog.d/5858.sdk
Normal file
@ -0,0 +1 @@
|
||||
`Room` apis are now available by requesting the service first. For instance `Room.updateAvatar(...)` is now `Room.stateService().updateAvatar(...)`
|
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
|
1
changelog.d/5874.bugfix
Normal file
1
changelog.d/5874.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fixes sign in via other requiring homeserver registration to be enabled
|
1
changelog.d/5890.sdk
Normal file
1
changelog.d/5890.sdk
Normal file
@ -0,0 +1 @@
|
||||
Remove unecessary field `eventId` from `EventAnnotationsSummary` and `ReferencesAggregatedSummary`
|
1
changelog.d/5924.bugfix
Normal file
1
changelog.d/5924.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fix a crash with space invitations in the space list, and do not display space invitation twice.
|
1
changelog.d/5925.bugfix
Normal file
1
changelog.d/5925.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Fixes crash on android api 21/22 devices when opening messages due to Konfetti library
|
@ -21,6 +21,8 @@ import kotlinx.coroutines.flow.Flow
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
@ -45,81 +47,81 @@ class FlowRoom(private val room: Room) {
|
||||
}
|
||||
|
||||
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow<List<RoomMemberSummary>> {
|
||||
return room.getRoomMembersLive(queryParams).asFlow()
|
||||
return room.membershipService().getRoomMembersLive(queryParams).asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getRoomMembers(queryParams)
|
||||
room.membershipService().getRoomMembers(queryParams)
|
||||
}
|
||||
}
|
||||
|
||||
fun liveAnnotationSummary(eventId: String): Flow<Optional<EventAnnotationsSummary>> {
|
||||
return room.getEventAnnotationsSummaryLive(eventId).asFlow()
|
||||
return room.relationService().getEventAnnotationsSummaryLive(eventId).asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getEventAnnotationsSummary(eventId).toOptional()
|
||||
room.relationService().getEventAnnotationsSummary(eventId).toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> {
|
||||
return room.getTimelineEventLive(eventId).asFlow()
|
||||
return room.timelineService().getTimelineEventLive(eventId).asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getTimelineEvent(eventId).toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow<Optional<Event>> {
|
||||
return room.getStateEventLive(eventType, stateKey).asFlow()
|
||||
return room.stateService().getStateEventLive(eventType, stateKey).asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getStateEvent(eventType, stateKey).toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveStateEvents(eventTypes: Set<String>): Flow<List<Event>> {
|
||||
return room.getStateEventsLive(eventTypes).asFlow()
|
||||
return room.stateService().getStateEventsLive(eventTypes).asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getStateEvents(eventTypes)
|
||||
room.stateService().getStateEvents(eventTypes)
|
||||
}
|
||||
}
|
||||
|
||||
fun liveReadMarker(): Flow<Optional<String>> {
|
||||
return room.getReadMarkerLive().asFlow()
|
||||
return room.readService().getReadMarkerLive().asFlow()
|
||||
}
|
||||
|
||||
fun liveReadReceipt(): Flow<Optional<String>> {
|
||||
return room.getMyReadReceiptLive().asFlow()
|
||||
return room.readService().getMyReadReceiptLive().asFlow()
|
||||
}
|
||||
|
||||
fun liveEventReadReceipts(eventId: String): Flow<List<ReadReceipt>> {
|
||||
return room.getEventReadReceiptsLive(eventId).asFlow()
|
||||
return room.readService().getEventReadReceiptsLive(eventId).asFlow()
|
||||
}
|
||||
|
||||
fun liveDraft(): Flow<Optional<UserDraft>> {
|
||||
return room.getDraftLive().asFlow()
|
||||
return room.draftService().getDraftLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getDraft().toOptional()
|
||||
room.draftService().getDraft().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveNotificationState(): Flow<RoomNotificationState> {
|
||||
return room.getLiveRoomNotificationState().asFlow()
|
||||
return room.roomPushRuleService().getLiveRoomNotificationState().asFlow()
|
||||
}
|
||||
|
||||
fun liveThreadSummaries(): Flow<List<ThreadSummary>> {
|
||||
return room.getAllThreadSummariesLive().asFlow()
|
||||
return room.threadsService().getAllThreadSummariesLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getAllThreadSummaries()
|
||||
room.threadsService().getAllThreadSummaries()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.getAllThreadsLive().asFlow()
|
||||
return room.threadsLocalService().getAllThreadsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getAllThreads()
|
||||
room.threadsLocalService().getAllThreads()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveLocalUnreadThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.getMarkedThreadNotificationsLive().asFlow()
|
||||
return room.threadsLocalService().getMarkedThreadNotificationsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.getMarkedThreadNotifications()
|
||||
room.threadsLocalService().getMarkedThreadNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,18 @@ dokkaHtml {
|
||||
configureEach {
|
||||
// Emit warnings about not documented members.
|
||||
reportUndocumented.set(true)
|
||||
// Suppress package legacy riot. Sadly this does not work
|
||||
/*
|
||||
// Suppress legacy Riot's packages.
|
||||
perPackageOption {
|
||||
prefix.set("org.matrix.android.sdk.internal.legacy.riot")
|
||||
matchingRegex.set("org.matrix.android.sdk.internal.legacy.riot")
|
||||
suppress.set(true)
|
||||
}
|
||||
*/
|
||||
perPackageOption {
|
||||
matchingRegex.set("org.matrix.androidsdk.crypto.data")
|
||||
suppress.set(true)
|
||||
}
|
||||
// List of files with module and package documentation
|
||||
// https://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation
|
||||
includes.from("./docs/modules.md", "./docs/packages.md")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
matrix-sdk-android/docs/modules.md
Normal file
18
matrix-sdk-android/docs/modules.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Module matrix-sdk-android
|
||||
|
||||
## Welcome to the matrix-sdk-android documentation!
|
||||
|
||||
This pages list the complete API that this SDK is exposing to a client application.
|
||||
|
||||
*We are still building the documentation, so everything is not documented yet.*
|
||||
|
||||
A few entry points:
|
||||
|
||||
- **Matrix**: The app will have to create and manage a Matrix object.
|
||||
- From this **Matrix** object, you will be able to get various services, including the **AuthenticationService**.
|
||||
- With this **AuthenticationService** you will be able to get an existing **Session**, or create one using a **LoginWizard** or a **RegistrationWizard**, which will finally give you a **Session**.
|
||||
- From the **Session**, you will be able to retrieve many Services, including the **RoomService**.
|
||||
- From the **RoomService**, you will be able to list the rooms, create a **Room**, and get a specific **Room**.
|
||||
- And from a **Room**, you will be able to do many things, including get a **Timeline**, send messages, etc.
|
||||
|
||||
Please read the whole documentation to learn more!
|
3
matrix-sdk-android/docs/packages.md
Normal file
3
matrix-sdk-android/docs/packages.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Package org.matrix.android.sdk.api
|
||||
|
||||
This is the root package of the API exposed by this SDK.
|
@ -146,7 +146,7 @@ class CommonTestHelper(context: Context) {
|
||||
* @param nbOfMessages the number of time the message will be sent
|
||||
*/
|
||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||
val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
|
||||
timeline.start()
|
||||
val sentEvents = sendTextMessagesBatched(timeline, room, message, nbOfMessages, timeout)
|
||||
timeline.dispose()
|
||||
@ -166,12 +166,12 @@ class CommonTestHelper(context: Context) {
|
||||
.forEach { batchedMessages ->
|
||||
batchedMessages.forEach { formattedMessage ->
|
||||
if (rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
replyInThreadText = formattedMessage
|
||||
)
|
||||
} else {
|
||||
room.sendTextMessage(formattedMessage)
|
||||
room.sendService().sendTextMessage(formattedMessage)
|
||||
}
|
||||
}
|
||||
waitWithLatch(timeout) { latch ->
|
||||
@ -216,7 +216,7 @@ class CommonTestHelper(context: Context) {
|
||||
numberOfMessages: Int,
|
||||
rootThreadEventId: String,
|
||||
timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||
val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
|
||||
timeline.start()
|
||||
val sentEvents = sendTextMessagesBatched(timeline, room, message, numberOfMessages, timeout, rootThreadEventId)
|
||||
timeline.dispose()
|
||||
|
@ -70,7 +70,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
if (encryptedRoom) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
val room = aliceSession.getRoom(roomId)!!
|
||||
room.enableEncryption()
|
||||
room.roomCryptoService().enableEncryption()
|
||||
val roomSummaryLive = room.getRoomSummaryLive()
|
||||
val roomSummaryObserver = object : Observer<Optional<RoomSummary>> {
|
||||
override fun onChanged(roomSummary: Optional<RoomSummary>) {
|
||||
@ -109,7 +109,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
}
|
||||
}
|
||||
bobRoomSummariesLive.observeForever(newRoomObserver)
|
||||
aliceRoom.invite(bobSession.myUserId)
|
||||
aliceRoom.membershipService().invite(bobSession.myUserId)
|
||||
}
|
||||
|
||||
testHelper.waitWithLatch { latch ->
|
||||
@ -117,6 +117,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
val roomJoinedObserver = object : Observer<List<RoomSummary>> {
|
||||
override fun onChanged(t: List<RoomSummary>?) {
|
||||
if (bobSession.getRoom(aliceRoomId)
|
||||
?.membershipService()
|
||||
?.getRoomMember(bobSession.myUserId)
|
||||
?.membership == Membership.JOIN) {
|
||||
bobRoomSummariesLive.removeObserver(this)
|
||||
@ -161,7 +162,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
room.invite(samSession.myUserId, null)
|
||||
room.membershipService().invite(samSession.myUserId, null)
|
||||
}
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
@ -261,6 +262,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
val newRoomObserver = object : Observer<List<RoomSummary>> {
|
||||
override fun onChanged(t: List<RoomSummary>?) {
|
||||
if (bob.getRoom(roomId)
|
||||
?.membershipService()
|
||||
?.getRoomMember(bob.myUserId)
|
||||
?.membership == Membership.JOIN) {
|
||||
bobRoomSummariesLive.removeObserver(this)
|
||||
@ -373,13 +375,13 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
val room = aliceSession.getRoom(roomId)!!
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
room.enableEncryption()
|
||||
room.roomCryptoService().enableEncryption()
|
||||
}
|
||||
|
||||
val sessions = mutableListOf(aliceSession)
|
||||
for (index in 1 until numberOfMembers) {
|
||||
val session = testHelper.createAccount("User_$index", defaultSessionParams)
|
||||
testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
|
||||
testHelper.runBlockingTest(timeout = 600_000) { room.membershipService().invite(session.myUserId, null) }
|
||||
println("TEST -> " + session.myUserId + " invited")
|
||||
testHelper.runBlockingTest { session.roomService().joinRoom(room.roomId, null, emptyList()) }
|
||||
println("TEST -> " + session.myUserId + " joined")
|
||||
|
@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.getRoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
@ -88,7 +89,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
otherAccounts.forEach {
|
||||
testHelper.runBlockingTest {
|
||||
Log.v("#E2E TEST", "Alice invites ${it.myUserId}")
|
||||
aliceRoomPOV.invite(it.myUserId)
|
||||
aliceRoomPOV.membershipService().invite(it.myUserId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +131,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
newAccount.forEach {
|
||||
testHelper.runBlockingTest {
|
||||
Log.v("#E2E TEST", "Alice invites ${it.myUserId}")
|
||||
aliceRoomPOV.invite(it.myUserId)
|
||||
aliceRoomPOV.membershipService().invite(it.myUserId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,10 +526,10 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? {
|
||||
aliceRoomPOV.sendTextMessage(text)
|
||||
aliceRoomPOV.sendService().sendTextMessage(text)
|
||||
var sentEventId: String? = null
|
||||
testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch ->
|
||||
val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60))
|
||||
val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60))
|
||||
timeline.start()
|
||||
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
|
@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
|
||||
|
@ -98,7 +98,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||
|
||||
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20))
|
||||
val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(20))
|
||||
bobTimeline.start()
|
||||
|
||||
val bobFinalLatch = CountDownLatch(1)
|
||||
@ -129,7 +129,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||
messagesReceivedByBob = emptyList()
|
||||
|
||||
// - Alice sends a 1st message with a 1st megolm session
|
||||
roomFromAlicePOV.sendTextMessage("First message")
|
||||
roomFromAlicePOV.sendService().sendTextMessage("First message")
|
||||
|
||||
// Wait for the message to be received by Bob
|
||||
testHelper.await(latch)
|
||||
@ -157,7 +157,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||
|
||||
Timber.i("## CRYPTO | testUnwedging: Alice sends a 2nd message with a 2nd megolm session")
|
||||
// - Alice sends a 2nd message with a 2nd megolm session
|
||||
roomFromAlicePOV.sendTextMessage("Second message")
|
||||
roomFromAlicePOV.sendService().sendTextMessage("Second message")
|
||||
|
||||
// Wait for the message to be received by Bob
|
||||
testHelper.await(latch)
|
||||
@ -186,7 +186,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||
|
||||
Timber.i("## CRYPTO | testUnwedging: Alice sends a 3rd message with a 3rd megolm session but a wedged olm session")
|
||||
// - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session
|
||||
roomFromAlicePOV.sendTextMessage("Third message")
|
||||
roomFromAlicePOV.sendService().sendTextMessage("Third message")
|
||||
// Bob should not be able to decrypt, because the session key could not be sent
|
||||
}
|
||||
bobTimeline.removeListener(bobEventsListener)
|
||||
|
@ -49,7 +49,7 @@ class EncryptionTest : InstrumentedTest {
|
||||
fun test_EncryptionEvent() {
|
||||
performTest(roomShouldBeEncrypted = false) { room ->
|
||||
// Send an encryption Event as an Event (and not as a state event)
|
||||
room.sendEvent(
|
||||
room.sendService().sendEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
)
|
||||
@ -61,7 +61,7 @@ class EncryptionTest : InstrumentedTest {
|
||||
performTest(roomShouldBeEncrypted = true) { room ->
|
||||
runBlocking {
|
||||
// Send an encryption Event as a State Event
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
stateKey = "",
|
||||
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
@ -76,9 +76,9 @@ class EncryptionTest : InstrumentedTest {
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
|
||||
|
||||
room.isEncrypted() shouldBe false
|
||||
room.roomCryptoService().isEncrypted() shouldBe false
|
||||
|
||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||
val timeline = room.timelineService().createTimeline(null, TimelineSettings(10))
|
||||
val latch = CountDownLatch(1)
|
||||
val timelineListener = object : Timeline.Listener {
|
||||
override fun onTimelineFailure(throwable: Throwable) {
|
||||
@ -106,7 +106,7 @@ class EncryptionTest : InstrumentedTest {
|
||||
testHelper.await(latch)
|
||||
timeline.dispose()
|
||||
testHelper.waitWithLatch {
|
||||
room.isEncrypted() shouldBe roomShouldBeEncrypted
|
||||
room.roomCryptoService().isEncrypted() shouldBe roomShouldBeEncrypted
|
||||
it.countDown()
|
||||
}
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
|
@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
|
||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
@ -84,7 +85,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
val room = aliceSession.getRoom(roomId)
|
||||
assertNotNull(room)
|
||||
Thread.sleep(4_000)
|
||||
assertTrue(room?.isEncrypted() == true)
|
||||
assertTrue(room?.roomCryptoService()?.isEncrypted() == true)
|
||||
val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
|
||||
|
||||
// Open a new sessionx
|
||||
@ -351,7 +352,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
val roomAlicePov = aliceSession.getRoom(roomId)
|
||||
assertNotNull(roomAlicePov)
|
||||
Thread.sleep(1_000)
|
||||
assertTrue(roomAlicePov?.isEncrypted() == true)
|
||||
assertTrue(roomAlicePov?.roomCryptoService()?.isEncrypted() == true)
|
||||
val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
||||
|
||||
// Create bob session
|
||||
@ -375,7 +376,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
|
||||
// Let alice invite bob
|
||||
commonTestHelper.runBlockingTest {
|
||||
roomAlicePov.invite(bobSession.myUserId, null)
|
||||
roomAlicePov.membershipService().invite(bobSession.myUserId, null)
|
||||
}
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.MockOkHttpInterceptor
|
||||
|
@ -81,7 +81,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
@ -142,7 +142,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
replyInThread.root.getRootThreadEventId().shouldBeEqualTo(initMessage.root.eventId)
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
@ -215,7 +215,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
@ -310,7 +310,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
// The init normal message should now be a root thread event
|
||||
val timeline = aliceRoom.createTimeline(null, TimelineSettings(30))
|
||||
val timeline = aliceRoom.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
timeline.start()
|
||||
|
||||
aliceSession.startSync(true)
|
||||
|
@ -58,10 +58,10 @@ class PollAggregationTest : InstrumentedTest {
|
||||
|
||||
val roomFromBobPOV = cryptoTestData.secondSession!!.getRoom(cryptoTestData.roomId)!!
|
||||
// Bob creates a poll
|
||||
roomFromBobPOV.sendPoll(PollType.DISCLOSED, pollQuestion, pollOptions)
|
||||
roomFromBobPOV.sendService().sendPoll(PollType.DISCLOSED, pollQuestion, pollOptions)
|
||||
|
||||
aliceSession.startSync(true)
|
||||
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
|
||||
val aliceTimeline = roomFromAlicePOV.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
aliceTimeline.start()
|
||||
|
||||
val TOTAL_TEST_COUNT = 7
|
||||
@ -84,37 +84,37 @@ class PollAggregationTest : InstrumentedTest {
|
||||
// Poll has just been created.
|
||||
testInitialPollConditions(pollContent, pollSummary)
|
||||
lock.countDown()
|
||||
roomFromBobPOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
|
||||
roomFromBobPOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
|
||||
}
|
||||
TOTAL_TEST_COUNT - 1 -> {
|
||||
// Bob: Option 1
|
||||
testBobVotesOption1(pollContent, pollSummary)
|
||||
lock.countDown()
|
||||
roomFromBobPOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
roomFromBobPOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
}
|
||||
TOTAL_TEST_COUNT - 2 -> {
|
||||
// Bob: Option 2
|
||||
testBobChangesVoteToOption2(pollContent, pollSummary)
|
||||
lock.countDown()
|
||||
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
}
|
||||
TOTAL_TEST_COUNT - 3 -> {
|
||||
// Alice: Option 2, Bob: Option 2
|
||||
testAliceAndBobVoteToOption2(pollContent, pollSummary)
|
||||
lock.countDown()
|
||||
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
|
||||
roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.firstOrNull()?.id ?: "")
|
||||
}
|
||||
TOTAL_TEST_COUNT - 4 -> {
|
||||
// Alice: Option 1, Bob: Option 2
|
||||
testAliceVotesOption1AndBobVotesOption2(pollContent, pollSummary)
|
||||
lock.countDown()
|
||||
roomFromBobPOV.endPoll(pollEventId)
|
||||
roomFromBobPOV.sendService().endPoll(pollEventId)
|
||||
}
|
||||
TOTAL_TEST_COUNT - 5 -> {
|
||||
// Alice: Option 1, Bob: Option 2 [poll is ended]
|
||||
testEndedPoll(pollSummary)
|
||||
lock.countDown()
|
||||
roomFromAlicePOV.voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
roomFromAlicePOV.sendService().voteToPoll(pollEventId, pollContent.getBestPollCreationInfo()?.answers?.get(1)?.id ?: "")
|
||||
}
|
||||
TOTAL_TEST_COUNT - 6 -> {
|
||||
// Alice: Option 1 (ignore change), Bob: Option 2 [poll is ended]
|
||||
|
@ -75,7 +75,7 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||
|
||||
// Alice clear the cache and restart the sync
|
||||
commonTestHelper.clearCacheAndSync(aliceSession)
|
||||
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
|
||||
val aliceTimeline = roomFromAlicePOV.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
aliceTimeline.start()
|
||||
|
||||
// Alice sees the 10 last message of the room, and can only navigate BACKWARD
|
||||
|
@ -63,7 +63,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
||||
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
|
||||
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
|
||||
|
||||
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
|
||||
val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
bobTimeline.start()
|
||||
|
||||
run {
|
||||
|
@ -66,7 +66,7 @@ class TimelineSimpleBackPaginationTest : InstrumentedTest {
|
||||
message,
|
||||
numberOfMessagesToSent)
|
||||
|
||||
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
|
||||
val bobTimeline = roomFromBobPOV.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
bobTimeline.start()
|
||||
|
||||
commonTestHelper.waitWithLatch(timeout = TestConstants.timeOutMillis * 10) {
|
||||
|
@ -72,7 +72,7 @@ class TimelineWithManyMembersTest : InstrumentedTest {
|
||||
for (index in 1 until cryptoTestData.sessions.size) {
|
||||
val session = cryptoTestData.sessions[index]
|
||||
val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
|
||||
val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30))
|
||||
val timelineForCurrentMember = roomForCurrentMember.timelineService().createTimeline(null, TimelineSettings(30))
|
||||
timelineForCurrentMember.start()
|
||||
|
||||
session.startSync(true)
|
||||
|
@ -29,6 +29,7 @@ import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
|
||||
|
@ -37,6 +37,7 @@ 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.getRoom
|
||||
import org.matrix.android.sdk.api.session.getRoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
@ -499,7 +500,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||
))
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession.getRoom(spaceAInfo.spaceId)!!.invite(bobSession.myUserId, null)
|
||||
aliceSession.getRoom(spaceAInfo.spaceId)!!.membershipService().invite(bobSession.myUserId, null)
|
||||
}
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
@ -509,7 +510,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||
var bobRoomId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
bobRoomId = bobSession.roomService().createRoom(CreateRoomParams().apply { name = "A Bob Room" })
|
||||
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
|
||||
bobSession.getRoom(bobRoomId)!!.membershipService().invite(aliceSession.myUserId)
|
||||
it.countDown()
|
||||
}
|
||||
|
||||
@ -554,7 +555,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
|
||||
?.toContent()
|
||||
|
||||
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
|
||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
|
||||
it.countDown()
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ class RoomMemberCountCondition(
|
||||
// Parse the is field into prefix and number the first time
|
||||
val (prefix, count) = parseIsField() ?: return false
|
||||
|
||||
val numMembers = room.getNumberOfJoinedMembers()
|
||||
val numMembers = room.membershipService().getNumberOfJoinedMembers()
|
||||
|
||||
return when (prefix) {
|
||||
"<" -> numMembers < count
|
||||
|
@ -44,26 +44,7 @@ import org.matrix.android.sdk.api.util.Optional
|
||||
/**
|
||||
* This interface defines methods to interact within a room.
|
||||
*/
|
||||
interface Room :
|
||||
TimelineService,
|
||||
ThreadsService,
|
||||
ThreadsLocalService,
|
||||
SendService,
|
||||
DraftService,
|
||||
ReadService,
|
||||
TypingService,
|
||||
AliasService,
|
||||
TagsService,
|
||||
MembershipService,
|
||||
StateService,
|
||||
UploadsService,
|
||||
ReportingService,
|
||||
RoomCallService,
|
||||
RelationService,
|
||||
RoomCryptoService,
|
||||
RoomPushRuleService,
|
||||
RoomAccountDataService,
|
||||
RoomVersionService {
|
||||
interface Room {
|
||||
|
||||
val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
|
||||
@ -87,4 +68,99 @@ interface Room :
|
||||
* Use this room as a Space, if the type is correct.
|
||||
*/
|
||||
fun asSpace(): Space?
|
||||
|
||||
/**
|
||||
* Get the TimelineService associated to this Room
|
||||
*/
|
||||
fun timelineService(): TimelineService
|
||||
|
||||
/**
|
||||
* Get the ThreadsService associated to this Room
|
||||
*/
|
||||
fun threadsService(): ThreadsService
|
||||
|
||||
/**
|
||||
* Get the ThreadsLocalService associated to this Room
|
||||
*/
|
||||
fun threadsLocalService(): ThreadsLocalService
|
||||
|
||||
/**
|
||||
* Get the SendService associated to this Room
|
||||
*/
|
||||
fun sendService(): SendService
|
||||
|
||||
/**
|
||||
* Get the DraftService associated to this Room
|
||||
*/
|
||||
fun draftService(): DraftService
|
||||
|
||||
/**
|
||||
* Get the ReadService associated to this Room
|
||||
*/
|
||||
fun readService(): ReadService
|
||||
|
||||
/**
|
||||
* Get the TypingService associated to this Room
|
||||
*/
|
||||
fun typingService(): TypingService
|
||||
|
||||
/**
|
||||
* Get the AliasService associated to this Room
|
||||
*/
|
||||
fun aliasService(): AliasService
|
||||
|
||||
/**
|
||||
* Get the TagsService associated to this Room
|
||||
*/
|
||||
fun tagsService(): TagsService
|
||||
|
||||
/**
|
||||
* Get the MembershipService associated to this Room
|
||||
*/
|
||||
fun membershipService(): MembershipService
|
||||
|
||||
/**
|
||||
* Get the StateService associated to this Room
|
||||
*/
|
||||
fun stateService(): StateService
|
||||
|
||||
/**
|
||||
* Get the UploadsService associated to this Room
|
||||
*/
|
||||
fun uploadsService(): UploadsService
|
||||
|
||||
/**
|
||||
* Get the ReportingService associated to this Room
|
||||
*/
|
||||
fun reportingService(): ReportingService
|
||||
|
||||
/**
|
||||
* Get the RoomCallService associated to this Room
|
||||
*/
|
||||
fun roomCallService(): RoomCallService
|
||||
|
||||
/**
|
||||
* Get the RelationService associated to this Room
|
||||
*/
|
||||
fun relationService(): RelationService
|
||||
|
||||
/**
|
||||
* Get the RoomCryptoService associated to this Room
|
||||
*/
|
||||
fun roomCryptoService(): RoomCryptoService
|
||||
|
||||
/**
|
||||
* Get the RoomPushRuleService associated to this Room
|
||||
*/
|
||||
fun roomPushRuleService(): RoomPushRuleService
|
||||
|
||||
/**
|
||||
* Get the RoomAccountDataService associated to this Room
|
||||
*/
|
||||
fun roomAccountDataService(): RoomAccountDataService
|
||||
|
||||
/**
|
||||
* Get the RoomVersionService associated to this Room
|
||||
*/
|
||||
fun roomVersionService(): RoomVersionService
|
||||
}
|
||||
|
@ -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.api.session.room
|
||||
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
||||
/**
|
||||
* Get a TimelineEvent using the TimelineService of a Room
|
||||
*/
|
||||
fun Room.getTimelineEvent(eventId: String): TimelineEvent? =
|
||||
timelineService().getTimelineEvent(eventId)
|
||||
|
||||
/**
|
||||
* Get a StateEvent using the StateService of a Room
|
||||
*/
|
||||
fun Room.getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? =
|
||||
stateService().getStateEvent(eventType, stateKey)
|
@ -15,10 +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 eventId: String,
|
||||
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,
|
||||
)
|
||||
|
@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.events.model.Content
|
||||
* of all events that are referencing the 'eventId' event via the RelationType.REFERENCE
|
||||
*/
|
||||
data class ReferencesAggregatedSummary(
|
||||
val eventId: String,
|
||||
val content: Content?,
|
||||
val sourceEvents: List<String>,
|
||||
val localEchos: List<String>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -84,8 +84,9 @@ interface StateService {
|
||||
* @param eventType The type of event to send.
|
||||
* @param stateKey The state_key for the state to send. Can be an empty string.
|
||||
* @param body The content object of the event; the fields in this object will vary depending on the type of event
|
||||
* @return the id of the created state event
|
||||
*/
|
||||
suspend fun sendStateEvent(eventType: String, stateKey: String, body: JsonDict)
|
||||
suspend fun sendStateEvent(eventType: String, stateKey: String, body: JsonDict): String
|
||||
|
||||
/**
|
||||
* Get a state event of the room
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ package org.matrix.android.sdk.internal.database
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmResults
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
@ -38,10 +41,22 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
|
||||
it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true)
|
||||
}
|
||||
|
||||
private val onResultsChangedFlow = MutableSharedFlow<RealmResults<EventInsertEntity>>()
|
||||
|
||||
init {
|
||||
onResultsChangedFlow
|
||||
.onEach { handleChange(it) }
|
||||
.launchIn(observerScope)
|
||||
}
|
||||
|
||||
override fun onChange(results: RealmResults<EventInsertEntity>) {
|
||||
if (!results.isLoaded || results.isEmpty()) {
|
||||
return
|
||||
}
|
||||
observerScope.launch { onResultsChangedFlow.emit(results) }
|
||||
}
|
||||
|
||||
private suspend fun handleChange(results: RealmResults<EventInsertEntity>) {
|
||||
val idsToDeleteAfterProcess = ArrayList<String>()
|
||||
val filteredEvents = ArrayList<EventInsertEntity>(results.size)
|
||||
Timber.v("EventInsertEntity updated with ${results.size} results in db")
|
||||
@ -58,30 +73,29 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
|
||||
}
|
||||
idsToDeleteAfterProcess.add(it.eventId)
|
||||
}
|
||||
observerScope.launch {
|
||||
awaitTransaction(realmConfiguration) { realm ->
|
||||
Timber.v("##Transaction: There are ${filteredEvents.size} events to process ")
|
||||
filteredEvents.forEach { eventInsert ->
|
||||
val eventId = eventInsert.eventId
|
||||
val event = EventEntity.where(realm, eventId).findFirst()
|
||||
if (event == null) {
|
||||
Timber.v("Event $eventId not found")
|
||||
return@forEach
|
||||
}
|
||||
val domainEvent = event.asDomain()
|
||||
processors.filter {
|
||||
it.shouldProcess(eventId, domainEvent.getClearType(), eventInsert.insertType)
|
||||
}.forEach {
|
||||
it.process(realm, domainEvent)
|
||||
}
|
||||
|
||||
awaitTransaction(realmConfiguration) { realm ->
|
||||
Timber.v("##Transaction: There are ${filteredEvents.size} events to process ")
|
||||
filteredEvents.forEach { eventInsert ->
|
||||
val eventId = eventInsert.eventId
|
||||
val event = EventEntity.where(realm, eventId).findFirst()
|
||||
if (event == null) {
|
||||
Timber.v("Event $eventId not found")
|
||||
return@forEach
|
||||
}
|
||||
val domainEvent = event.asDomain()
|
||||
processors.filter {
|
||||
it.shouldProcess(eventId, domainEvent.getClearType(), eventInsert.insertType)
|
||||
}.forEach {
|
||||
it.process(realm, domainEvent)
|
||||
}
|
||||
realm.where(EventInsertEntity::class.java)
|
||||
.`in`(EventInsertEntityFields.EVENT_ID, idsToDeleteAfterProcess.toTypedArray())
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
processors.forEach { it.onPostProcess() }
|
||||
realm.where(EventInsertEntity::class.java)
|
||||
.`in`(EventInsertEntityFields.EVENT_ID, idsToDeleteAfterProcess.toTypedArray())
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
processors.forEach { it.onPostProcess() }
|
||||
}
|
||||
|
||||
private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEnt
|
||||
internal object EventAnnotationsSummaryMapper {
|
||||
fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary {
|
||||
return EventAnnotationsSummary(
|
||||
eventId = annotationsSummary.eventId,
|
||||
reactionsSummary = annotationsSummary.reactionsSummary.toList().map {
|
||||
ReactionAggregatedSummary(
|
||||
it.key,
|
||||
@ -50,7 +49,6 @@ internal object EventAnnotationsSummaryMapper {
|
||||
},
|
||||
referencesAggregatedSummary = annotationsSummary.referencesSummaryEntity?.let {
|
||||
ReferencesAggregatedSummary(
|
||||
it.eventId,
|
||||
ContentMapper.map(it.content),
|
||||
it.sourceEvents.toList(),
|
||||
it.sourceLocalEcho.toList()
|
||||
@ -58,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)
|
||||
}
|
@ -72,6 +72,7 @@ internal class ViaParameterFinder @Inject constructor(
|
||||
*/
|
||||
private fun getUserIdsOfJoinedMembers(roomId: String): Set<String> {
|
||||
return roomGetterProvider.get().getRoom(roomId)
|
||||
?.membershipService()
|
||||
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
|
||||
?.map { it.userId }
|
||||
.orEmpty()
|
||||
@ -84,6 +85,7 @@ internal class ViaParameterFinder @Inject constructor(
|
||||
// It may not be possible for a user to join a room if there's no overlap between these
|
||||
fun computeViaParamsForRestricted(roomId: String, max: Int): List<String> {
|
||||
val userThatCanInvite = roomGetterProvider.get().getRoom(roomId)
|
||||
?.membershipService()
|
||||
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
|
||||
?.map { it.userId }
|
||||
?.filter { userCanInvite(userId, roomId) }
|
||||
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition
|
||||
import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition
|
||||
import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition
|
||||
import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||
@ -60,7 +61,7 @@ internal class DefaultConditionResolver @Inject constructor(
|
||||
condition: ContainsDisplayNameCondition): Boolean {
|
||||
val roomId = event.roomId ?: return false
|
||||
val room = roomGetter.getRoom(roomId) ?: return false
|
||||
val myDisplayName = room.getRoomMember(userId)?.displayName ?: return false
|
||||
val myDisplayName = room.membershipService().getRoomMember(userId)?.displayName ?: return false
|
||||
return condition.isSatisfied(event, myDisplayName)
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,11 @@ package org.matrix.android.sdk.internal.session.room
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataService
|
||||
import org.matrix.android.sdk.api.session.room.alias.AliasService
|
||||
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
||||
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
@ -44,57 +42,35 @@ import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.awaitCallback
|
||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
||||
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.space.DefaultSpace
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
internal class DefaultRoom(override val roomId: String,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val timelineService: TimelineService,
|
||||
private val threadsService: ThreadsService,
|
||||
private val threadsLocalService: ThreadsLocalService,
|
||||
private val sendService: SendService,
|
||||
private val draftService: DraftService,
|
||||
private val stateService: StateService,
|
||||
private val uploadsService: UploadsService,
|
||||
private val reportingService: ReportingService,
|
||||
private val roomCallService: RoomCallService,
|
||||
private val readService: ReadService,
|
||||
private val typingService: TypingService,
|
||||
private val aliasService: AliasService,
|
||||
private val tagsService: TagsService,
|
||||
private val cryptoService: CryptoService,
|
||||
private val relationService: RelationService,
|
||||
private val roomMembersService: MembershipService,
|
||||
private val roomPushRuleService: RoomPushRuleService,
|
||||
private val roomAccountDataService: RoomAccountDataService,
|
||||
private val roomVersionService: RoomVersionService,
|
||||
private val sendStateTask: SendStateTask,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
override val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) :
|
||||
Room,
|
||||
TimelineService by timelineService,
|
||||
ThreadsService by threadsService,
|
||||
ThreadsLocalService by threadsLocalService,
|
||||
SendService by sendService,
|
||||
DraftService by draftService,
|
||||
StateService by stateService,
|
||||
UploadsService by uploadsService,
|
||||
ReportingService by reportingService,
|
||||
RoomCallService by roomCallService,
|
||||
ReadService by readService,
|
||||
TypingService by typingService,
|
||||
AliasService by aliasService,
|
||||
TagsService by tagsService,
|
||||
RelationService by relationService,
|
||||
MembershipService by roomMembersService,
|
||||
RoomPushRuleService by roomPushRuleService,
|
||||
RoomAccountDataService by roomAccountDataService,
|
||||
RoomVersionService by roomVersionService {
|
||||
internal class DefaultRoom(
|
||||
override val roomId: String,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val roomCryptoService: RoomCryptoService,
|
||||
private val timelineService: TimelineService,
|
||||
private val threadsService: ThreadsService,
|
||||
private val threadsLocalService: ThreadsLocalService,
|
||||
private val sendService: SendService,
|
||||
private val draftService: DraftService,
|
||||
private val stateService: StateService,
|
||||
private val uploadsService: UploadsService,
|
||||
private val reportingService: ReportingService,
|
||||
private val roomCallService: RoomCallService,
|
||||
private val readService: ReadService,
|
||||
private val typingService: TypingService,
|
||||
private val aliasService: AliasService,
|
||||
private val tagsService: TagsService,
|
||||
private val relationService: RelationService,
|
||||
private val roomMembersService: MembershipService,
|
||||
private val roomPushRuleService: RoomPushRuleService,
|
||||
private val roomAccountDataService: RoomAccountDataService,
|
||||
private val roomVersionService: RoomVersionService,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
override val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) : Room {
|
||||
|
||||
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||
return roomSummaryDataSource.getRoomSummaryLive(roomId)
|
||||
@ -104,49 +80,28 @@ internal class DefaultRoom(override val roomId: String,
|
||||
return roomSummaryDataSource.getRoomSummary(roomId)
|
||||
}
|
||||
|
||||
override fun isEncrypted(): Boolean {
|
||||
return cryptoService.isRoomEncrypted(roomId)
|
||||
}
|
||||
|
||||
override fun encryptionAlgorithm(): String? {
|
||||
return cryptoService.getEncryptionAlgorithm(roomId)
|
||||
}
|
||||
|
||||
override fun shouldEncryptForInvitedMembers(): Boolean {
|
||||
return cryptoService.shouldEncryptForInvitedMembers(roomId)
|
||||
}
|
||||
|
||||
override suspend fun prepareToEncrypt() {
|
||||
awaitCallback<Unit> {
|
||||
cryptoService.prepareToEncrypt(roomId, it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun enableEncryption(algorithm: String, force: Boolean) {
|
||||
when {
|
||||
(!force && isEncrypted() && encryptionAlgorithm() == MXCRYPTO_ALGORITHM_MEGOLM) -> {
|
||||
throw IllegalStateException("Encryption is already enabled for this room")
|
||||
}
|
||||
(!force && algorithm != MXCRYPTO_ALGORITHM_MEGOLM) -> {
|
||||
throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")
|
||||
}
|
||||
else -> {
|
||||
val params = SendStateTask.Params(
|
||||
roomId = roomId,
|
||||
stateKey = "",
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
body = mapOf(
|
||||
"algorithm" to algorithm
|
||||
)
|
||||
)
|
||||
|
||||
sendStateTask.execute(params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun asSpace(): Space? {
|
||||
if (roomSummary()?.roomType != RoomType.SPACE) return null
|
||||
return DefaultSpace(this, roomSummaryDataSource, viaParameterFinder)
|
||||
}
|
||||
|
||||
override fun timelineService() = timelineService
|
||||
override fun threadsService() = threadsService
|
||||
override fun threadsLocalService() = threadsLocalService
|
||||
override fun sendService() = sendService
|
||||
override fun draftService() = draftService
|
||||
override fun stateService() = stateService
|
||||
override fun uploadsService() = uploadsService
|
||||
override fun reportingService() = reportingService
|
||||
override fun roomCallService() = roomCallService
|
||||
override fun readService() = readService
|
||||
override fun typingService() = typingService
|
||||
override fun aliasService() = aliasService
|
||||
override fun tagsService() = tagsService
|
||||
override fun relationService() = relationService
|
||||
override fun roomCryptoService() = roomCryptoService
|
||||
override fun membershipService() = roomMembersService
|
||||
override fun roomPushRuleService() = roomPushRuleService
|
||||
override fun roomAccountDataService() = roomAccountDataService
|
||||
override fun roomVersionService() = roomVersionService
|
||||
}
|
||||
|
@ -28,14 +28,16 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
||||
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||
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.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
|
||||
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
|
||||
@ -93,7 +95,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)
|
||||
@ -108,12 +110,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)
|
||||
@ -139,7 +141,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) {
|
||||
@ -148,7 +150,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 ||
|
||||
@ -191,8 +193,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,7 +217,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) {
|
||||
@ -235,7 +237,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}")
|
||||
@ -243,22 +245,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 ")
|
||||
|
@ -194,7 +194,8 @@ internal interface RoomAPI {
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}")
|
||||
suspend fun sendStateEvent(@Path("roomId") roomId: String,
|
||||
@Path("state_event_type") stateEventType: String,
|
||||
@Body params: JsonDict)
|
||||
@Body params: JsonDict
|
||||
): SendResponse
|
||||
|
||||
/**
|
||||
* Send a generic state event
|
||||
@ -208,7 +209,8 @@ internal interface RoomAPI {
|
||||
suspend fun sendStateEvent(@Path("roomId") roomId: String,
|
||||
@Path("state_event_type") stateEventType: String,
|
||||
@Path("state_key") stateKey: String,
|
||||
@Body params: JsonDict)
|
||||
@Body params: JsonDict
|
||||
): SendResponse
|
||||
|
||||
/**
|
||||
* Get state events of a room
|
||||
|
@ -17,13 +17,13 @@
|
||||
package org.matrix.android.sdk.internal.session.room
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
||||
import org.matrix.android.sdk.internal.session.room.accountdata.DefaultRoomAccountDataService
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService
|
||||
import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService
|
||||
import org.matrix.android.sdk.internal.session.room.crypto.DefaultRoomCryptoService
|
||||
import org.matrix.android.sdk.internal.session.room.draft.DefaultDraftService
|
||||
import org.matrix.android.sdk.internal.session.room.membership.DefaultMembershipService
|
||||
import org.matrix.android.sdk.internal.session.room.notification.DefaultRoomPushRuleService
|
||||
@ -32,7 +32,6 @@ import org.matrix.android.sdk.internal.session.room.relation.DefaultRelationServ
|
||||
import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportingService
|
||||
import org.matrix.android.sdk.internal.session.room.send.DefaultSendService
|
||||
import org.matrix.android.sdk.internal.session.room.state.DefaultStateService
|
||||
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.room.tags.DefaultTagsService
|
||||
import org.matrix.android.sdk.internal.session.room.threads.DefaultThreadsService
|
||||
@ -48,35 +47,36 @@ internal interface RoomFactory {
|
||||
}
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultRoomFactory @Inject constructor(private val cryptoService: CryptoService,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val timelineServiceFactory: DefaultTimelineService.Factory,
|
||||
private val threadsServiceFactory: DefaultThreadsService.Factory,
|
||||
private val threadsLocalServiceFactory: DefaultThreadsLocalService.Factory,
|
||||
private val sendServiceFactory: DefaultSendService.Factory,
|
||||
private val draftServiceFactory: DefaultDraftService.Factory,
|
||||
private val stateServiceFactory: DefaultStateService.Factory,
|
||||
private val uploadsServiceFactory: DefaultUploadsService.Factory,
|
||||
private val reportingServiceFactory: DefaultReportingService.Factory,
|
||||
private val roomCallServiceFactory: DefaultRoomCallService.Factory,
|
||||
private val readServiceFactory: DefaultReadService.Factory,
|
||||
private val typingServiceFactory: DefaultTypingService.Factory,
|
||||
private val aliasServiceFactory: DefaultAliasService.Factory,
|
||||
private val tagsServiceFactory: DefaultTagsService.Factory,
|
||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
|
||||
private val roomVersionServiceFactory: DefaultRoomVersionService.Factory,
|
||||
private val roomAccountDataServiceFactory: DefaultRoomAccountDataService.Factory,
|
||||
private val sendStateTask: SendStateTask,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) :
|
||||
RoomFactory {
|
||||
internal class DefaultRoomFactory @Inject constructor(
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val timelineServiceFactory: DefaultTimelineService.Factory,
|
||||
private val threadsServiceFactory: DefaultThreadsService.Factory,
|
||||
private val threadsLocalServiceFactory: DefaultThreadsLocalService.Factory,
|
||||
private val sendServiceFactory: DefaultSendService.Factory,
|
||||
private val draftServiceFactory: DefaultDraftService.Factory,
|
||||
private val stateServiceFactory: DefaultStateService.Factory,
|
||||
private val uploadsServiceFactory: DefaultUploadsService.Factory,
|
||||
private val reportingServiceFactory: DefaultReportingService.Factory,
|
||||
private val roomCallServiceFactory: DefaultRoomCallService.Factory,
|
||||
private val readServiceFactory: DefaultReadService.Factory,
|
||||
private val typingServiceFactory: DefaultTypingService.Factory,
|
||||
private val aliasServiceFactory: DefaultAliasService.Factory,
|
||||
private val tagsServiceFactory: DefaultTagsService.Factory,
|
||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||
private val roomCryptoServiceFactory: DefaultRoomCryptoService.Factory,
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
|
||||
private val roomVersionServiceFactory: DefaultRoomVersionService.Factory,
|
||||
private val roomAccountDataServiceFactory: DefaultRoomAccountDataService.Factory,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) : RoomFactory {
|
||||
|
||||
override fun create(roomId: String): Room {
|
||||
return DefaultRoom(
|
||||
roomId = roomId,
|
||||
roomSummaryDataSource = roomSummaryDataSource,
|
||||
roomCryptoService = roomCryptoServiceFactory.create(roomId),
|
||||
timelineService = timelineServiceFactory.create(roomId),
|
||||
threadsService = threadsServiceFactory.create(roomId),
|
||||
threadsLocalService = threadsLocalServiceFactory.create(roomId),
|
||||
@ -90,13 +90,11 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
||||
typingService = typingServiceFactory.create(roomId),
|
||||
aliasService = aliasServiceFactory.create(roomId),
|
||||
tagsService = tagsServiceFactory.create(roomId),
|
||||
cryptoService = cryptoService,
|
||||
relationService = relationServiceFactory.create(roomId),
|
||||
roomMembersService = membershipServiceFactory.create(roomId),
|
||||
roomPushRuleService = roomPushRuleServiceFactory.create(roomId),
|
||||
roomAccountDataService = roomAccountDataServiceFactory.create(roomId),
|
||||
roomVersionService = roomVersionServiceFactory.create(roomId),
|
||||
sendStateTask = sendStateTask,
|
||||
viaParameterFinder = viaParameterFinder,
|
||||
coroutineDispatchers = coroutineDispatchers
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.session.room.crypto
|
||||
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
||||
import org.matrix.android.sdk.api.util.awaitCallback
|
||||
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
internal class DefaultRoomCryptoService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
private val cryptoService: CryptoService,
|
||||
private val sendStateTask: SendStateTask,
|
||||
) : RoomCryptoService {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(roomId: String): DefaultRoomCryptoService
|
||||
}
|
||||
|
||||
override fun isEncrypted(): Boolean {
|
||||
return cryptoService.isRoomEncrypted(roomId)
|
||||
}
|
||||
|
||||
override fun encryptionAlgorithm(): String? {
|
||||
return cryptoService.getEncryptionAlgorithm(roomId)
|
||||
}
|
||||
|
||||
override fun shouldEncryptForInvitedMembers(): Boolean {
|
||||
return cryptoService.shouldEncryptForInvitedMembers(roomId)
|
||||
}
|
||||
|
||||
override suspend fun prepareToEncrypt() {
|
||||
awaitCallback<Unit> {
|
||||
cryptoService.prepareToEncrypt(roomId, it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun enableEncryption(algorithm: String, force: Boolean) {
|
||||
when {
|
||||
(!force && isEncrypted() && encryptionAlgorithm() == MXCRYPTO_ALGORITHM_MEGOLM) -> {
|
||||
throw IllegalStateException("Encryption is already enabled for this room")
|
||||
}
|
||||
(!force && algorithm != MXCRYPTO_ALGORITHM_MEGOLM) -> {
|
||||
throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")
|
||||
}
|
||||
else -> {
|
||||
val params = SendStateTask.Params(
|
||||
roomId = roomId,
|
||||
stateKey = "",
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
body = mapOf(
|
||||
"algorithm" to algorithm
|
||||
)
|
||||
)
|
||||
|
||||
sendStateTask.execute(params)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -72,7 +72,6 @@ import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@ -245,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
body = geoUri,
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableLocationAsset = LocationAsset(type = assetType),
|
||||
unstableTs = TimeUnit.MILLISECONDS.toSeconds(clock.epochMillis()),
|
||||
unstableTimestampMillis = clock.epochMillis(),
|
||||
unstableText = geoUri
|
||||
)
|
||||
return createMessageEvent(roomId, content)
|
||||
@ -257,14 +256,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(clock.epochMillis()),
|
||||
unstableTimestampMillis = clock.epochMillis(),
|
||||
)
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
return Event(
|
||||
|
@ -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
|
||||
@ -73,14 +73,14 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
||||
eventType: String,
|
||||
stateKey: String,
|
||||
body: JsonDict
|
||||
) {
|
||||
): String {
|
||||
val params = SendStateTask.Params(
|
||||
roomId = roomId,
|
||||
stateKey = stateKey,
|
||||
eventType = eventType,
|
||||
body = body.toSafeJson(eventType)
|
||||
)
|
||||
sendStateTask.executeRetry(params, 3)
|
||||
return sendStateTask.executeRetry(params, 3)
|
||||
}
|
||||
|
||||
private fun JsonDict.toSafeJson(eventType: String): JsonDict {
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,10 @@ 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.task.Task
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SendStateTask : Task<SendStateTask.Params, Unit> {
|
||||
internal interface SendStateTask : Task<SendStateTask.Params, String> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val stateKey: String,
|
||||
@ -37,9 +38,9 @@ internal class DefaultSendStateTask @Inject constructor(
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : SendStateTask {
|
||||
|
||||
override suspend fun execute(params: SendStateTask.Params) {
|
||||
override suspend fun execute(params: SendStateTask.Params): String {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
if (params.stateKey.isEmpty()) {
|
||||
val response = if (params.stateKey.isEmpty()) {
|
||||
roomAPI.sendStateEvent(
|
||||
roomId = params.roomId,
|
||||
stateEventType = params.eventType,
|
||||
@ -53,6 +54,9 @@ internal class DefaultSendStateTask @Inject constructor(
|
||||
params = params.body
|
||||
)
|
||||
}
|
||||
response.eventId.also {
|
||||
Timber.d("State event: $it just sent in room ${params.roomId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||
import org.matrix.android.sdk.api.query.isNormalized
|
||||
@ -43,7 +42,6 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
@ -57,10 +55,8 @@ import javax.inject.Inject
|
||||
|
||||
internal class RoomSummaryDataSource @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val realmSessionProvider: RealmSessionProvider,
|
||||
private val roomSummaryMapper: RoomSummaryMapper,
|
||||
private val queryStringValueProcessor: QueryStringValueProcessor,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||
) {
|
||||
|
||||
fun getRoomSummary(roomIdOrAlias: String): RoomSummary? {
|
||||
|
@ -102,9 +102,7 @@ internal class UIEchoManager(
|
||||
val relatedEventID = timelineEvent.eventId
|
||||
val contents = inMemoryReactions[relatedEventID] ?: return timelineEvent
|
||||
|
||||
var existingAnnotationSummary = timelineEvent.annotations ?: EventAnnotationsSummary(
|
||||
relatedEventID
|
||||
)
|
||||
var existingAnnotationSummary = timelineEvent.annotations ?: EventAnnotationsSummary()
|
||||
val updateReactions = existingAnnotationSummary.reactionsSummary.toMutableList()
|
||||
|
||||
contents.forEach { uiEchoReaction ->
|
||||
|
@ -60,7 +60,7 @@ internal class DefaultSpace(
|
||||
}
|
||||
?: viaParameterFinder.computeViaParams(roomId, 3))
|
||||
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
@ -79,7 +79,7 @@ internal class DefaultSpace(
|
||||
// return
|
||||
|
||||
// edit state event and set via to null
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
@ -91,19 +91,19 @@ internal class DefaultSpace(
|
||||
}
|
||||
|
||||
override fun getChildInfo(roomId: String): SpaceChildContent? {
|
||||
return room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
return room.stateService().getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
}
|
||||
|
||||
override suspend fun setChildrenOrder(roomId: String, order: String?) {
|
||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
val existing = room.stateService().getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
?: throw IllegalArgumentException("$roomId is not a child of this space")
|
||||
|
||||
// edit state event and set via to null
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
@ -140,7 +140,7 @@ internal class DefaultSpace(
|
||||
// }
|
||||
|
||||
override suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) {
|
||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
val existing = room.stateService().getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
?: throw IllegalArgumentException("$roomId is not a child of this space")
|
||||
@ -150,7 +150,7 @@ internal class DefaultSpace(
|
||||
return
|
||||
}
|
||||
// edit state event and set via to null
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
|
@ -26,6 +26,7 @@ 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.RoomSortOrder
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
@ -258,7 +259,7 @@ internal class DefaultSpaceService @Inject constructor(
|
||||
val room = roomGetter.getRoom(childRoomId)
|
||||
?: throw IllegalArgumentException("Unknown Room $childRoomId")
|
||||
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_PARENT,
|
||||
stateKey = parentSpaceId,
|
||||
body = SpaceParentContent(
|
||||
@ -276,7 +277,7 @@ internal class DefaultSpaceService @Inject constructor(
|
||||
if (existingEvent != null) {
|
||||
// Should i check if it was sent by me?
|
||||
// we don't check power level, it will throw if you cannot do that
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_PARENT,
|
||||
stateKey = parentSpaceId,
|
||||
body = SpaceParentContent(
|
||||
|
@ -29,9 +29,10 @@ 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.task.Task
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface CreateWidgetTask : Task<CreateWidgetTask.Params, Unit> {
|
||||
internal interface CreateWidgetTask : Task<CreateWidgetTask.Params, String> {
|
||||
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
@ -45,8 +46,8 @@ internal class DefaultCreateWidgetTask @Inject constructor(@SessionDatabase priv
|
||||
@UserId private val userId: String,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver) : CreateWidgetTask {
|
||||
|
||||
override suspend fun execute(params: CreateWidgetTask.Params) {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
override suspend fun execute(params: CreateWidgetTask.Params): String {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.sendStateEvent(
|
||||
roomId = params.roomId,
|
||||
stateEventType = EventType.STATE_ROOM_WIDGET_LEGACY,
|
||||
@ -60,5 +61,8 @@ internal class DefaultCreateWidgetTask @Inject constructor(@SessionDatabase priv
|
||||
.and()
|
||||
.equalTo(CurrentStateEventEntityFields.ROOT.SENDER, userId)
|
||||
}
|
||||
return response.eventId.also {
|
||||
Timber.d("Widget state event: $it just sent in room ${params.roomId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.MatrixTest
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
@ -148,14 +149,22 @@ class PushRulesConditionTest : MatrixTest {
|
||||
val room2JoinedId = "2joined"
|
||||
val room3JoinedId = "3joined"
|
||||
|
||||
val roomStub2Joined = mockk<Room> {
|
||||
val roomMembershipService2 = mockk<MembershipService> {
|
||||
every { getNumberOfJoinedMembers() } returns 2
|
||||
}
|
||||
|
||||
val roomStub3Joined = mockk<Room> {
|
||||
val roomMembershipService3 = mockk<MembershipService> {
|
||||
every { getNumberOfJoinedMembers() } returns 3
|
||||
}
|
||||
|
||||
val roomStub2Joined = mockk<Room> {
|
||||
every { membershipService() } returns roomMembershipService2
|
||||
}
|
||||
|
||||
val roomStub3Joined = mockk<Room> {
|
||||
every { membershipService() } returns roomMembershipService3
|
||||
}
|
||||
|
||||
val roomGetterStub = mockk<RoomGetter> {
|
||||
every { getRoom(room2JoinedId) } returns roomStub2Joined
|
||||
every { getRoom(room3JoinedId) } returns roomStub3Joined
|
||||
|
@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -102,7 +102,7 @@ class AppStateHandler @Inject constructor(
|
||||
if (spaceId != null) {
|
||||
uSession.coroutineScope.launch(Dispatchers.IO) {
|
||||
tryOrNull {
|
||||
uSession.getRoom(spaceId)?.loadRoomMembersIfNeeded()
|
||||
uSession.getRoom(spaceId)?.membershipService()?.loadRoomMembersIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import nl.dionsegijn.konfetti.xml.KonfettiView
|
||||
|
||||
/**
|
||||
* Konfetti workaround to avoid crashes on API 21/22
|
||||
* https://github.com/DanielMartinus/Konfetti/issues/297
|
||||
*/
|
||||
class CompatKonfetti @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : KonfettiView(context, attrs) {
|
||||
|
||||
override fun onVisibilityChanged(changedView: View, visibility: Int) {
|
||||
when (Build.VERSION.SDK_INT) {
|
||||
Build.VERSION_CODES.LOLLIPOP, Build.VERSION_CODES.LOLLIPOP_MR1 -> safeOnVisibilityChanged(changedView, visibility)
|
||||
else -> super.onVisibilityChanged(changedView, visibility)
|
||||
}
|
||||
}
|
||||
|
||||
private fun safeOnVisibilityChanged(changedView: View, visibility: Int) {
|
||||
runCatching { super.onVisibilityChanged(changedView, visibility) }
|
||||
}
|
||||
}
|
@ -33,18 +33,20 @@ fun Int?.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
||||
}
|
||||
}
|
||||
|
||||
fun RoomSummary?.toAnalyticsJoinedRoom(): JoinedRoom {
|
||||
fun RoomSummary?.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom {
|
||||
return JoinedRoom(
|
||||
isDM = this?.isDirect.orFalse(),
|
||||
isSpace = this?.roomType == RoomType.SPACE,
|
||||
roomSize = this?.joinedMembersCount?.toAnalyticsRoomSize() ?: JoinedRoom.RoomSize.Two
|
||||
roomSize = this?.joinedMembersCount?.toAnalyticsRoomSize() ?: JoinedRoom.RoomSize.Two,
|
||||
trigger = trigger
|
||||
)
|
||||
}
|
||||
|
||||
fun PublicRoom.toAnalyticsJoinedRoom(): JoinedRoom {
|
||||
fun PublicRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom {
|
||||
return JoinedRoom(
|
||||
isDM = false,
|
||||
isSpace = false,
|
||||
roomSize = numJoinedMembers.toAnalyticsRoomSize()
|
||||
roomSize = numJoinedMembers.toAnalyticsRoomSize(),
|
||||
trigger = trigger
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.features.analytics.extensions
|
||||
|
||||
import im.vector.app.RoomGroupingMethod
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
|
||||
fun RoomSummary?.toAnalyticsViewRoom(trigger: ViewRoom.Trigger?, groupingMethod: RoomGroupingMethod? = null, viaKeyboard: Boolean? = null): ViewRoom {
|
||||
val activeSpace = groupingMethod?.let {
|
||||
when (it) {
|
||||
is RoomGroupingMethod.BySpace -> it.spaceSummary?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
return ViewRoom(
|
||||
isDM = this?.isDirect.orFalse(),
|
||||
isSpace = this?.roomType == RoomType.SPACE,
|
||||
trigger = trigger,
|
||||
activeSpace = activeSpace,
|
||||
viaKeyboard = viaKeyboard
|
||||
)
|
||||
}
|
||||
|
||||
private fun RoomSummary.toActiveSpace(): ViewRoom.ActiveSpace {
|
||||
return if (isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private
|
||||
}
|
@ -127,7 +127,8 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
|
||||
)
|
||||
|
||||
private fun createMemberItems(queryParams: RoomMemberQueryParams) =
|
||||
room.getRoomMembers(queryParams)
|
||||
room.membershipService()
|
||||
.getRoomMembers(queryParams)
|
||||
.asSequence()
|
||||
.sortedBy { it.displayName }
|
||||
.disambiguate()
|
||||
|
@ -30,7 +30,7 @@ class CallUserMapper(private val session: Session, private val protocolsChecker:
|
||||
fun nativeRoomForVirtualRoom(roomId: String): String? {
|
||||
if (!protocolsChecker.supportVirtualRooms) return null
|
||||
val virtualRoom = session.getRoom(roomId) ?: return null
|
||||
val virtualRoomEvent = virtualRoom.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM)
|
||||
val virtualRoomEvent = virtualRoom.roomAccountDataService().getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM)
|
||||
return virtualRoomEvent?.content?.toModel<RoomVirtualContent>()?.nativeRoomId
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ class CallUserMapper(private val session: Session, private val protocolsChecker:
|
||||
|
||||
private suspend fun Room.markVirtual(nativeRoomId: String) {
|
||||
val virtualRoomContent = RoomVirtualContent(nativeRoomId = nativeRoomId)
|
||||
updateAccountData(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM, virtualRoomContent.toContent())
|
||||
roomAccountDataService().updateAccountData(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM, virtualRoomContent.toContent())
|
||||
}
|
||||
|
||||
private suspend fun ensureVirtualRoomExists(userId: String, nativeRoomId: String): String {
|
||||
|
@ -30,7 +30,7 @@ fun WebRtcCall.getOpponentAsMatrixItem(session: Session): MatrixItem? {
|
||||
roomSummary.toMatrixItem()
|
||||
} else {
|
||||
val userId = roomSummary.otherMemberIds.first()
|
||||
return room.getRoomMember(userId)?.toMatrixItem()
|
||||
return room.membershipService().getRoomMember(userId)?.toMatrixItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.core.utils.onPermissionDeniedSnackbar
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.contactsbook.ContactsBookFragment
|
||||
import im.vector.app.features.qrcode.QrCodeScannerEvents
|
||||
import im.vector.app.features.qrcode.QrCodeScannerFragment
|
||||
@ -206,7 +207,11 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
|
||||
}
|
||||
|
||||
private fun renderCreationSuccess(roomId: String) {
|
||||
navigator.openRoom(this, roomId)
|
||||
navigator.openRoom(
|
||||
context = this,
|
||||
roomId = roomId,
|
||||
trigger = ViewRoom.Trigger.MessageUser
|
||||
)
|
||||
finish()
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import android.content.Context
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
@ -159,7 +160,12 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
||||
if (roomId.isNullOrBlank()) {
|
||||
it.navigator.waitSessionVerification(it)
|
||||
} else {
|
||||
it.navigator.openRoom(it, roomId, pr.transactionId)
|
||||
it.navigator.openRoom(
|
||||
context = it,
|
||||
roomId = roomId,
|
||||
eventId = pr.transactionId,
|
||||
trigger = ViewRoom.Trigger.VerificationRequest
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,11 +174,10 @@ class RoomDevToolViewModel @AssistedInject constructor(
|
||||
val json = adapter.fromJson(state.editedContent ?: "")
|
||||
?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content))
|
||||
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
state.selectedEvent?.type.orEmpty(),
|
||||
state.selectedEvent?.stateKey.orEmpty(),
|
||||
json
|
||||
|
||||
)
|
||||
_viewEvents.post(DevToolsViewEvents.ShowSnackMessage(stringProvider.getString(R.string.dev_tools_success_state_event)))
|
||||
setState {
|
||||
@ -212,7 +211,7 @@ class RoomDevToolViewModel @AssistedInject constructor(
|
||||
?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_message_type))
|
||||
|
||||
if (isState) {
|
||||
room.sendStateEvent(
|
||||
room.stateService().sendStateEvent(
|
||||
eventType,
|
||||
state.sendEventDraft.stateKey.orEmpty(),
|
||||
json
|
||||
@ -222,7 +221,7 @@ class RoomDevToolViewModel @AssistedInject constructor(
|
||||
// val validParse = MoshiProvider.providesMoshi().adapter(MessageContent::class.java).fromJson(it.sendEventDraft.content ?: "")
|
||||
json.toModel<MessageContent>(catchError = false)
|
||||
?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_malformed_event))
|
||||
room.sendEvent(
|
||||
room.sendService().sendEvent(
|
||||
eventType,
|
||||
json
|
||||
)
|
||||
|
@ -49,8 +49,10 @@ import im.vector.app.features.MainActivity
|
||||
import im.vector.app.features.MainActivityArgs
|
||||
import im.vector.app.features.analytics.accountdata.AnalyticsAccountDataViewModel
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.disclaimer.showDisclaimerDialog
|
||||
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
||||
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
||||
import im.vector.app.features.navigation.Navigator
|
||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import im.vector.app.features.permalink.NavigationInterceptor
|
||||
@ -243,7 +245,7 @@ class HomeActivity :
|
||||
}
|
||||
if (args?.inviteNotificationRoomId != null) {
|
||||
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(args.inviteNotificationRoomId)?.let {
|
||||
navigator.openMatrixToBottomSheet(this, it)
|
||||
navigator.openMatrixToBottomSheet(this, it, OriginOfMatrixTo.NOTIFICATION)
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,7 +482,7 @@ class HomeActivity :
|
||||
activeSessionHolder.getSafeActiveSession()
|
||||
?.permalinkService()
|
||||
?.createPermalink(parcelableExtra.inviteNotificationRoomId)?.let {
|
||||
navigator.openMatrixToBottomSheet(this, it)
|
||||
navigator.openMatrixToBottomSheet(this, it, OriginOfMatrixTo.NOTIFICATION)
|
||||
}
|
||||
}
|
||||
handleIntent(intent)
|
||||
@ -567,14 +569,14 @@ class HomeActivity :
|
||||
|
||||
override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean {
|
||||
// TODO check if there is already one??
|
||||
MatrixToBottomSheet.withLink(deepLink.toString())
|
||||
MatrixToBottomSheet.withLink(deepLink.toString(), OriginOfMatrixTo.LINK)
|
||||
.show(supportFragmentManager, "HA#MatrixToBottomSheet")
|
||||
return true
|
||||
}
|
||||
|
||||
override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean {
|
||||
if (roomId == null) return false
|
||||
MatrixToBottomSheet.withLink(deepLink.toString())
|
||||
MatrixToBottomSheet.withLink(deepLink.toString(), OriginOfMatrixTo.LINK)
|
||||
.show(supportFragmentManager, "HA#MatrixToBottomSheet")
|
||||
return true
|
||||
}
|
||||
@ -608,8 +610,8 @@ class HomeActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun mxToBottomSheetNavigateToRoom(roomId: String) {
|
||||
navigator.openRoom(this, roomId)
|
||||
override fun mxToBottomSheetNavigateToRoom(roomId: String, trigger: ViewRoom.Trigger?) {
|
||||
navigator.openRoom(this, roomId, trigger = trigger)
|
||||
}
|
||||
|
||||
override fun mxToBottomSheetSwitchToSpace(spaceId: String) {
|
||||
|
@ -36,6 +36,7 @@ import im.vector.app.core.extensions.replaceFragment
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityRoomDetailBinding
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
|
||||
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
|
||||
@ -205,8 +206,8 @@ class RoomDetailActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun mxToBottomSheetNavigateToRoom(roomId: String) {
|
||||
navigator.openRoom(this, roomId)
|
||||
override fun mxToBottomSheetNavigateToRoom(roomId: String, trigger: ViewRoom.Trigger?) {
|
||||
navigator.openRoom(this, roomId, trigger = trigger)
|
||||
}
|
||||
|
||||
override fun mxToBottomSheetSwitchToSpace(spaceId: String) {
|
||||
|
@ -40,6 +40,7 @@ import im.vector.app.core.utils.BehaviorDataSource
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.DecryptionFailureTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import im.vector.app.features.call.conference.ConferenceEvent
|
||||
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
|
||||
import im.vector.app.features.call.conference.JitsiService
|
||||
@ -88,6 +89,8 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.file.FileService
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
@ -182,7 +185,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
setupPreviewUrlObservers()
|
||||
room.getRoomSummaryLive()
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
|
||||
tryOrNull { room.readService().markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
|
||||
}
|
||||
// Inform the SDK that the room is displayed
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
@ -193,7 +196,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
chatEffectManager.delegate = this
|
||||
|
||||
// Ensure to share the outbound session keys with all members
|
||||
if (OutboundSessionKeySharingStrategy.WhenEnteringRoom == BuildConfig.outboundSessionKeySharingStrategy && room.isEncrypted()) {
|
||||
if (OutboundSessionKeySharingStrategy.WhenEnteringRoom == BuildConfig.outboundSessionKeySharingStrategy && room.roomCryptoService().isEncrypted()) {
|
||||
prepareForEncryption()
|
||||
}
|
||||
|
||||
@ -249,7 +252,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
prepareToEncrypt = Loading()
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
room.prepareToEncrypt()
|
||||
room.roomCryptoService().prepareToEncrypt()
|
||||
}.fold({
|
||||
prepareToEncrypt = Success(Unit)
|
||||
}, {
|
||||
@ -353,7 +356,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
private fun markThreadTimelineAsReadLocal() {
|
||||
initialState.rootThreadEventId?.let {
|
||||
session.coroutineScope.launch {
|
||||
room.markThreadAsRead(it)
|
||||
room.threadsLocalService().markThreadAsRead(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,7 +492,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
private fun handleSetNewAvatar(action: RoomDetailAction.SetAvatarAction) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
room.updateAvatar(action.newAvatarUri, action.newAvatarFileName)
|
||||
room.stateService().updateAvatar(action.newAvatarUri, action.newAvatarFileName)
|
||||
_viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
|
||||
@ -506,7 +509,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
||||
room.getUserReadReceipt(action.userId)
|
||||
room.readService().getUserReadReceipt(action.userId)
|
||||
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
||||
}
|
||||
|
||||
@ -518,7 +521,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
eventId = it))
|
||||
} ?: action.stickerContent
|
||||
|
||||
room.sendEvent(EventType.STICKER, content.toContent())
|
||||
room.sendService().sendEvent(EventType.STICKER, content.toContent())
|
||||
}
|
||||
|
||||
private fun handleStartCall(action: RoomDetailAction.StartCall) {
|
||||
@ -638,7 +641,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
if (trackUnreadMessages.getAndSet(false)) {
|
||||
mostRecentDisplayedEvent?.root?.eventId?.also {
|
||||
session.coroutineScope.launch {
|
||||
tryOrNull { room.setReadMarker(it) }
|
||||
tryOrNull { room.readService().setReadMarker(it) }
|
||||
}
|
||||
}
|
||||
mostRecentDisplayedEvent = null
|
||||
@ -651,12 +654,12 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
fun getMember(userId: String): RoomMemberSummary? {
|
||||
return room.getRoomMember(userId)
|
||||
return room.membershipService().getRoomMember(userId)
|
||||
}
|
||||
|
||||
private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) {
|
||||
// Ensure outbound session keys
|
||||
if (OutboundSessionKeySharingStrategy.WhenTyping == BuildConfig.outboundSessionKeySharingStrategy && room.isEncrypted()) {
|
||||
if (OutboundSessionKeySharingStrategy.WhenTyping == BuildConfig.outboundSessionKeySharingStrategy && room.roomCryptoService().isEncrypted()) {
|
||||
if (action.focused) {
|
||||
// Should we add some rate limit here, or do it only once per model lifecycle?
|
||||
prepareForEncryption()
|
||||
@ -737,36 +740,36 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
private fun handleSendReaction(action: RoomDetailAction.SendReaction) {
|
||||
room.sendReaction(action.targetEventId, action.reaction)
|
||||
room.relationService().sendReaction(action.targetEventId, action.reaction)
|
||||
}
|
||||
|
||||
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
|
||||
val event = room.getTimelineEvent(action.targetEventId) ?: return
|
||||
room.redactEvent(event.root, action.reason)
|
||||
room.sendService().redactEvent(event.root, action.reason)
|
||||
}
|
||||
|
||||
private fun handleUndoReact(action: RoomDetailAction.UndoReaction) {
|
||||
viewModelScope.launch {
|
||||
tryOrNull {
|
||||
room.undoReaction(action.targetEventId, action.reaction)
|
||||
room.relationService().undoReaction(action.targetEventId, action.reaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateQuickReaction(action: RoomDetailAction.UpdateQuickReactAction) {
|
||||
if (action.add) {
|
||||
room.sendReaction(action.targetEventId, action.selectedReaction)
|
||||
room.relationService().sendReaction(action.targetEventId, action.selectedReaction)
|
||||
} else {
|
||||
viewModelScope.launch {
|
||||
tryOrNull {
|
||||
room.undoReaction(action.targetEventId, action.selectedReaction)
|
||||
room.relationService().undoReaction(action.targetEventId, action.selectedReaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSendMedia(action: RoomDetailAction.SendMedia) {
|
||||
room.sendMedias(
|
||||
room.sendService().sendMedias(
|
||||
action.attachments,
|
||||
action.compressBeforeSending,
|
||||
emptySet(),
|
||||
@ -821,13 +824,22 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
session.roomService().joinRoom(room.roomId)
|
||||
analyticsTracker.capture(room.roomSummary().toAnalyticsJoinedRoom())
|
||||
trackRoomJoined()
|
||||
} catch (throwable: Throwable) {
|
||||
_viewEvents.post(RoomDetailViewEvents.Failure(throwable, showInDialog = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun trackRoomJoined() {
|
||||
val trigger = if (initialState.isInviteAlreadyAccepted) {
|
||||
JoinedRoom.Trigger.Invite
|
||||
} else {
|
||||
JoinedRoom.Trigger.Timeline
|
||||
}
|
||||
analyticsTracker.capture(room.roomSummary().toAnalyticsJoinedRoom(trigger))
|
||||
}
|
||||
|
||||
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
||||
val mxcUrl = action.messageFileContent.getFileUrl() ?: return
|
||||
val isLocalSendingFile = action.senderId == session.myUserId &&
|
||||
@ -892,8 +904,8 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
return
|
||||
}
|
||||
when {
|
||||
it.root.isTextMessage() -> room.resendTextMessage(it)
|
||||
it.root.isAttachmentMessage() -> room.resendMediaMessage(it)
|
||||
it.root.isTextMessage() -> room.sendService().resendTextMessage(it)
|
||||
it.root.isAttachmentMessage() -> room.sendService().resendMediaMessage(it)
|
||||
else -> {
|
||||
// TODO
|
||||
}
|
||||
@ -909,13 +921,13 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
||||
return
|
||||
}
|
||||
room.deleteFailedEcho(it)
|
||||
room.sendService().deleteFailedEcho(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCancel(action: RoomDetailAction.CancelSend) {
|
||||
if (action.force) {
|
||||
room.cancelSend(action.eventId)
|
||||
room.sendService().cancelSend(action.eventId)
|
||||
return
|
||||
}
|
||||
val targetEventId = action.eventId
|
||||
@ -925,16 +937,16 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
Timber.e("Cannot cancel message, it is not sending")
|
||||
return
|
||||
}
|
||||
room.cancelSend(targetEventId)
|
||||
room.sendService().cancelSend(targetEventId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResendAll() {
|
||||
room.resendAllFailedMessages()
|
||||
room.sendService().resendAllFailedMessages()
|
||||
}
|
||||
|
||||
private fun handleRemoveAllFailedMessages() {
|
||||
room.cancelAllFailedMessages()
|
||||
room.sendService().cancelAllFailedMessages()
|
||||
}
|
||||
|
||||
private fun observeEventDisplayedActions() {
|
||||
@ -957,7 +969,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
}
|
||||
bufferedMostRecentDisplayedEvent.root.eventId?.let { eventId ->
|
||||
session.coroutineScope.launch {
|
||||
tryOrNull { room.setReadReceipt(eventId) }
|
||||
tryOrNull { room.readService().setReadReceipt(eventId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -974,14 +986,14 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
private fun handleMarkAllAsRead() {
|
||||
setState { copy(unreadState = UnreadState.HasNoUnread) }
|
||||
viewModelScope.launch {
|
||||
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.BOTH) }
|
||||
tryOrNull { room.readService().markAsRead(ReadService.MarkAsReadParams.BOTH) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportContent(action: RoomDetailAction.ReportContent) {
|
||||
viewModelScope.launch {
|
||||
val event = try {
|
||||
room.reportContent(action.eventId, -100, action.reason)
|
||||
room.reportingService().reportContent(action.eventId, -100, action.reason)
|
||||
RoomDetailViewEvents.ActionSuccess(action)
|
||||
} catch (failure: Exception) {
|
||||
RoomDetailViewEvents.ActionFailure(action, failure)
|
||||
@ -1071,13 +1083,13 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
room.getTimelineEvent(action.eventId)?.let { pollTimelineEvent ->
|
||||
val currentVote = pollTimelineEvent.annotations?.pollResponseSummary?.aggregatedContent?.myVote
|
||||
if (currentVote != action.optionKey) {
|
||||
room.voteToPoll(action.eventId, action.optionKey)
|
||||
room.sendService().voteToPoll(action.eventId, action.optionKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleEndPoll(eventId: String) {
|
||||
room.endPoll(eventId)
|
||||
room.sendService().endPoll(eventId)
|
||||
}
|
||||
|
||||
private fun observeSyncState() {
|
||||
@ -1255,7 +1267,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||
timeline.removeAllListeners()
|
||||
decryptionFailureTracker.onTimeLineDisposed(room.roomId)
|
||||
if (vectorPreferences.sendTypingNotifs()) {
|
||||
room.userStopsTyping()
|
||||
room.typingService().userStopsTyping()
|
||||
}
|
||||
chatEffectManager.delegate = null
|
||||
chatEffectManager.dispose()
|
||||
|
@ -28,6 +28,7 @@ import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsComposer
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import im.vector.app.features.attachments.toContentAttachmentData
|
||||
import im.vector.app.features.command.CommandParser
|
||||
import im.vector.app.features.command.ParsedCommand
|
||||
@ -52,6 +53,8 @@ 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.getRoom
|
||||
import org.matrix.android.sdk.api.session.getRoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm
|
||||
@ -204,12 +207,12 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
// Send the text message to the room
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = action.text,
|
||||
autoMarkdown = action.autoMarkdown)
|
||||
} else {
|
||||
room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown)
|
||||
room.sendService().sendTextMessage(action.text, autoMarkdown = action.autoMarkdown)
|
||||
}
|
||||
|
||||
_viewEvents.post(MessageComposerViewEvents.MessageSent)
|
||||
@ -230,12 +233,12 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.SendPlainText -> {
|
||||
// Send the text message to the room, without markdown
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = parsedCommand.message,
|
||||
autoMarkdown = false)
|
||||
} else {
|
||||
room.sendTextMessage(parsedCommand.message, autoMarkdown = false)
|
||||
room.sendService().sendTextMessage(parsedCommand.message, autoMarkdown = false)
|
||||
}
|
||||
_viewEvents.post(MessageComposerViewEvents.MessageSent)
|
||||
popDraft()
|
||||
@ -285,13 +288,16 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
}
|
||||
is ParsedCommand.SendEmote -> {
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = parsedCommand.message,
|
||||
msgType = MessageType.MSGTYPE_EMOTE,
|
||||
autoMarkdown = action.autoMarkdown)
|
||||
} else {
|
||||
room.sendTextMessage(parsedCommand.message, msgType = MessageType.MSGTYPE_EMOTE, autoMarkdown = action.autoMarkdown)
|
||||
room.sendService().sendTextMessage(
|
||||
text = parsedCommand.message,
|
||||
msgType = MessageType.MSGTYPE_EMOTE,
|
||||
autoMarkdown = action.autoMarkdown)
|
||||
}
|
||||
_viewEvents.post(MessageComposerViewEvents.SlashCommandResultOk(parsedCommand))
|
||||
popDraft()
|
||||
@ -299,12 +305,12 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.SendRainbow -> {
|
||||
val message = parsedCommand.message.toString()
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = parsedCommand.message,
|
||||
formattedText = rainbowGenerator.generate(message))
|
||||
} else {
|
||||
room.sendFormattedTextMessage(message, rainbowGenerator.generate(message))
|
||||
room.sendService().sendFormattedTextMessage(message, rainbowGenerator.generate(message))
|
||||
}
|
||||
_viewEvents.post(MessageComposerViewEvents.SlashCommandResultOk(parsedCommand))
|
||||
popDraft()
|
||||
@ -312,13 +318,13 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.SendRainbowEmote -> {
|
||||
val message = parsedCommand.message.toString()
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = parsedCommand.message,
|
||||
msgType = MessageType.MSGTYPE_EMOTE,
|
||||
formattedText = rainbowGenerator.generate(message))
|
||||
} else {
|
||||
room.sendFormattedTextMessage(message, rainbowGenerator.generate(message), MessageType.MSGTYPE_EMOTE)
|
||||
room.sendService().sendFormattedTextMessage(message, rainbowGenerator.generate(message), MessageType.MSGTYPE_EMOTE)
|
||||
}
|
||||
|
||||
_viewEvents.post(MessageComposerViewEvents.SlashCommandResultOk(parsedCommand))
|
||||
@ -328,12 +334,12 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
val text = "[${stringProvider.getString(R.string.spoiler)}](${parsedCommand.message})"
|
||||
val formattedText = "<span data-mx-spoiler>${parsedCommand.message}</span>"
|
||||
if (state.rootThreadEventId != null) {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = text,
|
||||
formattedText = formattedText)
|
||||
} else {
|
||||
room.sendFormattedTextMessage(
|
||||
room.sendService().sendFormattedTextMessage(
|
||||
text,
|
||||
formattedText)
|
||||
}
|
||||
@ -376,7 +382,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.DiscardSession -> {
|
||||
if (room.isEncrypted()) {
|
||||
if (room.roomCryptoService().isEncrypted()) {
|
||||
session.cryptoService().discardOutboundSession(room.roomId)
|
||||
_viewEvents.post(MessageComposerViewEvents.SlashCommandResultOk(parsedCommand))
|
||||
popDraft()
|
||||
@ -488,13 +494,13 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
if (inReplyTo != null) {
|
||||
// TODO check if same content?
|
||||
room.getTimelineEvent(inReplyTo)?.let {
|
||||
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
||||
room.relationService().editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
||||
}
|
||||
} else {
|
||||
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||
val existingBody = messageContent?.body ?: ""
|
||||
if (existingBody != action.text) {
|
||||
room.editTextMessage(state.sendMode.timelineEvent,
|
||||
room.relationService().editTextMessage(state.sendMode.timelineEvent,
|
||||
messageContent?.msgType ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
@ -506,7 +512,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
popDraft()
|
||||
}
|
||||
is SendMode.Quote -> {
|
||||
room.sendQuotedTextMessage(
|
||||
room.sendService().sendQuotedTextMessage(
|
||||
quotedEvent = state.sendMode.timelineEvent,
|
||||
text = action.text.toString(),
|
||||
autoMarkdown = action.autoMarkdown,
|
||||
@ -520,12 +526,12 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
// If threads are disabled this will make the fallback replies visible to clients with threads enabled
|
||||
val rootThreadEventId = if (showInThread) timelineEvent.root.getRootThreadEventId() else null
|
||||
state.rootThreadEventId?.let {
|
||||
room.replyInThread(
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = it,
|
||||
replyInThreadText = action.text.toString(),
|
||||
autoMarkdown = action.autoMarkdown,
|
||||
eventReplied = timelineEvent)
|
||||
} ?: room.replyToMessage(
|
||||
} ?: room.relationService().replyToMessage(
|
||||
eventReplied = timelineEvent,
|
||||
replyText = action.text.toString(),
|
||||
autoMarkdown = action.autoMarkdown,
|
||||
@ -551,13 +557,13 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
// Otherwise we clear the composer and remove the draft from db
|
||||
setState { copy(sendMode = SendMode.Regular("", false)) }
|
||||
viewModelScope.launch {
|
||||
room.deleteDraft()
|
||||
room.draftService().deleteDraft()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadDraftIfAny() {
|
||||
val currentDraft = room.getDraft()
|
||||
val currentDraft = room.draftService().getDraft()
|
||||
setState {
|
||||
copy(
|
||||
// Create a sendMode from a draft and retrieve the TimelineEvent
|
||||
@ -588,9 +594,9 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
private fun handleUserIsTyping(action: MessageComposerAction.UserIsTyping) {
|
||||
if (vectorPreferences.sendTypingNotifs()) {
|
||||
if (action.isTyping) {
|
||||
room.userIsTyping()
|
||||
room.typingService().userIsTyping()
|
||||
} else {
|
||||
room.userStopsTyping()
|
||||
room.typingService().userStopsTyping()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -602,9 +608,9 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
ChatEffect.CONFETTI -> R.string.default_message_emote_confetti
|
||||
ChatEffect.SNOWFALL -> R.string.default_message_emote_snow
|
||||
})
|
||||
room.sendTextMessage(defaultMessage, MessageType.MSGTYPE_EMOTE)
|
||||
room.sendService().sendTextMessage(defaultMessage, MessageType.MSGTYPE_EMOTE)
|
||||
} else {
|
||||
room.sendTextMessage(sendChatEffect.message, sendChatEffect.chatEffect.toMessageType())
|
||||
room.sendService().sendTextMessage(sendChatEffect.message, sendChatEffect.chatEffect.toMessageType())
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,7 +623,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
return@launch
|
||||
}
|
||||
session.getRoomSummary(command.roomAlias)
|
||||
?.also { analyticsTracker.capture(it.toAnalyticsJoinedRoom()) }
|
||||
?.also { analyticsTracker.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.SlashCommand)) }
|
||||
?.roomId
|
||||
?.let {
|
||||
_viewEvents.post(MessageComposerViewEvents.JoinRoomCommandSuccess(it))
|
||||
@ -647,19 +653,19 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
|
||||
private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) {
|
||||
launchSlashCommandFlowSuspendable(changeTopic) {
|
||||
room.updateTopic(changeTopic.topic)
|
||||
room.stateService().updateTopic(changeTopic.topic)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) {
|
||||
launchSlashCommandFlowSuspendable(invite) {
|
||||
room.invite(invite.userId, invite.reason)
|
||||
room.membershipService().invite(invite.userId, invite.reason)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInvite3pidSlashCommand(invite: ParsedCommand.Invite3Pid) {
|
||||
launchSlashCommandFlowSuspendable(invite) {
|
||||
room.invite3pid(invite.threePid)
|
||||
room.membershipService().invite3pid(invite.threePid)
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,7 +678,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
?: return
|
||||
|
||||
launchSlashCommandFlowSuspendable(setUserPowerLevel) {
|
||||
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent)
|
||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent)
|
||||
}
|
||||
}
|
||||
|
||||
@ -700,25 +706,25 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
|
||||
private fun handleRemoveSlashCommand(removeUser: ParsedCommand.RemoveUser) {
|
||||
launchSlashCommandFlowSuspendable(removeUser) {
|
||||
room.remove(removeUser.userId, removeUser.reason)
|
||||
room.membershipService().remove(removeUser.userId, removeUser.reason)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBanSlashCommand(ban: ParsedCommand.BanUser) {
|
||||
launchSlashCommandFlowSuspendable(ban) {
|
||||
room.ban(ban.userId, ban.reason)
|
||||
room.membershipService().ban(ban.userId, ban.reason)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnbanSlashCommand(unban: ParsedCommand.UnbanUser) {
|
||||
launchSlashCommandFlowSuspendable(unban) {
|
||||
room.unban(unban.userId, unban.reason)
|
||||
room.membershipService().unban(unban.userId, unban.reason)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleChangeRoomNameSlashCommand(changeRoomName: ParsedCommand.ChangeRoomName) {
|
||||
launchSlashCommandFlowSuspendable(changeRoomName) {
|
||||
room.updateName(changeRoomName.name)
|
||||
room.stateService().updateName(changeRoomName.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,14 +740,14 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
?.copy(displayName = changeDisplayName.displayName)
|
||||
?.toContent()
|
||||
?.let {
|
||||
room.sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it)
|
||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleChangeRoomAvatarSlashCommand(changeAvatar: ParsedCommand.ChangeRoomAvatar) {
|
||||
launchSlashCommandFlowSuspendable(changeAvatar) {
|
||||
room.sendStateEvent(EventType.STATE_ROOM_AVATAR, stateKey = "", RoomAvatarContent(changeAvatar.url).toContent())
|
||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_AVATAR, stateKey = "", RoomAvatarContent(changeAvatar.url).toContent())
|
||||
}
|
||||
}
|
||||
|
||||
@ -751,7 +757,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
?.copy(avatarUrl = changeAvatar.url)
|
||||
?.toContent()
|
||||
?.let {
|
||||
room.sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it)
|
||||
room.stateService().sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -792,8 +798,8 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
rootThreadEventId?.let {
|
||||
room.replyInThread(it, sequence)
|
||||
} ?: room.sendTextMessage(sequence)
|
||||
room.relationService().replyInThread(it, sequence)
|
||||
} ?: room.sendService().sendTextMessage(sequence)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -804,19 +810,19 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
when {
|
||||
it.sendMode is SendMode.Regular && !it.sendMode.fromSharing -> {
|
||||
setState { copy(sendMode = it.sendMode.copy(text = draft)) }
|
||||
room.saveDraft(UserDraft.Regular(draft))
|
||||
room.draftService().saveDraft(UserDraft.Regular(draft))
|
||||
}
|
||||
it.sendMode is SendMode.Reply -> {
|
||||
setState { copy(sendMode = it.sendMode.copy(text = draft)) }
|
||||
room.saveDraft(UserDraft.Reply(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
room.draftService().saveDraft(UserDraft.Reply(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
}
|
||||
it.sendMode is SendMode.Quote -> {
|
||||
setState { copy(sendMode = it.sendMode.copy(text = draft)) }
|
||||
room.saveDraft(UserDraft.Quote(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
room.draftService().saveDraft(UserDraft.Quote(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
}
|
||||
it.sendMode is SendMode.Edit -> {
|
||||
setState { copy(sendMode = it.sendMode.copy(text = draft)) }
|
||||
room.saveDraft(UserDraft.Edit(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
room.draftService().saveDraft(UserDraft.Edit(it.sendMode.timelineEvent.root.eventId!!, draft))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -837,7 +843,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
} else {
|
||||
audioMessageHelper.stopRecording(convertForSending = true)?.let { audioType ->
|
||||
if (audioType.duration > 1000) {
|
||||
room.sendMedia(
|
||||
room.sendService().sendMedia(
|
||||
attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
|
||||
compressBeforeSending = false,
|
||||
roomIds = emptySet(),
|
||||
@ -904,7 +910,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||
viewModelScope.launch {
|
||||
playingAudioContent?.toContentAttachmentData()?.let { voiceDraft ->
|
||||
val content = voiceDraft.toJsonString()
|
||||
room.saveDraft(UserDraft.Voice(content))
|
||||
room.draftService().saveDraft(UserDraft.Voice(content))
|
||||
setState { copy(sendMode = SendMode.Voice(content)) }
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import im.vector.app.core.extensions.trackItemsVisibilityChange
|
||||
import im.vector.app.core.platform.StateView
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentSearchBinding
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
@ -134,7 +135,16 @@ class SearchFragment @Inject constructor(
|
||||
roomEncryptionTrustLevel = null,
|
||||
rootThreadEventId = it)
|
||||
navigator.openThread(requireContext(), threadTimelineArgs, event.eventId)
|
||||
} ?: navigator.openRoom(requireContext(), roomId, event.eventId)
|
||||
} ?: openRoom(roomId, event.eventId)
|
||||
}
|
||||
|
||||
private fun openRoom(roomId: String, eventId: String?) {
|
||||
navigator.openRoom(
|
||||
context = requireContext(),
|
||||
roomId = roomId,
|
||||
eventId = eventId,
|
||||
trigger = ViewRoom.Trigger.MessageSearch
|
||||
)
|
||||
}
|
||||
|
||||
override fun loadMore() {
|
||||
|
@ -62,7 +62,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(
|
||||
|
||||
viewModelScope.launch {
|
||||
val data = try {
|
||||
room.fetchEditHistory(eventId)
|
||||
room.relationService().fetchEditHistory(eventId)
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(editList = Fail(failure))
|
||||
|
@ -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(
|
||||
|
@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
|
@ -99,8 +99,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
|
||||
@ -209,15 +209,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)
|
||||
|
@ -45,18 +45,18 @@ class TimelineFactory @Inject constructor(private val session: Session, private
|
||||
val settings = timelineSettingsFactory.create(rootThreadEventId)
|
||||
|
||||
if (!session.vectorCallService.protocolChecker.supportVirtualRooms) {
|
||||
return mainRoom.createTimeline(eventId, settings)
|
||||
return mainRoom.timelineService().createTimeline(eventId, settings)
|
||||
}
|
||||
val virtualRoomId = session.vectorCallService.userMapper.virtualRoomForNativeRoom(mainRoom.roomId)
|
||||
return if (virtualRoomId == null) {
|
||||
mainRoom.createTimeline(eventId, settings)
|
||||
mainRoom.timelineService().createTimeline(eventId, settings)
|
||||
} else {
|
||||
val virtualRoom = session.getRoom(virtualRoomId)!!
|
||||
MergedTimelines(
|
||||
coroutineScope = coroutineScope,
|
||||
mainTimeline = mainRoom.createTimeline(eventId, settings),
|
||||
mainTimeline = mainRoom.timelineService().createTimeline(eventId, settings),
|
||||
secondaryTimelineParams = MergedTimelines.SecondaryTimelineParams(
|
||||
timeline = virtualRoom.createTimeline(null, settings),
|
||||
timeline = virtualRoom.timelineService().createTimeline(null, settings),
|
||||
shouldFilterTypes = true,
|
||||
allowedTypes = secondaryTimelineAllowedTypes
|
||||
)
|
||||
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent
|
||||
import javax.inject.Inject
|
||||
|
@ -34,6 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFrag
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.flow.flow
|
||||
import org.matrix.android.sdk.flow.unwrap
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class MigrateRoomViewModel @AssistedInject constructor(
|
||||
val summary = session.getRoomSummary(initialState.roomId)
|
||||
setState {
|
||||
copy(
|
||||
currentVersion = room?.getRoomVersion(),
|
||||
currentVersion = room?.roomVersionService()?.getRoomVersion(),
|
||||
isPublic = summary?.isPublic ?: false,
|
||||
otherMemberCount = summary?.otherMemberIds?.count() ?: 0,
|
||||
knownParents = summary?.flattenParentIds ?: emptyList()
|
||||
|
@ -50,12 +50,12 @@ class UpgradeRoomViewModelTask @Inject constructor(
|
||||
|
||||
val room = session.getRoom(params.roomId)
|
||||
?: return Result.UnknownRoom
|
||||
if (!room.userMayUpgradeRoom(session.myUserId)) {
|
||||
if (!room.roomVersionService().userMayUpgradeRoom(session.myUserId)) {
|
||||
return Result.NotAllowed
|
||||
}
|
||||
|
||||
val updatedRoomId = try {
|
||||
room.upgradeToVersion(params.newVersion)
|
||||
room.roomVersionService().upgradeToVersion(params.newVersion)
|
||||
} catch (failure: Throwable) {
|
||||
return Result.ErrorFailure(failure)
|
||||
}
|
||||
@ -65,7 +65,7 @@ class UpgradeRoomViewModelTask @Inject constructor(
|
||||
params.userIdsToAutoInvite.forEach {
|
||||
params.progressReporter?.invoke(false, currentStep, totalStep)
|
||||
tryOrNull {
|
||||
session.getRoom(updatedRoomId)?.invite(it)
|
||||
session.getRoom(updatedRoomId)?.membershipService()?.invite(it)
|
||||
}
|
||||
currentStep++
|
||||
}
|
||||
|
@ -44,12 +44,14 @@ import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.databinding.FragmentRoomListBinding
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
||||
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@ -179,7 +181,7 @@ class RoomListFragment @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleShowMxToLink(link: String) {
|
||||
navigator.openMatrixToBottomSheet(requireContext(), link)
|
||||
navigator.openMatrixToBottomSheet(requireContext(), link, OriginOfMatrixTo.ROOM_LIST)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@ -196,7 +198,12 @@ class RoomListFragment @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleSelectRoom(event: RoomListViewEvents.SelectRoom, isInviteAlreadyAccepted: Boolean) {
|
||||
navigator.openRoom(context = requireActivity(), roomId = event.roomSummary.roomId, isInviteAlreadyAccepted = isInviteAlreadyAccepted)
|
||||
navigator.openRoom(
|
||||
context = requireActivity(),
|
||||
roomId = event.roomSummary.roomId,
|
||||
isInviteAlreadyAccepted = isInviteAlreadyAccepted,
|
||||
trigger = ViewRoom.Trigger.RoomList
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupCreateRoomButton() {
|
||||
|
@ -32,6 +32,8 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
@ -43,6 +45,7 @@ import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.getRoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
@ -167,7 +170,7 @@ class RoomListViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
fun isPublicRoom(roomId: String): Boolean {
|
||||
return session.getRoom(roomId)?.isPublic().orFalse()
|
||||
return session.getRoom(roomId)?.stateService()?.isPublic().orFalse()
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
@ -251,7 +254,7 @@ class RoomListViewModel @AssistedInject constructor(
|
||||
if (room != null) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
room.setRoomNotificationState(action.notificationState)
|
||||
room.roomPushRuleService().setRoomNotificationState(action.notificationState)
|
||||
} catch (failure: Exception) {
|
||||
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
||||
}
|
||||
@ -276,6 +279,8 @@ class RoomListViewModel @AssistedInject constructor(
|
||||
this[action.roomId] = Fail(failure)
|
||||
}.toMap())
|
||||
}
|
||||
session.getRoomSummary(action.roomId)
|
||||
?.let { analyticsTracker.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.RoomDirectory)) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,13 +299,13 @@ class RoomListViewModel @AssistedInject constructor(
|
||||
action.tag.otherTag()
|
||||
?.takeIf { room.roomSummary()?.hasTag(it).orFalse() }
|
||||
?.let { tagToRemove ->
|
||||
room.deleteTag(tagToRemove)
|
||||
room.tagsService().deleteTag(tagToRemove)
|
||||
}
|
||||
|
||||
// Set the tag. We do not handle the order for the moment
|
||||
room.addTag(action.tag, 0.5)
|
||||
room.tagsService().addTag(action.tag, 0.5)
|
||||
} else {
|
||||
room.deleteTag(action.tag)
|
||||
room.tagsService().deleteTag(action.tag)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(RoomListViewEvents.Failure(failure))
|
||||
|
@ -85,7 +85,7 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState
|
||||
private fun observeThreadSummaries() {
|
||||
room?.flow()
|
||||
?.liveThreadSummaries()
|
||||
?.map { room.enhanceThreadWithEditions(it) }
|
||||
?.map { room.threadsService().enhanceThreadWithEditions(it) }
|
||||
?.flowOn(room.coroutineDispatchers.io)
|
||||
?.execute { asyncThreads ->
|
||||
copy(threadSummaryList = asyncThreads)
|
||||
@ -99,10 +99,10 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState
|
||||
private fun observeThreadsList() {
|
||||
room?.flow()
|
||||
?.liveThreadList()
|
||||
?.map { room.mapEventsWithEdition(it) }
|
||||
?.map { room.threadsLocalService().mapEventsWithEdition(it) }
|
||||
?.map {
|
||||
it.map { threadRootEvent ->
|
||||
val isParticipating = room.isUserParticipatingInThread(threadRootEvent.eventId)
|
||||
val isParticipating = room.threadsLocalService().isUserParticipatingInThread(threadRootEvent.eventId)
|
||||
ThreadTimelineEvent(threadRootEvent, isParticipating)
|
||||
}
|
||||
}
|
||||
@ -115,7 +115,7 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState
|
||||
private fun fetchThreadList() {
|
||||
viewModelScope.launch {
|
||||
setLoading(true)
|
||||
room?.fetchThreadSummaries()
|
||||
room?.threadsService()?.fetchThreadSummaries()
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user