Merge branch 'develop' into feature/crypto

This commit is contained in:
ganfra 2019-06-07 18:53:24 +02:00
commit 81330d30cf
404 changed files with 12812 additions and 2612 deletions

View File

@ -47,9 +47,7 @@ android {
// Set to true to log privacy or sensible data, such as token
// TODO Set to false
buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"

// Set to BODY instead of NONE to enable logging
//TODO Revert BODY
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.HEADERS"
}

@ -93,6 +91,7 @@ dependencies {
def moshi_version = '1.8.0'
def lifecycle_version = '2.0.0'
def coroutines_version = "1.0.1"
def markwon_version = '3.0.0'

implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -114,6 +113,8 @@ dependencies {
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

implementation "ru.noties.markwon:core:$markwon_version"

// Database
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'

View File

@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
@ -78,7 +77,7 @@ object RoomDataHelper {
fun fakeInitialSync(monarchy: Monarchy, roomId: String) {
monarchy.runTransactionSync { realm ->
val roomEntity = realm.createObject<RoomEntity>(roomId)
roomEntity.membership = MyMembership.JOINED
roomEntity.membership = Membership.JOIN
val eventList = createFakeListOfEvents(10)
val chunkEntity = realm.createObject<ChunkEntity>().apply {
nextToken = null

View File

@ -20,7 +20,9 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
import im.vector.matrix.android.internal.database.model.SessionRealmModule
import im.vector.matrix.android.internal.session.room.EventRelationExtractor
import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimeline
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
@ -46,7 +48,9 @@ internal class TimelineTest : InstrumentedTest {
fun setup() {
Timber.plant(Timber.DebugTree())
Realm.init(context())
val testConfiguration = RealmConfiguration.Builder().name("test-realm").build()
val testConfiguration = RealmConfiguration.Builder().name("test-realm")
.modules(SessionRealmModule()).build()

Realm.deleteRealm(testConfiguration)
monarchy = Monarchy.Builder().setRealmConfiguration(testConfiguration).build()
RoomDataHelper.fakeInitialSync(monarchy, ROOM_ID)
@ -58,8 +62,16 @@ internal class TimelineTest : InstrumentedTest {
val paginationTask = FakePaginationTask(tokenChunkEventPersistor)
val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor)
val roomMemberExtractor = SenderRoomMemberExtractor(ROOM_ID)
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
return DefaultTimeline(ROOM_ID, initialEventId, monarchy.realmConfiguration, taskExecutor, getContextOfEventTask, timelineEventFactory, paginationTask, null)
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, EventRelationExtractor())
return DefaultTimeline(
ROOM_ID,
initialEventId,
monarchy.realmConfiguration,
taskExecutor,
getContextOfEventTask,
timelineEventFactory,
paginationTask,
null)
}

@Test

View File

@ -16,7 +16,6 @@

package im.vector.matrix.android.api

import java.util.*
import java.util.regex.Pattern

/**
@ -25,53 +24,53 @@ import java.util.regex.Pattern
object MatrixPatterns {

// Note: TLD is not mandatory (localhost, IP address...)
private val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"
private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"

// regex pattern to find matrix user ids in a string.
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
private val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = Pattern.compile(MATRIX_USER_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = Pattern.compile(MATRIX_USER_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find room ids in a string.
private val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = Pattern.compile(MATRIX_ROOM_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = Pattern.compile(MATRIX_ROOM_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find room aliases in a string.
private val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_ALIAS = Pattern.compile(MATRIX_ROOM_ALIAS_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_ALIAS = Pattern.compile(MATRIX_ROOM_ALIAS_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find message ids in a string.
private val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = Pattern.compile(MATRIX_EVENT_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = Pattern.compile(MATRIX_EVENT_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find message ids in a string.
private val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = Pattern.compile(MATRIX_EVENT_IDENTIFIER_V3_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = Pattern.compile(MATRIX_EVENT_IDENTIFIER_V3_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find group ids in a string.
private val MATRIX_GROUP_IDENTIFIER_REGEX = "\\+[A-Z0-9=_\\-./]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER = Pattern.compile(MATRIX_GROUP_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_GROUP_IDENTIFIER_REGEX = "\\+[A-Z0-9=_\\-./]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER = Pattern.compile(MATRIX_GROUP_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find permalink with message id.
// Android does not support in URL so extract it.
private val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
private val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/"
val SEP_REGEX = "/"
private const val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
private const val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/"
const val SEP_REGEX = "/"

private val LINK_TO_ROOM_ID_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_ROOM_ID_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_ROOM_ALIAS_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_ROOM_ALIAS_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_APP_ROOM_ID_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_APP_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_APP_ROOM_ID_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_APP_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_APP_ROOM_ALIAS_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_APP_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_APP_ROOM_ALIAS_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_APP_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)

// list of patterns to find some matrix item.
val MATRIX_PATTERNS = Arrays.asList(
val MATRIX_PATTERNS = listOf(
PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID,
PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS,
PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID,
@ -133,4 +132,4 @@ object MatrixPatterns {
fun isGroupId(str: String?): Boolean {
return str != null && PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER.matcher(str).matches()
}
}// Cannot be instantiated
}

View File

@ -70,11 +70,11 @@ data class HomeServerConnectionConfig(
if (hsUri.scheme != "http" && hsUri.scheme != "https") {
throw RuntimeException("Invalid home server URI: " + hsUri)
}
// remove trailing /
homeServerUri = if (hsUri.toString().endsWith("/")) {
// ensure trailing /
homeServerUri = if (!hsUri.toString().endsWith("/")) {
try {
val url = hsUri.toString()
Uri.parse(url.substring(0, url.length - 1))
Uri.parse("$url/")
} catch (e: Exception) {
throw RuntimeException("Invalid home server URI: $hsUri")
}
@ -96,11 +96,11 @@ data class HomeServerConnectionConfig(
if (identityServerUri.scheme != "http" && identityServerUri.scheme != "https") {
throw RuntimeException("Invalid identity server URI: $identityServerUri")
}
// remove trailing /
if (identityServerUri.toString().endsWith("/")) {
// ensure trailing /
if (!identityServerUri.toString().endsWith("/")) {
try {
val url = identityServerUri.toString()
this.identityServerUri = Uri.parse(url.substring(0, url.length - 1))
this.identityServerUri = Uri.parse("$url/")
} catch (e: Exception) {
throw RuntimeException("Invalid identity server URI: $identityServerUri")
}

View File

@ -17,9 +17,6 @@
package im.vector.matrix.android.api.permalinks

import android.text.Spannable
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.widget.TextView
import im.vector.matrix.android.api.MatrixPatterns

/**
@ -56,37 +53,5 @@ object MatrixLinkify {
}
return hasMatch
}

fun addLinks(textView: TextView, callback: MatrixPermalinkSpan.Callback?): Boolean {
val text = textView.text
if (text is Spannable) {
if (addLinks(text, callback)) {
addLinkMovementMethod(textView)
return true
}

return false
} else {
val spannableString = SpannableString.valueOf(text)
if (addLinks(spannableString, callback)) {
addLinkMovementMethod(textView)
textView.text = spannableString
return true
}
return false
}
}

/**
* Add linkMovementMethod on textview if not already set
* @param textView the textView on which the movementMethod is set
*/
fun addLinkMovementMethod(textView: TextView) {
val movementMethod = textView.movementMethod
if (movementMethod == null || movementMethod !is LinkMovementMethod) {
if (textView.linksClickable) {
textView.movementMethod = LinkMovementMethod.getInstance()
}
}
}
}

View File

@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService
@ -34,6 +35,7 @@ import im.vector.matrix.android.api.session.user.UserService
*/
interface Session :
RoomService,
RoomDirectoryService,
GroupService,
UserService,
CryptoService,

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model

import com.squareup.moshi.JsonClass

/**
* <code>
* {
* "chunk": [
* {
* "type": "m.reaction",
* "key": "👍",
* "count": 3
* }
* ],
* "limited": false,
* "count": 1
* },
* </code>
*/

@JsonClass(generateAdapter = true)
data class AggregatedAnnotation (
override val limited: Boolean? = false,
override val count: Int? = 0,
val chunk: List<RelationChunkInfo>? = null

) : UnsignedRelationInfo

View File

@ -0,0 +1,53 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model

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

/**
* <code>
* {
* "m.annotation": {
* "chunk": [
* {
* "type": "m.reaction",
* "key": "👍",
* "count": 3
* }
* ],
* "limited": false,
* "count": 1
* },
* "m.reference": {
* "chunk": [
* {
* "type": "m.room.message",
* "event_id": "$some_event_id"
* }
* ],
* "limited": false,
* "count": 1
* }
* }
* </code>
*/

@JsonClass(generateAdapter = true)
data class AggregatedRelations(
@Json(name = "m.annotation") val annotations: AggregatedAnnotation? = null,
@Json(name = "m.reference") val references: DefaultUnsignedRelationInfo? = null
)

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class DefaultUnsignedRelationInfo(
override val limited: Boolean? = false,
override val count: Int? = 0,
val chunk: List<Map<String, Any>>? = null

) : UnsignedRelationInfo

View File

@ -31,11 +31,20 @@ typealias Content = JsonDict
/**
* This methods is a facility method to map a json content to a model.
*/
inline fun <reified T> Content?.toModel(): T? {
inline fun <reified T> Content?.toModel(catchError: Boolean = true): T? {
return this?.let {
val moshi = MoshiProvider.providesMoshi()
val moshiAdapter = moshi.adapter(T::class.java)
return moshiAdapter.fromJsonValue(it)
return try {
moshiAdapter.fromJsonValue(it)
} catch (e: Exception) {
if (catchError) {
Timber.e(e, "To model failed : $e")
null
} else {
throw e
}
}
}
}


View File

@ -74,6 +74,9 @@ object EventType {
const val KEY_VERIFICATION_MAC = "m.key.verification.mac"
const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel"

// Relation Events
const val REACTION = "m.reaction"

private val STATE_EVENTS = listOf(
STATE_ROOM_NAME,
STATE_ROOM_TOPIC,

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model

import com.squareup.moshi.JsonClass

/**
* <code>
* {
* "type": "m.reaction",
* "key": "👍",
* "count": 3
* }
* </code>
*/

@JsonClass(generateAdapter = true)
data class RelationChunkInfo(
val type: String,
val key: String,
val count: Int
)

View File

@ -0,0 +1,31 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model


/**
* Constants defining known event relation types from Matrix specifications.
*/
object RelationType {

/** Lets you define an event which annotates an existing event.*/
const val ANNOTATION = "m.annotation"
/** Lets you define an event which replaces an existing event.*/
const val REPLACE = "m.replace"
/** ets you define an event which references an existing event.*/
const val REFERENCE = "m.reference"

}

View File

@ -24,5 +24,6 @@ data class UnsignedData(
@Json(name = "age") val age: Long?,
@Json(name = "redacted_because") val redactedEvent: Event? = null,
@Json(name = "transaction_id") val transactionId: String? = null,
@Json(name = "prev_content") val prevContent: Map<String, Any>? = null
@Json(name = "prev_content") val prevContent: Map<String, Any>? = null,
@Json(name = "m.relations") val relations: AggregatedRelations? = null
)

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.matrix.android.api.session.events.model


interface UnsignedRelationInfo {
val limited : Boolean?
val count: Int?
}

View File

@ -18,8 +18,9 @@ package im.vector.matrix.android.api.session.room

import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.session.room.crypto.RoomCryptoService
import im.vector.matrix.android.api.session.room.members.RoomMembersService
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
@ -28,11 +29,13 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineService
/**
* This interface defines methods to interact within a room.
*/
interface Room : TimelineService,
interface Room :
TimelineService,
SendService,
ReadService,
RoomMembersService,
MembershipService,
StateService,
RelationService,
RoomCryptoService {

/**

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019 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.matrix.android.api.session.room

import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.api.util.Cancelable

/**
* This interface defines methods to get and join public rooms. It's implemented at the session level.
*/
interface RoomDirectoryService {

/**
* Get rooms from directory
*/
fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams,
callback: MatrixCallback<PublicRoomsResponse>): Cancelable

/**
* Join a room by id
*/
fun joinRoom(roomId: String,
callback: MatrixCallback<Unit>)

/**
* Fetches the overall metadata about protocols supported by the homeserver.
* Includes both the available protocols and all fields required for queries against each protocol.
*/
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>)

}

View File

@ -24,9 +24,9 @@ import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.util.Cancelable

/**
* This interface defines methods to retrieve room members of a room. It's implemented at the room level.
* This interface defines methods to handling membership. It's implemented at the room level.
*/
interface RoomMembersService {
interface MembershipService {

/**
* This methods load all room members if it was done yet.
@ -54,4 +54,15 @@ interface RoomMembersService {
*/
fun invite(userId: String, callback: MatrixCallback<Unit>)

/**
* Join the room
*/
fun join(callback: MatrixCallback<Unit>)

/**
* Leave the room.
*
*/
fun leave(callback: MatrixCallback<Unit>)

}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model

import im.vector.matrix.android.api.session.events.model.Content

data class EditAggregatedSummary(
val aggregatedContent: Content? = null,
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
val sourceEvents: List<String>,
val localEchos: List<String>,
val lastEditTs: Long = 0
)

View File

@ -13,15 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.api.session.room.model

/**
* Represents the membership of the current auth user on a room.
*/
enum class MyMembership {
JOINED,
LEFT,
INVITED,
NONE
}
data class EventAnnotationsSummary(
var eventId: String,
var reactionsSummary: List<ReactionAggregatedSummary>,
var editSummary: EditAggregatedSummary?
)

View File

@ -19,10 +19,12 @@ package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json

/**
* Represents the membership of a user on a room. Linked to a [RoomMember]
* Represents the membership of a user on a room
*/
enum class Membership(val value: String) {

NONE("none"),

@Json(name = "invite")
INVITE("invite"),

@ -38,4 +40,8 @@ enum class Membership(val value: String) {
@Json(name = "ban")
BAN("ban");

fun isLeft(): Boolean {
return this == KNOCK || this == LEAVE || this == BAN
}

}

View File

@ -0,0 +1,10 @@
package im.vector.matrix.android.api.session.room.model

data class ReactionAggregatedSummary(
val key: String, // "👍"
val count: Int, // 8
val addedByMe: Boolean, // true
val firstTimestamp: Long, // unix timestamp
val sourceEvents: List<String>,
val localEchoEvents: List<String>
)

View File

@ -31,7 +31,8 @@ data class RoomSummary(
val isDirect: Boolean = false,
val lastMessage: Event? = null,
val otherMemberIds: List<String> = emptyList(),
var notificationCount: Int = 0,
var highlightCount: Int = 0,
var tags: List<RoomTag> = emptyList()
val notificationCount: Int = 0,
val highlightCount: Int = 0,
val tags: List<RoomTag> = emptyList(),
val membership: Membership = Membership.NONE
)

View File

@ -21,7 +21,7 @@ import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class FileInfo(
@Json(name = "mimetype") val mimeType: String,
@Json(name = "mimetype") val mimeType: String?,
@Json(name = "size") val size: Long = 0,
@Json(name = "thumbnail_info") val thumbnailInfo: ThumbnailInfo? = null,
@Json(name = "thumbnail_url") val thumbnailUrl: String? = null

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageAudioContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "info") val info: AudioInfo? = null,
@Json(name = "url") val url: String? = null
@Json(name = "url") val url: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -16,8 +16,13 @@

package im.vector.matrix.android.api.session.room.model.message

import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent


interface MessageContent {
val type: String
val body: String
val relatesTo: RelationDefaultContent?
val newContent: Content?
}

View File

@ -18,9 +18,13 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageDefaultContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageEmoteContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "format") val format: String? = null,
@Json(name = "formatted_body") val formattedBody: String? = null
@Json(name = "formatted_body") val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,6 +18,8 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageFileContent(
@ -25,5 +27,7 @@ data class MessageFileContent(
@Json(name = "body") override val body: String,
@Json(name = "filename") val filename: String? = null,
@Json(name = "info") val info: FileInfo? = null,
@Json(name = "url") val url: String? = null
@Json(name = "url") val url: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageImageContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "info") val info: ImageInfo? = null,
@Json(name = "url") val url: String? = null
@Json(name = "url") val url: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageLocationContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "geo_uri") val geoUri: String,
@Json(name = "info") val info: LocationInfo? = null
@Json(name = "info") val info: LocationInfo? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageNoticeContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "format") val format: String? = null,
@Json(name = "formatted_body") val formattedBody: String? = null
@Json(name = "formatted_body") val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageTextContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "format") val format: String? = null,
@Json(name = "formatted_body") val formattedBody: String? = null
@Json(name = "formatted_body") val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -18,16 +18,16 @@ package im.vector.matrix.android.api.session.room.model.message

object MessageType {

val MSGTYPE_TEXT = "m.text"
val MSGTYPE_EMOTE = "m.emote"
val MSGTYPE_NOTICE = "m.notice"
val MSGTYPE_IMAGE = "m.image"
val MSGTYPE_AUDIO = "m.audio"
val MSGTYPE_VIDEO = "m.video"
val MSGTYPE_LOCATION = "m.location"
val MSGTYPE_FILE = "m.file"
val FORMAT_MATRIX_HTML = "org.matrix.custom.html"
const val MSGTYPE_TEXT = "m.text"
const val MSGTYPE_EMOTE = "m.emote"
const val MSGTYPE_NOTICE = "m.notice"
const val MSGTYPE_IMAGE = "m.image"
const val MSGTYPE_AUDIO = "m.audio"
const val MSGTYPE_VIDEO = "m.video"
const val MSGTYPE_LOCATION = "m.location"
const val MSGTYPE_FILE = "m.file"
const val FORMAT_MATRIX_HTML = "org.matrix.custom.html"
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
// Because sticker isn't a message type but a event type without msgtype field
val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
}

View File

@ -18,11 +18,15 @@ package im.vector.matrix.android.api.session.room.model.message

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent

@JsonClass(generateAdapter = true)
data class MessageVideoContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "body") override val body: String,
@Json(name = "info") val info: VideoInfo? = null,
@Json(name = "url") val url: String? = null
@Json(name = "url") val url: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@ -0,0 +1,9 @@
package im.vector.matrix.android.api.session.room.model.relation

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

@JsonClass(generateAdapter = true)
data class ReactionContent(
@Json(name = "m.relates_to") val relatesTo: ReactionInfo? = null
)

View File

@ -0,0 +1,13 @@
package im.vector.matrix.android.api.session.room.model.relation

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

@JsonClass(generateAdapter = true)
data class ReactionInfo(
@Json(name = "rel_type") override val type: String?,
@Json(name = "event_id") override val eventId: String,
val key: String,
//always null for reaction
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null
) : RelationContent

View File

@ -0,0 +1,7 @@
package im.vector.matrix.android.api.session.room.model.relation

interface RelationContent {
val type: String?
val eventId: String?
val inReplyTo: ReplyToContent?
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.relation

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

@JsonClass(generateAdapter = true)
data class RelationDefaultContent(
@Json(name = "rel_type") override val type: String?,
@Json(name = "event_id") override val eventId: String?,
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null
) : RelationContent

View File

@ -0,0 +1,94 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.relation

import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.util.Cancelable

/**
* In some cases, events may wish to reference other events.
* This could be to form a thread of messages for the user to follow along with,
* or to provide more context as to what a particular event is describing.
* Relation are used to associate new information with an existing event.
*
* Relations are events which have an m.relates_to mixin in their contents,
* and the new information they convey is expressed in their usual event type and content.
*
* Three types of relations are defined, each defining different behaviour when aggregated:
*
* m.annotation - lets you define an event which annotates an existing event.
* When aggregated, groups events together based on key and returns a count.
* (aka SQL's COUNT) These are primarily intended for handling reactions.
*
* m.replace - lets you define an event which replaces an existing event.
* When aggregated, returns the most recent replacement event. (aka SQL's MAX)
* These are primarily intended for handling edits.
*
* m.reference - lets you define an event which references an existing event.
* When aggregated, currently doesn't do anything special, but in future could bundle chains of references (i.e. threads).
* These are primarily intended for handling replies (and in future threads).
*/
interface RelationService {


/**
* Sends a reaction (emoji) to the targetedEvent.
* @param reaction the reaction (preferably emoji)
* @param targetEventId the id of the event being reacted
*/
fun sendReaction(reaction: String, targetEventId: String): Cancelable


/**
* Undo a reaction (emoji) to the targetedEvent.
* @param reaction the reaction (preferably emoji)
* @param targetEventId the id of the event being reacted
* @param myUserId used to know if a reaction event was made by the user
*/
fun undoReaction(reaction: String, targetEventId: String, myUserId: String)//: Cancelable


/**
* Update a quick reaction (toggle).
* If you have reacted with agree and then you click on disagree, this call will delete(redact)
* the disagree and add the agree
* If you click on a reaction that you already reacted with, it will undo it
* @param reaction the reaction (preferably emoji)
* @param oppositeReaction the opposite reaction(preferably emoji)
* @param targetEventId the id of the event being reacted
* @param myUserId used to know if a reaction event was made by the user
*/
fun updateQuickReaction(reaction: String, oppositeReaction: String, targetEventId: String, myUserId: String)


/**
* Edit a text message body. Limited to "m.text" contentType
* @param targetEventId The event to edit
* @param newBodyText The edited body
* @param compatibilityBodyText The text that will appear on clients that don't support yet edition
*/
fun editTextMessage(targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String = "* $newBodyText"): Cancelable


/**
* Reply to an event in the timeline (must be in same room)
* https://matrix.org/docs/spec/client_server/r0.4.0.html#id350
* @param eventReplied the event referenced by the reply
* @param replyText the reply text
*/
fun replyToMessage(eventReplied: Event, replyText: String): Cancelable?

}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.relation

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

@JsonClass(generateAdapter = true)
data class ReplyToContent(
@Json(name = "event_id") val eventId: String
)

View File

@ -0,0 +1,87 @@
/*
* Copyright 2014 OpenMarket 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.matrix.android.api.session.room.model.roomdirectory

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

/**
* Class representing the objects returned by /publicRooms call.
*/
@JsonClass(generateAdapter = true)
data class PublicRoom(

/**
* Aliases of the room. May be empty.
*/
@Json(name = "aliases")
var aliases: List<String>? = null,

/**
* The canonical alias of the room, if any.
*/
@Json(name = "canonical_alias")
var canonicalAlias: String? = null,

/**
* The name of the room, if any.
*/
@Json(name = "name")
var name: String? = null,

/**
* Required. The number of members joined to the room.
*/
@Json(name = "num_joined_members")
var numJoinedMembers: Int = 0,

/**
* Required. The ID of the room.
*/
@Json(name = "room_id")
var roomId: String,

/**
* The topic of the room, if any.
*/
@Json(name = "topic")
var topic: String? = null,

/**
* Required. Whether the room may be viewed by guest users without joining.
*/
@Json(name = "world_readable")
var worldReadable: Boolean = false,

/**
* Required. Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.
*/
@Json(name = "guest_can_join")
var guestCanJoin: Boolean = false,

/**
* The URL for the room's avatar, if one is set.
*/
@Json(name = "avatar_url")
var avatarUrl: String? = null,

/**
* Undocumented item
*/
@Json(name = "m.federate")
var isFederated: Boolean = false

)

View File

@ -0,0 +1,31 @@
/*
* Copyright 2014 OpenMarket 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.matrix.android.api.session.room.model.roomdirectory

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

/**
* Class to define a filter to retrieve public rooms
*/
@JsonClass(generateAdapter = true)
data class PublicRoomsFilter(
/**
* A string to search for in the room metadata, e.g. name, topic, canonical alias etc. (Optional).
*/
@Json(name = "generic_search_term")
var searchTerm: String? = null
)

View File

@ -0,0 +1,57 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2017 Vector Creations 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.matrix.android.api.session.room.model.roomdirectory

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

/**
* Class to pass parameters to get the public rooms list
*/
@JsonClass(generateAdapter = true)
data class PublicRoomsParams(
/**
* Limit the number of results returned.
*/
@Json(name = "limit")
var limit: Int? = null,

/**
* A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms.
* The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag.
*/
@Json(name = "since")
var since: String? = null,

/**
* Filter to apply to the results.
*/
@Json(name = "filter")
var filter: PublicRoomsFilter? = null,

/**
* Whether or not to include all known networks/protocols from application services on the homeserver. Defaults to false.
*/
@Json(name = "include_all_networks")
var includeAllNetworks: Boolean = false,

/**
* The specific third party network/protocol to request from the homeserver. Can only be used if include_all_networks is false.
*/
@Json(name = "third_party_instance_id")
var thirdPartyInstanceId: String? = null
)

View File

@ -0,0 +1,50 @@
/*
* Copyright 2014 OpenMarket 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.matrix.android.api.session.room.model.roomdirectory

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

/**
* Class representing the public rooms request response
*/
@JsonClass(generateAdapter = true)
data class PublicRoomsResponse(
/**
* A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.
*/
@Json(name = "next_batch")
var nextBatch: String? = null,

/**
* A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch,
* i.e. this is the first batch.
*/
@Json(name = "prev_batch")
var prevBatch: String? = null,

/**
* A paginated chunk of public rooms.
*/
@Json(name = "chunk")
var chunk: List<PublicRoom>? = null,

/**
* An estimate on the total number of public rooms, if the server has an estimate.
*/
@Json(name = "total_room_count_estimate")
var totalRoomCountEstimate: Int? = null
)

View File

@ -0,0 +1,36 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.thirdparty

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

@JsonClass(generateAdapter = true)
data class FieldType(
/**
* Required. A regular expression for validation of a field's value. This may be relatively coarse to verify the value as the application
* service providing this protocol may apply additional
*/
@Json(name = "regexp")
val regexp: String? = null,

/**
* Required. An placeholder serving as a valid example of the field value.
*/
@Json(name = "placeholder")
val placeholder: String? = null
)

View File

@ -0,0 +1,55 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.thirdparty

/**
* This class describes a rooms directory server.
*/
data class RoomDirectoryData(

/**
* The server name (might be null)
* Set null when the server is the current user's home server.
*/
val homeServer: String? = null,

/**
* The display name (the server description)
*/
val displayName: String = DEFAULT_HOME_SERVER_NAME,

/**
* The third party server identifier
*/
val thirdPartyInstanceId: String? = null,

/**
* Tell if all the federated servers must be included
*/
val includeAllNetworks: Boolean = false,

/**
* the avatar url
*/
val avatarUrl: String? = null
) {

companion object {
const val DEFAULT_HOME_SERVER_NAME = "Matrix"
}

}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.thirdparty

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

@JsonClass(generateAdapter = true)
data class ThirdPartyProtocol(
/**
* Required. Fields which may be used to identify a third party user. These should be ordered to suggest the way that entities may be grouped,
* where higher groupings are ordered first. For example, the name of a network should be searched before the nickname of a user.
*/
@Json(name = "user_fields")
var userFields: List<String>? = null,

/**
* Required. Fields which may be used to identify a third party location. These should be ordered to suggest the way that
* entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be
* searched before the name of a channel.
*/
@Json(name = "location_fields")
var locationFields: List<String>? = null,

/**
* Required. A content URI representing an icon for the third party protocol.
*
* FIXDOC: This field was not present in legacy Riot, and it is sometimes sent by the server (no not Required?)
*/
@Json(name = "icon")
var icon: String? = null,

/**
* Required. The type definitions for the fields defined in the user_fields and location_fields. Each entry in those arrays MUST have an entry here.
* The string key for this object is field name itself.
*
* May be an empty object if no fields are defined.
*/
@Json(name = "field_types")
var fieldTypes: Map<String, FieldType>? = null,

/**
* Required. A list of objects representing independent instances of configuration. For example, multiple networks on IRC
* if multiple are provided by the same application service.
*/
@Json(name = "instances")
var instances: List<ThirdPartyProtocolInstance>? = null
)

View File

@ -0,0 +1,60 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model.thirdparty

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

@JsonClass(generateAdapter = true)
data class ThirdPartyProtocolInstance(
/**
* Required. A human-readable description for the protocol, such as the name.
*/
@Json(name = "desc")
var desc: String? = null,

/**
* An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object.
*/
@Json(name = "icon")
var icon: String? = null,

/**
* Required. Preset values for fields the client may use to search by.
*/
@Json(name = "fields")
var fields: Map<String, Any>? = null,

/**
* Required. A unique identifier across all instances.
*/
@Json(name = "network_id")
var networkId: String? = null,

/**
* FIXDOC Not documented on matrix.org doc
*/
@Json(name = "instance_id")
var instanceId: String? = null,


/**
* FIXDOC Not documented on matrix.org doc
*/
@Json(name = "bot_user_id")
var botUserId: String? = null
)

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.room.send

import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.util.Cancelable

@ -30,9 +31,18 @@ interface SendService {
* Method to send a text message asynchronously.
* @param text the text message to send
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
* @return a [Cancelable]
*/
fun sendTextMessage(text: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable
fun sendTextMessage(text: String, msgType: String = MessageType.MSGTYPE_TEXT, autoMarkdown: Boolean = false): Cancelable

/**
* Method to send a text message with a formatted body.
* @param text the text message to send
* @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
* @return a [Cancelable]
*/
fun sendFormattedTextMessage(text: String,formattedText: String): Cancelable

/**
* Method to send a media asynchronously.
@ -48,5 +58,11 @@ interface SendService {
*/
fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable

/**
* Redacts (delete) the given event.
* @param event The event to redact
* @param reason Optional reason string
*/
fun redactEvent(event: Event, reason: String?): Cancelable

}

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room.timeline

import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.send.SendState

@ -32,7 +33,8 @@ data class TimelineEvent(
val displayIndex: Int,
val senderName: String?,
val senderAvatar: String?,
val sendState: SendState
val sendState: SendState,
val annotations: EventAnnotationsSummary? = null
) {

val metadata = HashMap<String, Any>()

View File

@ -18,6 +18,7 @@

package im.vector.matrix.android.api.session.user

import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.session.user.model.User

/**
@ -32,4 +33,11 @@ interface UserService {
*/
fun getUser(userId: String): User?

/**
* Observe a live user from a userId
* @param userId the userId to look for.
* @return a Livedata of user with userId
*/
fun observeUser(userId: String): LiveData<User?>

}

View File

@ -39,7 +39,6 @@ import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
@ -62,8 +61,8 @@ import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
@ -116,20 +115,18 @@ internal class CryptoManager(
// Actions
private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val megolmSessionDataImporter: MegolmSessionDataImporter,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
// Repository
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
// Repository
private val megolmEncryptionFactory: MXMegolmEncryptionFactory,
private val olmEncryptionFactory: MXOlmEncryptionFactory,
// Tasks
private val deleteDeviceTask: DeleteDeviceTask,
// Tasks
private val getDevicesTask: GetDevicesTask,
private val setDeviceNameTask: SetDeviceNameTask,
private val uploadKeysTask: UploadKeysTask,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val monarchy: Monarchy,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
// TaskExecutor
private val taskExecutor: TaskExecutor
) : CryptoService {


View File

@ -233,12 +233,11 @@ internal class CryptoModule {
olmManager = get(),
setDeviceVerificationAction = get(),
megolmSessionDataImporter = get(),
ensureOlmSessionsForDevicesAction = get(),
warnOnUnknownDevicesRepository = get(),
megolmEncryptionFactory = get(),
olmEncryptionFactory = get(),
// Tasks
deleteDeviceTask = get(),
// Tasks
getDevicesTask = get(),
setDeviceNameTask = get(),
uploadKeysTask = get(),

View File

@ -86,10 +86,9 @@ internal fun ChunkEntity.add(roomId: String,
isUnlinked: Boolean = false) {

assertIsManaged()
if (event.eventId.isNullOrEmpty() || this.events.fastContains(event.eventId)) {
if (event.eventId != null && events.fastContains(event.eventId)) {
return
}

var currentDisplayIndex = lastDisplayIndex(direction, 0)
if (direction == PaginationDirection.FORWARDS) {
currentDisplayIndex += 1

View File

@ -0,0 +1,36 @@
package im.vector.matrix.android.internal.database.mapper

import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity

internal object EventAnnotationsSummaryMapper {
fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary {
return EventAnnotationsSummary(
eventId = annotationsSummary.eventId,
reactionsSummary = annotationsSummary.reactionsSummary.toList().map {
ReactionAggregatedSummary(
it.key,
it.count,
it.addedByMe,
it.firstTimestamp,
it.sourceEvents.toList(),
it.sourceLocalEcho.toList()
)
},
editSummary = annotationsSummary.editSummary?.let {
EditAggregatedSummary(
ContentMapper.map(it.aggregatedContent),
it.sourceEvents.toList(),
it.sourceLocalEchoEvents.toList(),
it.lastEditTs
)
}
)
}
}

internal fun EventAnnotationsSummaryEntity.asDomain(): EventAnnotationsSummary {
return EventAnnotationsSummaryMapper.map(this)
}

View File

@ -19,14 +19,17 @@ package im.vector.matrix.android.internal.database.mapper
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.UnsignedData
import im.vector.matrix.android.internal.database.model.EventEntity

import im.vector.matrix.android.internal.di.MoshiProvider
import java.util.*

internal object EventMapper {


fun map(event: Event, roomId: String): EventEntity {
val uds = if (event.unsignedData == null) null
else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData)
val eventEntity = EventEntity()
eventEntity.eventId = event.eventId ?: ""
eventEntity.eventId = event.eventId ?: UUID.randomUUID().toString()
eventEntity.roomId = event.roomId ?: roomId
eventEntity.content = ContentMapper.map(event.content)
val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent
@ -37,10 +40,14 @@ internal object EventMapper {
eventEntity.originServerTs = event.originServerTs
eventEntity.redacts = event.redacts
eventEntity.age = event.unsignedData?.age ?: event.originServerTs
eventEntity.unsignedData = uds
return eventEntity
}

fun map(eventEntity: EventEntity): Event {
//TODO proxy the event to only parse unsigned data when accessed?
var ud = if (eventEntity.unsignedData.isNullOrBlank()) null
else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).fromJson(eventEntity.unsignedData)
return Event(
type = eventEntity.type,
eventId = eventEntity.eventId,
@ -50,7 +57,7 @@ internal object EventMapper {
sender = eventEntity.sender,
stateKey = eventEntity.stateKey,
roomId = eventEntity.roomId,
unsignedData = UnsignedData(eventEntity.age),
unsignedData = ud,
redacts = eventEntity.redacts
)
}

View File

@ -37,7 +37,8 @@ internal object RoomSummaryMapper {
otherMemberIds = roomSummaryEntity.otherMemberIds.toList(),
highlightCount = roomSummaryEntity.highlightCount,
notificationCount = roomSummaryEntity.notificationCount,
tags = tags
tags = tags,
membership = roomSummaryEntity.membership
)
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019 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.matrix.android.internal.database.mapper

import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.database.model.UserEntity

internal object UserMapper {

fun map(userEntity: UserEntity): User {
return User(
userEntity.userId,
userEntity.displayName,
userEntity.avatarUrl
)
}
}

internal fun UserEntity.asDomain(): User {
return UserMapper.map(this)
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019 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.matrix.android.internal.database.model

import io.realm.RealmList
import io.realm.RealmObject

/**
* Keep the latest state of edition of a message
*/
internal open class EditAggregatedSummaryEntity(
var aggregatedContent: String? = null,
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
var sourceEvents: RealmList<String> = RealmList(),
var sourceLocalEchoEvents: RealmList<String> = RealmList(),
var lastEditTs: Long = 0
) : RealmObject() {

companion object

}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019 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.matrix.android.internal.database.model

import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey


internal open class EventAnnotationsSummaryEntity(
@PrimaryKey
var eventId: String = "",
var roomId: String? = null,
var reactionsSummary: RealmList<ReactionAggregatedSummaryEntity> = RealmList(),
var editSummary: EditAggregatedSummaryEntity? = null
) : RealmObject() {

companion object

}

View File

@ -36,6 +36,7 @@ internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUI
var originServerTs: Long? = null,
@Index var sender: String? = null,
var age: Long? = 0,
var unsignedData: String? = null,
var redacts: String? = null,
@Index var stateIndex: Int = 0,
@Index var displayIndex: Int = 0,

View File

@ -16,7 +16,7 @@

package im.vector.matrix.android.internal.database.model

import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
@ -26,10 +26,10 @@ internal open class GroupEntity(@PrimaryKey var groupId: String = ""

) : RealmObject() {

private var membershipStr: String = MyMembership.NONE.name
private var membershipStr: String = Membership.NONE.name

@delegate:Ignore
var membership: MyMembership by Delegates.observable(MyMembership.valueOf(membershipStr)) { _, _, newValue ->
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
membershipStr = newValue.name
}


View File

@ -0,0 +1,26 @@
package im.vector.matrix.android.internal.database.model

import io.realm.RealmList
import io.realm.RealmObject

/**
* Aggregated Summary of a reaction.
*/
internal open class ReactionAggregatedSummaryEntity(
// The reaction String 😀
var key: String = "",
// Number of time this reaction was selected
var count: Int = 0,
// Did the current user sent this reaction
var addedByMe: Boolean = false,
// The first time this reaction was added (for ordering purpose)
var firstTimestamp: Long = 0,
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
var sourceEvents: RealmList<String> = RealmList(),
// List of transaction ids for local echos
var sourceLocalEcho: RealmList<String> = RealmList()
) : RealmObject() {

companion object

}

View File

@ -16,7 +16,7 @@

package im.vector.matrix.android.internal.database.model

import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
@ -30,10 +30,10 @@ internal open class RoomEntity(@PrimaryKey var roomId: String = "",
var areAllMembersLoaded: Boolean = false
) : RealmObject() {

private var membershipStr: String = MyMembership.NONE.name
private var membershipStr: String = Membership.NONE.name

@delegate:Ignore
var membership: MyMembership by Delegates.observable(MyMembership.valueOf(membershipStr)) { _, _, newValue ->
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
membershipStr = newValue.name
}


View File

@ -16,9 +16,12 @@

package im.vector.matrix.android.internal.database.model

import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import kotlin.properties.Delegates

internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var displayName: String? = "",
@ -35,6 +38,13 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var tags: RealmList<RoomTagEntity> = RealmList()
) : RealmObject() {

private var membershipStr: String = Membership.NONE.name

@delegate:Ignore
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
membershipStr = newValue.name
}

companion object

}

View File

@ -33,6 +33,9 @@ import io.realm.annotations.RealmModule
RoomSummaryEntity::class,
RoomTagEntity::class,
SyncEntity::class,
UserEntity::class
UserEntity::class,
EventAnnotationsSummaryEntity::class,
ReactionAggregatedSummaryEntity::class,
EditAggregatedSummaryEntity::class
])
internal class SessionRealmModule

View File

@ -0,0 +1,27 @@
package im.vector.matrix.android.internal.database.query

import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntityFields
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where

internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
val query = realm.where<EventAnnotationsSummaryEntity>()
query.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
return query
}

internal fun EventAnnotationsSummaryEntity.Companion.whereInRoom(realm: Realm, roomId: String?): RealmQuery<EventAnnotationsSummaryEntity> {
val query = realm.where<EventAnnotationsSummaryEntity>()
if (roomId != null) {
query.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
}
return query
}


internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, eventId: String): EventAnnotationsSummaryEntity {
val obj = realm.createObject(EventAnnotationsSummaryEntity::class.java, eventId)
return obj
}

View File

@ -52,6 +52,7 @@ internal fun EventEntity.Companion.where(realm: Realm,
}
}


internal fun EventEntity.Companion.latestEvent(realm: Realm,
roomId: String,
includedTypes: List<String> = emptyList(),

View File

@ -16,7 +16,7 @@

package im.vector.matrix.android.internal.database.query

import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.database.model.GroupEntity
import im.vector.matrix.android.internal.database.model.GroupEntityFields
import io.realm.Realm
@ -27,7 +27,7 @@ internal fun GroupEntity.Companion.where(realm: Realm, roomId: String): RealmQue
return realm.where<GroupEntity>().equalTo(GroupEntityFields.GROUP_ID, roomId)
}

internal fun GroupEntity.Companion.where(realm: Realm, membership: MyMembership? = null): RealmQuery<GroupEntity> {
internal fun GroupEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<GroupEntity> {
val query = realm.where<GroupEntity>()
if (membership != null) {
query.equalTo(GroupEntityFields.MEMBERSHIP_STR, membership.name)

View File

@ -16,7 +16,7 @@

package im.vector.matrix.android.internal.database.query

import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomEntityFields
@ -28,7 +28,7 @@ internal fun RoomEntity.Companion.where(realm: Realm, roomId: String): RealmQuer
return realm.where<RoomEntity>().equalTo(RoomEntityFields.ROOM_ID, roomId)
}

internal fun RoomEntity.Companion.where(realm: Realm, membership: MyMembership? = null): RealmQuery<RoomEntity> {
internal fun RoomEntity.Companion.where(realm: Realm, membership: Membership? = null): RealmQuery<RoomEntity> {
val query = realm.where<RoomEntity>()
if (membership != null) {
query.equalTo(RoomEntityFields.MEMBERSHIP_STR, membership.name)

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.StringProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.asCoroutineDispatcher
import org.koin.dsl.module.module
@ -46,6 +47,9 @@ class MatrixModule(private val context: Context) {
single {
TaskExecutor(get())
}
single {
StringProvider(context.resources)
}

single {
BackgroundDetectionObserver()

View File

@ -37,13 +37,18 @@ import im.vector.matrix.android.api.session.group.Group
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.CryptoModule
@ -78,6 +83,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
private val liveEntityUpdaters by inject<List<LiveEntityObserver>>()
private val sessionListeners by inject<SessionListeners>()
private val roomService by inject<RoomService>()
private val roomDirectoryService by inject<RoomDirectoryService>()
private val groupService by inject<GroupService>()
private val userService by inject<UserService>()
private val filterService by inject<FilterService>()
@ -195,6 +201,23 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
return roomService.liveRoomSummaries()
}

// ROOM DIRECTORY SERVICE

override fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, callback: MatrixCallback<PublicRoomsResponse>): Cancelable {
assert(isOpen)
return roomDirectoryService.getPublicRooms(server, publicRoomsParams, callback)
}

override fun joinRoom(roomId: String, callback: MatrixCallback<Unit>) {
assert(isOpen)
return roomDirectoryService.joinRoom(roomId, callback)
}

override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
assert(isOpen)
return roomDirectoryService.getThirdPartyProtocol(callback)
}

// GROUP SERVICE

override fun getGroup(groupId: String): Group? {
@ -232,6 +255,11 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
return userService.getUser(userId)
}

override fun observeUser(userId: String): LiveData<User?> {
assert(isOpen)
return userService.observeUser(userId)
}

// CRYPTO SERVICE

override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {

View File

@ -21,6 +21,7 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService
@ -33,11 +34,13 @@ import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
import im.vector.matrix.android.internal.session.filter.*
import im.vector.matrix.android.internal.session.group.DefaultGroupService
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
import im.vector.matrix.android.internal.session.room.DefaultRoomService
import im.vector.matrix.android.internal.session.room.RoomAvatarResolver
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.members.RoomMemberDisplayNameResolver
import im.vector.matrix.android.internal.session.room.*
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
import im.vector.matrix.android.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask
import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask
import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyProtocolsTask
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.membership.RoomMemberDisplayNameResolver
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
import im.vector.matrix.android.internal.session.signout.DefaultSignOutService
import im.vector.matrix.android.internal.session.user.DefaultUserService
@ -106,6 +109,18 @@ internal class SessionModule(private val sessionParams: SessionParams) {
DefaultRoomService(get(), get(), get(), get()) as RoomService
}

scope(DefaultSession.SCOPE) {
DefaultGetPublicRoomTask(get()) as GetPublicRoomTask
}

scope(DefaultSession.SCOPE) {
DefaultGetThirdPartyProtocolsTask(get()) as GetThirdPartyProtocolsTask
}

scope(DefaultSession.SCOPE) {
DefaultRoomDirectoryService(get(), get(), get(), get()) as RoomDirectoryService
}

scope(DefaultSession.SCOPE) {
DefaultGroupService(get()) as GroupService
}
@ -149,9 +164,11 @@ internal class SessionModule(private val sessionParams: SessionParams) {

scope(DefaultSession.SCOPE) {
val groupSummaryUpdater = GroupSummaryUpdater(get())
val eventsPruner = EventsPruner(get())
val userEntityUpdater = UserEntityUpdater(get(), get(), get())
listOf<LiveEntityObserver>(groupSummaryUpdater, eventsPruner, userEntityUpdater)
val aggregationUpdater = EventRelationsAggregationUpdater(get(), get(), get(), get())
//Event pruner must be the last one, because it will clear contents
val eventsPruner = EventsPruner(get(), get(), get(), get())
listOf<LiveEntityObserver>(groupSummaryUpdater, userEntityUpdater, aggregationUpdater, eventsPruner)
}



View File

@ -21,10 +21,19 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver


private const val MATRIX_CONTENT_URI_SCHEME = "mxc://"
internal const val URI_PREFIX_CONTENT_API = "/_matrix/media/v1/"
private const val URI_PREFIX_CONTENT_API = "_matrix/media/v1/"

internal class DefaultContentUrlResolver(private val homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver {

companion object {
fun getUploadUrl(homeServerConnectionConfig: HomeServerConnectionConfig): String {
val baseUrl = homeServerConnectionConfig.homeServerUri.toString()
val sep = if (baseUrl.endsWith("/")) "" else "/"

return baseUrl + sep + URI_PREFIX_CONTENT_API + "upload"
}
}

override fun resolveFullSize(contentUrl: String?): String? {
if (contentUrl?.isValidMatrixContentUrl() == true) {
val baseUrl = homeServerConnectionConfig.homeServerUri.toString()
@ -57,7 +66,10 @@ internal class DefaultContentUrlResolver(private val homeServerConnectionConfig:
fragment = serverAndMediaId.substring(fragmentOffset)
serverAndMediaId = serverAndMediaId.substring(0, fragmentOffset)
}
return baseUrl + prefix + serverAndMediaId + (params ?: "") + fragment

val sep = if (baseUrl.endsWith("/")) "" else "/"

return baseUrl + sep + prefix + serverAndMediaId + (params ?: "") + fragment
}

private fun String.isValidMatrixContentUrl(): Boolean {

View File

@ -21,19 +21,15 @@ import arrow.core.Try.Companion.raise
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.ProgressRequestBody
import okhttp3.HttpUrl
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.*
import java.io.File
import java.io.IOException


internal class FileUploader(private val okHttpClient: OkHttpClient,
private val sessionParams: SessionParams) {
sessionParams: SessionParams) {

private val uploadUrl = sessionParams.homeServerConnectionConfig.homeServerUri.toString() + URI_PREFIX_CONTENT_API + "upload"
private val uploadUrl = DefaultContentUrlResolver.getUploadUrl(sessionParams.homeServerConnectionConfig)

private val moshi = MoshiProvider.providesMoshi()
private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java)
@ -82,7 +78,7 @@ internal class FileUploader(private val okHttpClient: OkHttpClient,
response.body()?.source()?.let {
responseAdapter.fromJson(it)
}
?: throw IOException()
?: throw IOException()
}
}
}

View File

@ -21,8 +21,9 @@ import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.RoomMembersService
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
@ -40,14 +41,16 @@ internal class DefaultRoom(
private val sendService: SendService,
private val stateService: StateService,
private val readService: ReadService,
private val roomMembersService: RoomMembersService,
private val cryptoService: CryptoService
private val cryptoService: CryptoService,
private val relationService: RelationService,
private val roomMembersService: MembershipService
) : Room,
TimelineService by timelineService,
SendService by sendService,
StateService by stateService,
ReadService by readService,
RoomMembersService by roomMembersService {
RelationService by relationService,
MembershipService by roomMembersService {

override val roomSummary: LiveData<RoomSummary> by lazy {
val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm ->

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room

import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask
import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyProtocolsTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith

internal class DefaultRoomDirectoryService(private val getPublicRoomTask: GetPublicRoomTask,
private val joinRoomTask: JoinRoomTask,
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
private val taskExecutor: TaskExecutor) : RoomDirectoryService {

override fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams,
callback: MatrixCallback<PublicRoomsResponse>): Cancelable {
return getPublicRoomTask
.configureWith(GetPublicRoomTask.Params(server, publicRoomsParams))
.dispatchTo(callback)
.executeBy(taskExecutor)
}

override fun joinRoom(roomId: String, callback: MatrixCallback<Unit>) {
joinRoomTask
.configureWith(JoinRoomTask.Params(roomId))
.dispatchTo(callback)
.executeBy(taskExecutor)
}

override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
getThirdPartyProtocolsTask
.configureWith(Unit)
.dispatchTo(callback)
.executeBy(taskExecutor)
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room

import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.where
import io.realm.Realm

/**
* Fetches annotations (reactions, edits...) associated to a given eventEntity from the data layer.
*/
internal class EventRelationExtractor {

fun extractFrom(event: EventEntity, realm: Realm = event.realm): EventAnnotationsSummary? {
return EventAnnotationsSummaryEntity.where(realm, event.eventId).findFirst()?.asDomain()
}
}

View File

@ -0,0 +1,309 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room

import arrow.core.Try
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.*
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.relation.ReactionContent
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.EventMapper
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.query.create
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.tryTransactionAsync
import io.realm.Realm
import timber.log.Timber

internal interface EventRelationsAggregationTask : Task<EventRelationsAggregationTask.Params, Unit> {

data class Params(
val events: List<Pair<Event, SendState>>,
val userId: String
)
}

/**
* Called by EventRelationAggregationUpdater, when new events that can affect relations are inserted in base.
*/
internal class DefaultEventRelationsAggregationTask(private val monarchy: Monarchy) : EventRelationsAggregationTask {

override suspend fun execute(params: EventRelationsAggregationTask.Params): Try<Unit> {
return monarchy.tryTransactionAsync { realm ->
update(realm, params.events, params.userId)
}
}

private fun update(realm: Realm, events: List<Pair<Event, SendState>>, userId: String) {
events.forEach { pair ->
val roomId = pair.first.roomId ?: return@forEach
val event = pair.first
val sendState = pair.second
val isLocalEcho = sendState == SendState.UNSENT
when (event.type) {
EventType.REACTION -> {
//we got a reaction!!
Timber.v("###REACTION in room $roomId")
handleReaction(event, roomId, realm, userId, isLocalEcho)
}
EventType.MESSAGE -> {
if (event.unsignedData?.relations?.annotations != null) {
Timber.v("###REACTION Agreggation in room $roomId for event ${event.eventId}")
handleInitialAggregatedRelations(event, roomId, event.unsignedData.relations.annotations, realm)
} else {
val content: MessageContent? = event.content.toModel()
if (content?.relatesTo?.type == RelationType.REPLACE) {
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
//A replace!
handleReplace(realm, event, content, roomId, isLocalEcho)
}
}

}
EventType.REDACTION -> {
val eventToPrune = event.redacts?.let { EventEntity.where(realm, eventId = it).findFirst() }
?: return
when (eventToPrune.type) {
EventType.MESSAGE -> {
Timber.d("REDACTION for message ${eventToPrune.eventId}")
val unsignedData = EventMapper.map(eventToPrune).unsignedData
?: UnsignedData(null, null)

//was this event a m.replace
val contentModel = ContentMapper.map(eventToPrune.content)?.toModel<MessageContent>()
if (RelationType.REPLACE == contentModel?.relatesTo?.type && contentModel.relatesTo?.eventId != null) {
handleRedactionOfReplace(eventToPrune, contentModel.relatesTo!!.eventId!!, realm)
}

}
EventType.REACTION -> {
handleReactionRedact(eventToPrune, realm, userId)
}
}
}
}
}
}

private fun handleReplace(realm: Realm, event: Event, content: MessageContent, roomId: String, isLocalEcho: Boolean) {
val eventId = event.eventId ?: return
val targetEventId = content.relatesTo?.eventId ?: return
val newContent = content.newContent ?: return
//ok, this is a replace
var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst()
if (existing == null) {
Timber.v("###REPLACE creating no relation summary for ${targetEventId}")
existing = EventAnnotationsSummaryEntity.create(realm, targetEventId)
existing.roomId = roomId
}

//we have it
val existingSummary = existing.editSummary
if (existingSummary == null) {
Timber.v("###REPLACE no edit summary for ${targetEventId}, creating one (localEcho:$isLocalEcho)")
//create the edit summary
val editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java)
editSummary.lastEditTs = event.originServerTs ?: System.currentTimeMillis()
editSummary.aggregatedContent = ContentMapper.map(newContent)
if (isLocalEcho) {
editSummary.sourceLocalEchoEvents.add(eventId)
} else {
editSummary.sourceEvents.add(eventId)
}

existing.editSummary = editSummary
} else {
if (existingSummary.sourceEvents.contains(eventId)) {
//ignore this event, we already know it (??)
Timber.v("###REPLACE ignoring event for summary, it's known ${eventId}")
return
}
val txId = event.unsignedData?.transactionId
//is it a remote echo?
if (!isLocalEcho && existingSummary.sourceLocalEchoEvents.contains(txId)) {
//ok it has already been managed
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
existingSummary.sourceLocalEchoEvents.remove(txId)
existingSummary.sourceEvents.add(event.eventId)
} else if (event.originServerTs ?: 0 > existingSummary.lastEditTs) {
Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)")
existingSummary.lastEditTs = event.originServerTs ?: System.currentTimeMillis()
existingSummary.aggregatedContent = ContentMapper.map(newContent)
existingSummary.sourceEvents.add(eventId)
} else {
//ignore this event for the summary
Timber.v("###REPLACE ignoring event for summary, it's to old ${eventId}")
}
}

}

private fun handleInitialAggregatedRelations(event: Event, roomId: String, aggregation: AggregatedAnnotation, realm: Realm) {
aggregation.chunk?.forEach {
if (it.type == EventType.REACTION) {
val eventId = event.eventId ?: ""
val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
if (existing == null) {
val eventSummary = EventAnnotationsSummaryEntity.create(realm, eventId)
eventSummary.roomId = roomId
val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
sum.key = it.key
sum.firstTimestamp = event.originServerTs ?: 0 //TODO how to maintain order?
sum.count = it.count
eventSummary.reactionsSummary.add(sum)
} else {
//TODO how to handle that
}
}
}
}

private fun handleReaction(event: Event, roomId: String, realm: Realm, userId: String, isLocalEcho: Boolean) {
event.content.toModel<ReactionContent>()?.let { content ->
//rel_type must be m.annotation
if (RelationType.ANNOTATION == content.relatesTo?.type) {
val reaction = content.relatesTo.key
val eventId = content.relatesTo.eventId
val eventSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
?: EventAnnotationsSummaryEntity.create(realm, eventId).apply { this.roomId = roomId }

var sum = eventSummary.reactionsSummary.find { it.key == reaction }
val txId = event.unsignedData?.transactionId
if (isLocalEcho && txId.isNullOrBlank()) {
Timber.w("Received a local echo with no transaction ID")
}
if (sum == null) {
sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
sum.key = reaction
sum.firstTimestamp = event.originServerTs ?: 0
if (isLocalEcho) {
Timber.v("Adding local echo reaction $reaction")
sum.sourceLocalEcho.add(txId)
sum.count = 1
} else {
Timber.v("Adding synced reaction $reaction")
sum.count = 1
sum.sourceEvents.add(event.eventId)
}
sum.addedByMe = sum.addedByMe || (userId == event.sender)
eventSummary.reactionsSummary.add(sum)
} else {
//is this a known event (is possible? pagination?)
if (!sum.sourceEvents.contains(eventId)) {

//check if it's not the sync of a local echo
if (!isLocalEcho && sum.sourceLocalEcho.contains(txId)) {
//ok it has already been counted, just sync the list, do not touch count
Timber.v("Ignoring synced of local echo for reaction $reaction")
sum.sourceLocalEcho.remove(txId)
sum.sourceEvents.add(event.eventId)
} else {
sum.count += 1
if (isLocalEcho) {
Timber.v("Adding local echo reaction $reaction")
sum.sourceLocalEcho.add(txId)
} else {
Timber.v("Adding synced reaction $reaction")
sum.sourceEvents.add(event.eventId)
}

sum.addedByMe = sum.addedByMe || (userId == event.sender)
}

}
}

}
}
}

/**
* Called when an event is deleted
*/
private fun handleRedactionOfReplace(redacted: EventEntity, relatedEventId: String, realm: Realm) {
Timber.d("Handle redaction of m.replace")
val eventSummary = EventAnnotationsSummaryEntity.where(realm, relatedEventId).findFirst()
if (eventSummary == null) {
Timber.w("Redaction of a replace targeting an unknown event $relatedEventId")
return
}
val sourceEvents = eventSummary.editSummary?.sourceEvents
val sourceToDiscard = sourceEvents?.indexOf(redacted.eventId)
if (sourceToDiscard == null) {
Timber.w("Redaction of a replace that was not known in aggregation $sourceToDiscard")
return
}
//Need to remove this event from the redaction list and compute new aggregation state
sourceEvents.removeAt(sourceToDiscard)
val previousEdit = sourceEvents.mapNotNull { EventEntity.where(realm, it).findFirst() }.sortedBy { it.originServerTs }.lastOrNull()
if (previousEdit == null) {
//revert to original
eventSummary.editSummary?.deleteFromRealm()
} else {
//I have the last event
ContentMapper.map(previousEdit.content)?.toModel<MessageContent>()?.newContent?.let { newContent ->
eventSummary.editSummary?.lastEditTs = previousEdit.originServerTs
?: System.currentTimeMillis()
eventSummary.editSummary?.aggregatedContent = ContentMapper.map(newContent)
} ?: run {
Timber.e("Failed to udate edited summary")
//TODO how to reccover that
}

}
}

fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) {
Timber.v("REDACTION of reaction ${eventToPrune.eventId}")
//delete a reaction, need to update the annotation summary if any
val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel()
?: return
val eventThatWasReacted = reactionContent.relatesTo?.eventId ?: return

val reactionKey = reactionContent.relatesTo.key
Timber.v("REMOVE reaction for key $reactionKey")
val summary = EventAnnotationsSummaryEntity.where(realm, eventThatWasReacted).findFirst()
if (summary != null) {
summary.reactionsSummary.where()
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reactionKey)
.findFirst()?.let { aggregation ->
Timber.v("Find summary for key with ${aggregation.sourceEvents.size} known reactions (count:${aggregation.count})")
Timber.v("Known reactions ${aggregation.sourceEvents.joinToString(",")}")
if (aggregation.sourceEvents.contains(eventToPrune.eventId)) {
Timber.v("REMOVE reaction for key $reactionKey")
aggregation.sourceEvents.remove(eventToPrune.eventId)
Timber.v("Known reactions after ${aggregation.sourceEvents.joinToString(",")}")
aggregation.count = aggregation.count - 1
if (eventToPrune.sender == userId) {
//Was it a redact on my reaction?
aggregation.addedByMe = false
}
if (aggregation.count == 0) {
//delete!
aggregation.deleteFromRealm()
}
} else {
Timber.e("## Cannot remove summary from count, corresponding reaction ${eventToPrune.eventId} is not known")
}
}
} else {
Timber.e("## Cannot find summary for key $reactionKey")
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room

import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber

/**
* Acts as a listener of incoming messages in order to incrementally computes a summary of annotations.
* For reactions will build a EventAnnotationsSummaryEntity, ans for edits a EditAggregatedSummaryEntity.
* The summaries can then be extracted and added (as a decoration) to a TimelineEvent for final display.
*/
internal class EventRelationsAggregationUpdater(monarchy: Monarchy,
private val credentials: Credentials,
private val task: EventRelationsAggregationTask,
private val taskExecutor: TaskExecutor) :
RealmLiveEntityObserver<EventEntity>(monarchy) {

override val query = Monarchy.Query<EventEntity> {
EventEntity.where(it)
//mmm why is this query not working?
// EventEntity.byTypes(it, listOf(
// EventType.REDACTION, EventType.MESSAGE, EventType.REDACTION)
// )
}

override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
Timber.v("EventRelationsAggregationUpdater called with ${inserted.size} insertions")
val inserted = inserted
.mapNotNull { it.asDomain() to it.sendState }

val params = EventRelationsAggregationTask.Params(
inserted,
credentials.userId
)

task.configureWith(params)
.executeBy(taskExecutor)

}

}

View File

@ -20,9 +20,12 @@ import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.internal.network.NetworkConstants
import im.vector.matrix.android.internal.session.room.invite.InviteBody
import im.vector.matrix.android.internal.session.room.members.RoomMembersResponse
import im.vector.matrix.android.internal.session.room.membership.RoomMembersResponse
import im.vector.matrix.android.internal.session.room.membership.joining.InviteBody
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.internal.session.room.send.SendResponse
import im.vector.matrix.android.internal.session.room.timeline.EventContextResponse
import im.vector.matrix.android.internal.session.room.timeline.PaginationResponse
@ -31,6 +34,25 @@ import retrofit2.http.*

internal interface RoomAPI {

/**
* Get the third party server protocols.
*
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-thirdparty-protocols
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols")
fun thirdPartyProtocols(): Call<Map<String, ThirdPartyProtocol>>

/**
* Lists the public rooms on the server, with optional filter.
* This API returns paginated responses. The rooms are ordered by the number of joined members, with the largest rooms first.
*
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-publicrooms
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "publicRooms")
fun publicRooms(@Query("server") server: String?,
@Body publicRoomsParams: PublicRoomsParams
): Call<PublicRoomsResponse>

/**
* Create a room.
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-createroom
@ -156,4 +178,58 @@ internal interface RoomAPI {
@Path("state_event_type") stateEventType: String,
@Path("state_key") stateKey: String,
@Body params: Map<String, String>): Call<Unit>

/**
* Send a relation event to a room.
*
* @param txId the transaction Id
* @param roomId the room id
* @param eventType the event type
* @param content the event content
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send_relation/{parent_id}/{relation_type}/{event_type}")
fun sendRelation(@Path("roomId") roomId: String,
@Path("parentId") parent_id: String,
@Path("relation_type") relationType: String,
@Path("eventType") eventType: String,
@Body content: Content?
): Call<SendResponse>

/**
* Join the given room.
*
* @param roomId the room id
* @param params the request body
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/join")
fun join(@Path("roomId") roomId: String,
@Body params: Map<String, String>): Call<Unit>

/**
* Leave the given room.
*
* @param roomId the room id
* @param params the request body
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/leave")
fun leave(@Path("roomId") roomId: String,
@Body params: Map<String, String>): Call<Unit>

/**
* Strips all information out of an event which isn't critical to the integrity of the server-side representation of the room.
* This cannot be undone.
* Users may redact their own events, and any user with a power level greater than or equal to the redact power level of the room may redact events there.
*
* @param txId the transaction Id
* @param roomId the room id
* @param eventId the event to delete
* @param reason json containing reason key {"reason": "Indecent material"}
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/redact/{eventId}/{txnId}")
fun redactEvent(
@Path("txnId") txId: String,
@Path("roomId") roomId: String,
@Path("eventId") parent_id: String,
@Body reason: Map<String, String>
): Call<SendResponse>
}

View File

@ -20,14 +20,14 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomAvatarContent
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.prev
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.session.room.membership.RoomMembers

internal class RoomAvatarResolver(private val monarchy: Monarchy,
private val credentials: Credentials) {
@ -48,7 +48,7 @@ internal class RoomAvatarResolver(private val monarchy: Monarchy,
}
val roomMembers = RoomMembers(realm, roomId)
val members = roomMembers.getLoaded()
if (roomEntity?.membership == MyMembership.INVITED) {
if (roomEntity?.membership == Membership.INVITE) {
if (members.size == 1) {
res = members.entries.first().value.avatarUrl
} else if (members.size > 1) {
@ -57,9 +57,9 @@ internal class RoomAvatarResolver(private val monarchy: Monarchy,
}
} else {
// detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat)
if (roomMembers.getNumberOfJoinedMembers() == 1 && members.isNotEmpty()) {
if (members.size == 1) {
res = members.entries.first().value.avatarUrl
} else if (roomMembers.getNumberOfMembers() == 2 && members.size > 1) {
} else if (members.size == 2) {
val firstOtherMember = members.filterKeys { it != credentials.userId }.values.firstOrNull()
res = firstOtherMember?.avatarUrl
}

View File

@ -19,13 +19,17 @@ package im.vector.matrix.android.internal.session.room
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.session.room.invite.InviteTask
import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask
import im.vector.matrix.android.internal.session.room.read.DefaultReadService
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.state.DefaultStateService
@ -36,25 +40,31 @@ import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
import im.vector.matrix.android.internal.task.TaskExecutor

internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
internal class RoomFactory(private val monarchy: Monarchy,
private val eventFactory: LocalEchoEventFactory,
private val taskExecutor: TaskExecutor,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val inviteTask: InviteTask,
private val sendStateTask: SendStateTask,
private val monarchy: Monarchy,
private val paginationTask: PaginationTask,
private val contextOfEventTask: GetContextOfEventTask,
private val setReadMarkersTask: SetReadMarkersTask,
private val eventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val taskExecutor: TaskExecutor) {
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val updateQuickReactionTask: UpdateQuickReactionTask,
private val joinRoomTask: JoinRoomTask,
private val leaveRoomTask: LeaveRoomTask) {

fun instantiate(roomId: String): Room {
val roomMemberExtractor = SenderRoomMemberExtractor(roomId)
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, cryptoService)
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask)
val relationExtractor = EventRelationExtractor()
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, relationExtractor, cryptoService)
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, paginationTask)
val sendService = DefaultSendService(roomId, eventFactory, cryptoService, monarchy)
val stateService = DefaultStateService(roomId, sendStateTask, taskExecutor)
val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, inviteTask, taskExecutor)
val readService = DefaultReadService(roomId, monarchy, setReadMarkersTask, taskExecutor)
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask)
val reactionService = DefaultRelationService(roomId, eventFactory, findReactionEventForUndoTask, updateQuickReactionTask, monarchy, taskExecutor)

return DefaultRoom(
roomId,
@ -63,8 +73,9 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
sendService,
stateService,
readService,
roomMembersService,
cryptoService
cryptoService,
reactionService,
roomMembersService
)
}


View File

@ -19,20 +19,26 @@ package im.vector.matrix.android.internal.session.room
import im.vector.matrix.android.internal.session.DefaultSession
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask
import im.vector.matrix.android.internal.session.room.invite.DefaultInviteTask
import im.vector.matrix.android.internal.session.room.invite.InviteTask
import im.vector.matrix.android.internal.session.room.members.DefaultLoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.DefaultLoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.joining.DefaultInviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.DefaultJoinRoomTask
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.session.room.membership.leaving.DefaultLeaveRoomTask
import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask
import im.vector.matrix.android.internal.session.room.prune.DefaultPruneEventTask
import im.vector.matrix.android.internal.session.room.prune.PruneEventTask
import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.relation.DefaultFindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.DefaultUpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
import im.vector.matrix.android.internal.session.room.state.SendStateTask
import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask
import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
import im.vector.matrix.android.internal.session.room.timeline.*
import org.koin.dsl.module.module
import retrofit2.Retrofit

@ -67,11 +73,11 @@ class RoomModule {
}

scope(DefaultSession.SCOPE) {
LocalEchoEventFactory(get())
LocalEchoEventFactory(get(), get())
}

scope(DefaultSession.SCOPE) {
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
}

scope(DefaultSession.SCOPE) {
@ -82,9 +88,33 @@ class RoomModule {
DefaultInviteTask(get()) as InviteTask
}

scope(DefaultSession.SCOPE) {
DefaultJoinRoomTask(get()) as JoinRoomTask
}

scope(DefaultSession.SCOPE) {
DefaultLeaveRoomTask(get()) as LeaveRoomTask
}

scope(DefaultSession.SCOPE) {
DefaultSendStateTask(get()) as SendStateTask
}

scope(DefaultSession.SCOPE) {
DefaultFindReactionEventForUndoTask(get()) as FindReactionEventForUndoTask
}

scope(DefaultSession.SCOPE) {
DefaultUpdateQuickReactionTask(get()) as UpdateQuickReactionTask
}

scope(DefaultSession.SCOPE) {
DefaultPruneEventTask(get()) as PruneEventTask
}

scope(DefaultSession.SCOPE) {
DefaultEventRelationsAggregationTask(get()) as EventRelationsAggregationTask
}

}
}

View File

@ -21,6 +21,7 @@ package im.vector.matrix.android.internal.session.room
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
@ -28,8 +29,8 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.latestEvent
import im.vector.matrix.android.internal.database.query.prev
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications
import io.realm.Realm
@ -41,11 +42,12 @@ internal class RoomSummaryUpdater(private val credentials: Credentials,

fun update(realm: Realm,
roomId: String,
membership: Membership? = null,
roomSummary: RoomSyncSummary? = null,
unreadNotifications: RoomSyncUnreadNotifications? = null) {

val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
?: realm.createObject(roomId)

if (roomSummary != null) {
if (roomSummary.heroes.isNotEmpty()) {
@ -65,8 +67,11 @@ internal class RoomSummaryUpdater(private val credentials: Credentials,
if (unreadNotifications?.notificationCount != null) {
roomSummaryEntity.notificationCount = unreadNotifications.notificationCount
}
if (membership != null) {
roomSummaryEntity.membership = membership
}

val lastEvent = EventEntity.latestEvent(realm, roomId, includedTypes = listOf(EventType.MESSAGE))
val lastEvent = EventEntity.latestEvent(realm, roomId)
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
val otherRoomMembers = RoomMembers(realm, roomId).getLoaded().filterKeys { it != credentials.userId }
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room.directory

import arrow.core.Try
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task

internal interface GetPublicRoomTask : Task<GetPublicRoomTask.Params, PublicRoomsResponse> {
data class Params(
val server: String?,
val publicRoomsParams: PublicRoomsParams
)
}

internal class DefaultGetPublicRoomTask(private val roomAPI: RoomAPI) : GetPublicRoomTask {

override suspend fun execute(params: GetPublicRoomTask.Params): Try<PublicRoomsResponse> {
return executeRequest {
apiCall = roomAPI.publicRooms(params.server, params.publicRoomsParams)
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room.directory

import arrow.core.Try
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task

internal interface GetThirdPartyProtocolsTask : Task<Unit, Map<String, ThirdPartyProtocol>>

internal class DefaultGetThirdPartyProtocolsTask(private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask {

override suspend fun execute(params: Unit): Try<Map<String, ThirdPartyProtocol>> {
return executeRequest {
apiCall = roomAPI.thirdPartyProtocols()
}
}
}

View File

@ -16,28 +16,32 @@
*
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.members.RoomMembersService
import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.session.room.invite.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.fetchCopied

internal class DefaultRoomMembersService(private val roomId: String,
private val monarchy: Monarchy,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val inviteTask: InviteTask,
private val taskExecutor: TaskExecutor
) : RoomMembersService {
internal class DefaultMembershipService(private val roomId: String,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val inviteTask: InviteTask,
private val joinTask: JoinRoomTask,
private val leaveRoomTask: LeaveRoomTask
) : MembershipService {

override fun loadRoomMembersIfNeeded(): Cancelable {
val params = LoadRoomMembersTask.Params(roomId, Membership.LEAVE)
@ -68,4 +72,18 @@ internal class DefaultRoomMembersService(private val roomId: String,
.dispatchTo(callback)
.executeBy(taskExecutor)
}

override fun join(callback: MatrixCallback<Unit>) {
val params = JoinRoomTask.Params(roomId)
joinTask.configureWith(params)
.dispatchTo(callback)
.executeBy(taskExecutor)
}

override fun leave(callback: MatrixCallback<Unit>) {
val params = LeaveRoomTask.Params(roomId)
leaveRoomTask.configureWith(params)
.dispatchTo(callback)
.executeBy(taskExecutor)
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import arrow.core.Try
import com.zhuinden.monarchy.Monarchy

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import android.content.Context
import com.zhuinden.monarchy.Monarchy
@ -22,7 +22,7 @@ import im.vector.matrix.android.R
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomNameContent
@ -77,24 +77,17 @@ internal class RoomDisplayNameResolver(private val context: Context,
}

val roomMembers = RoomMembers(realm, roomId)
val otherRoomMembers = roomMembers.getLoaded()
.filterKeys { it != credentials.userId }

if (roomEntity?.membership == MyMembership.INVITED) {
//TODO handle invited
/*
if (currentUser != null
&& !othersActiveMembers.isEmpty()
&& !TextUtils.isEmpty(currentUser!!.mSender)) {
// extract who invited us to the room
name = context.getString(R.string.room_displayname_invite_from, roomState.resolve(currentUser!!.mSender))
val loadedMembers = roomMembers.getLoaded()
val otherRoomMembers = loadedMembers.filterKeys { it != credentials.userId }
if (roomEntity?.membership == Membership.INVITE) {
val inviteMeEvent = roomMembers.queryRoomMemberEvent(credentials.userId).findFirst()
val inviterId = inviteMeEvent?.sender
name = if (inviterId != null && otherRoomMembers.containsKey(inviterId)) {
roomMemberDisplayNameResolver.resolve(inviterId, otherRoomMembers)
} else {
name = context.getString(R.string.room_displayname_room_invite)
context.getString(R.string.room_displayname_room_invite)
}
*/
name = context.getString(R.string.room_displayname_room_invite)
} else {

val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
val memberIds = if (roomSummary?.heroes?.isNotEmpty() == true) {
roomSummary.heroes

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import im.vector.matrix.android.api.session.room.model.RoomMember

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

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

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.members
package im.vector.matrix.android.internal.session.room.membership

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel

View File

@ -5,7 +5,7 @@
* 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
* 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,
@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.invite
package im.vector.matrix.android.internal.session.room.membership.joining

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

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.invite
package im.vector.matrix.android.internal.session.room.membership.joining

import arrow.core.Try
import im.vector.matrix.android.internal.network.executeRequest

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room.membership.joining

import arrow.core.Try
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task

internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
data class Params(
val roomId: String
)
}

internal class DefaultJoinRoomTask(private val roomAPI: RoomAPI) : JoinRoomTask {

override suspend fun execute(params: JoinRoomTask.Params): Try<Unit> {
return executeRequest {
apiCall = roomAPI.join(params.roomId, HashMap())
}
}

}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room.membership.leaving

import arrow.core.Try
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task

internal interface LeaveRoomTask : Task<LeaveRoomTask.Params, Unit> {
data class Params(
val roomId: String
)
}

internal class DefaultLeaveRoomTask(private val roomAPI: RoomAPI) : LeaveRoomTask {

override suspend fun execute(params: LeaveRoomTask.Params): Try<Unit> {
return executeRequest {
apiCall = roomAPI.leave(params.roomId, HashMap())
}
}

}

Some files were not shown because too many files have changed in this diff Show More