Compare commits

..

1 Commits

Author SHA1 Message Date
ganfra 4a754166df Add menu action to download image and video on *ViewerActivity 2019-08-07 19:29:41 +02:00
199 changed files with 1710 additions and 5231 deletions

View File

@ -1,50 +1,8 @@
Changes in RiotX 0.5.0 (2019-XX-XX)
===================================================

Features:
-

Improvements:
- Reduce default release build log level, and lab option to enable more logs.

Other changes:
-

Bugfix:
- Fix crash due to missing informationData (#535)
- Progress in initial sync dialog is decreasing for a step and should not (#532)

Translations:
-

Build:
- Fix issue with version name (#533)
- Fix rendering issue of accepted third party invitation event

Changes in RiotX 0.4.0 (2019-XX-XX)
===================================================

Features:
- Display read receipts in timeline (#81)

Improvements:
- Reactions: Reinstate the ability to react with non-unicode keys (#307)

Bugfix:
- Fix text diff linebreak display (#441)
- Date change message repeats for each redaction until a normal message (#358)
- Slide-in reply icon is distorted (#423)
- Regression / e2e replies not encrypted
- Some video won't play
- Privacy: remove log of notifiable event (#519)
- Fix crash with EmojiCompat (#530)

Changes in RiotX 0.3.0 (2019-08-08)
Changes in RiotX 0.3.0 (2019-XX-XX)
===================================================

Features:
- Create Direct Room flow
- Handle `/markdown` command

Improvements:
- UI for pending edits (#193)
@ -53,10 +11,9 @@ Improvements:
- Enable proper cancellation of suspending functions (including db transaction)
- Enhances network connectivity checks in SDK
- Add "View Edit History" item in the message bottom sheet (#401)
- Cancel sync request on pause and timeout to 0 after pause (#404)

Other changes:
- Show sync progress also in room detail screen (#403)
-

Bugfix:
- Edited message: link confusion when (edited) appears in body (#398)
@ -66,6 +23,9 @@ Bugfix:
- Fix clear cache (#408) and Logout (#205)
- Fix `(edited)` link can be copied to clipboard (#402)

Translations:
-

Build:
- Split APK: generate one APK per arch, to reduce APK size of about 30%


View File

@ -18,7 +18,6 @@ package im.vector.matrix.rx

import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import io.reactivex.Observable
@ -50,10 +49,6 @@ class RxRoom(private val room: Room) {
room.join(viaServers, MatrixCallbackSingle(it)).toSingle(it)
}

fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {
return room.getEventReadReceiptsLive(eventId).asObservable()
}

}

fun Room.rx(): RxRoom {

View File

@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
import io.realm.RealmConfiguration
import kotlin.random.Random
import java.util.*

internal class CryptoStoreHelper {

@ -35,7 +35,7 @@ internal class CryptoStoreHelper {
}

fun createCredential() = Credentials(
userId = "userId_" + Random.nextInt(),
userId = "userId_" + Random().nextInt(),
homeServer = "http://matrix.org",
accessToken = "access_token",
refreshToken = null,

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.comparators

import im.vector.matrix.android.api.interfaces.DatedObject
import java.util.*

object DatedObjectComparators {


View File

@ -19,7 +19,7 @@ package im.vector.matrix.android.api.extensions
import im.vector.matrix.android.api.comparators.DatedObjectComparators
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import java.util.Collections
import java.util.*

/* ==========================================================================================
* MXDeviceInfo

View File

@ -18,17 +18,18 @@ package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.RoomService
import timber.log.Timber
import java.util.regex.Pattern

private val regex = Regex("^(==|<=|>=|<|>)?(\\d*)$")
private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")

class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_count) {
class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {

override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveRoomMemberCountCondition(this)
}

override fun technicalDescription(): String {
return "Room member count is $iz"
return "Room member count is $`is`"
}

fun isSatisfied(event: Event, session: RoomService?): Boolean {
@ -55,9 +56,12 @@ class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_coun
*/
private fun parseIsField(): Pair<String?, Int>? {
try {
val match = regex.find(iz) ?: return null
val (prefix, count) = match.destructured
return prefix to count.toInt()
val match = regex.matcher(`is`)
if (match.find()) {
val prefix = match.group(1)
val count = match.group(2).toInt()
return prefix to count
}
} catch (t: Throwable) {
Timber.d(t)
}

View File

@ -20,10 +20,10 @@ import androidx.lifecycle.LiveData

interface InitialSyncProgressService {

fun getInitialSyncProgressStatus() : LiveData<Status?>
fun getLiveStatus() : LiveData<Status?>

data class Status(
@StringRes val statusText: Int,
@StringRes val statusText: Int?,
val percentProgress: Int = 0
)
}

View File

@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.pushers

import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import java.util.UUID
import java.util.*


interface PushersService {

View File

@ -16,9 +16,8 @@

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

import im.vector.matrix.android.api.session.user.model.User

data class ReadReceipt(
val user: User,
val userId: String,
val eventId: String,
val originServerTs: Long
)

View File

@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.model.PowerLevels
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
import java.util.*

/**
* Parameter to create a room, with facilities functions to configure it
@ -132,7 +133,7 @@ class CreateRoomParams {
)

if (null == initialStates) {
initialStates = mutableListOf(algoEvent)
initialStates = Arrays.asList<Event>(algoEvent)
} else {
initialStates!!.add(algoEvent)
}
@ -165,7 +166,7 @@ class CreateRoomParams {
content = contentMap.toContent())

if (null == initialStates) {
initialStates = mutableListOf(historyVisibilityEvent)
initialStates = Arrays.asList<Event>(historyVisibilityEvent)
} else {
initialStates!!.add(historyVisibilityEvent)
}

View File

@ -16,9 +16,7 @@

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

import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.model.ReadReceipt

/**
* This interface defines methods to handle read receipts and read marker in a room. It's implemented at the room level.
@ -41,6 +39,4 @@ interface ReadService {
fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback<Unit>)

fun isEventRead(eventId: String): Boolean

fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>>
}

View File

@ -20,7 +20,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.toModel
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.isReply
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
@ -38,8 +37,7 @@ data class TimelineEvent(
val senderName: String?,
val isUniqueDisplayName: Boolean,
val senderAvatar: String?,
val annotations: EventAnnotationsSummary? = null,
val readReceipts: List<ReadReceipt> = emptyList()
val annotations: EventAnnotationsSummary? = null
) {

val metadata = HashMap<String, Any>()
@ -67,8 +65,8 @@ data class TimelineEvent(
"$name (${root.senderId})"
}
}
?: root.senderId
?: ""
?: root.senderId
?: ""
}

/**
@ -96,7 +94,7 @@ fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
* Get last MessageContent, after a possible edition
*/
fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel()
?: root.getClearContent().toModel()
?: root.getClearContent().toModel()


fun TimelineEvent.getTextEditableContent(): String? {

View File

@ -25,12 +25,12 @@ interface TimelineService {

/**
* Instantiate a [Timeline] with an optional initial eventId, to be used with permalink.
* You can also configure some settings with the [settings] param.
* You can filter the type you want to grab with the allowedTypes param.
* @param eventId the optional initial eventId.
* @param settings settings to configure the timeline.
* @param allowedTypes the optional filter types
* @return the instantiated timeline
*/
fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline
fun createTimeline(eventId: String?, allowedTypes: List<String>? = null): Timeline


fun getTimeLineEvent(eventId: String): TimelineEvent?

View File

@ -1,44 +0,0 @@
/*
* 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.timeline

/**
* Data class holding setting values for a [Timeline] instance.
*/
data class TimelineSettings(
/**
* The initial number of events to retrieve from cache. You might get less events if you don't have loaded enough yet.
*/
val initialSize: Int,
/**
* A flag to filter edit events
*/
val filterEdits: Boolean = false,
/**
* A flag to filter by types. It should be used with [allowedTypes] field
*/
val filterTypes: Boolean = false,
/**
* If [filterTypes] is true, the list of types allowed by the list.
*/
val allowedTypes: List<String> = emptyList(),
/**
* If true, will build read receipts for each event.
*/
val buildReadReceipts: Boolean = true

)

View File

@ -27,7 +27,7 @@ import java.math.BigInteger
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.SecureRandom
import java.util.Calendar
import java.util.*
import javax.crypto.*
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.IvParameterSpec
@ -479,7 +479,12 @@ object SecretStoringUtils {
val output = Cipher.getInstance(RSA_MODE)
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)

return CipherInputStream(encrypted, output).use { it.readBytes() }
val bos = ByteArrayOutputStream()
CipherInputStream(encrypted, output).use {
it.copyTo(bos)
}

return bos.toByteArray()
}

private fun formatMExtract(bis: InputStream): Pair<ByteArray, ByteArray> {
@ -490,7 +495,14 @@ object SecretStoringUtils {
val iv = ByteArray(ivSize)
bis.read(iv, 0, ivSize)

val encrypted = bis.readBytes()

val bos = ByteArrayOutputStream()
var next = bis.read()
while (next != -1) {
bos.write(next)
next = bis.read()
}
val encrypted = bos.toByteArray()
return Pair(iv, encrypted)
}

@ -518,7 +530,14 @@ object SecretStoringUtils {
val iv = ByteArray(ivSize)
bis.read(iv)

val encrypted = bis.readBytes()
val bos = ByteArrayOutputStream()

var next = bis.read()
while (next != -1) {
bos.write(next)
next = bis.read()
}
val encrypted = bos.toByteArray()
return Triple(encryptedKey, iv, encrypted)
}

@ -560,7 +579,14 @@ object SecretStoringUtils {
val iv = ByteArray(ivSize)
bis.read(iv)

val encrypted = bis.readBytes()
val bos = ByteArrayOutputStream()

var next = bis.read()
while (next != -1) {
bos.write(next)
next = bis.read()
}
val encrypted = bos.toByteArray()
return Triple(salt, iv, encrypted)
}
}

View File

@ -93,7 +93,7 @@ import kotlin.math.max
* Specially, it tracks all room membership changes events in order to do keys updates.
*/
@SessionScope
internal class DefaultCryptoService @Inject constructor(
internal class CryptoManager @Inject constructor(
// Olm Manager
private val olmManager: OlmManager,
// The credentials,
@ -1067,6 +1067,6 @@ internal class DefaultCryptoService @Inject constructor(
* ========================================================================================== */

override fun toString(): String {
return "DefaultCryptoService of " + credentials.userId + " (" + credentials.deviceId + ")"
return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")"
}
}

View File

@ -105,7 +105,7 @@ internal abstract class CryptoModule {
}

@Binds
abstract fun bindCryptoService(cryptoService: DefaultCryptoService): CryptoService
abstract fun bindCryptoService(cryptoManager: CryptoManager): CryptoService

@Binds
abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.crypto

import im.vector.matrix.android.api.auth.data.Credentials
import java.util.*
import javax.inject.Inject

internal class ObjectSigner @Inject constructor(private val credentials: Credentials,

View File

@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.util.convertFromUTF8
import timber.log.Timber
import java.util.*

internal class MXOlmDecryption(
// The olm device interface
@ -157,14 +158,33 @@ internal class MXOlmDecryption(
* @return payload, if decrypted successfully.
*/
private fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
val sessionIds = olmDevice.getSessionIds(theirDeviceIdentityKey) ?: emptySet()
val sessionIdsSet = olmDevice.getSessionIds(theirDeviceIdentityKey)

val messageBody = message["body"] as? String ?: return null
val messageType = when (val typeAsVoid = message["type"]) {
is Double -> typeAsVoid.toInt()
is Int -> typeAsVoid
is Long -> typeAsVoid.toInt()
else -> return null
val sessionIds: List<String>

if (null == sessionIdsSet) {
sessionIds = ArrayList()
} else {
sessionIds = ArrayList(sessionIdsSet)
}

val messageBody = message["body"] as? String
var messageType: Int? = null

val typeAsVoid = message["type"]

if (null != typeAsVoid) {
if (typeAsVoid is Double) {
messageType = typeAsVoid.toInt()
} else if (typeAsVoid is Int) {
messageType = typeAsVoid
} else if (typeAsVoid is Long) {
messageType = typeAsVoid.toInt()
}
}

if (null == messageBody || null == messageType) {
return null
}

// Try each session in turn

View File

@ -26,6 +26,7 @@ import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
@ -58,7 +59,8 @@ object MXEncryptedAttachments {
// Half of the IV is random, the lower order bits are zeroed
// such that the counter never wraps.
// See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75
val initVectorBytes = ByteArray(16) { 0.toByte() }
val initVectorBytes = ByteArray(16)
Arrays.fill(initVectorBytes, 0.toByte())

val ivRandomPart = ByteArray(8)
secureRandom.nextBytes(ivRandomPart)
@ -113,7 +115,7 @@ object MXEncryptedAttachments {
encryptedByteArray = outStream.toByteArray()
)

Timber.v("Encrypt in ${System.currentTimeMillis() - t0} ms")
Timber.v("Encrypt in " + (System.currentTimeMillis() - t0) + " ms")
return Try.just(result)
} catch (oom: OutOfMemoryError) {
Timber.e(oom, "## encryptAttachment failed")
@ -204,13 +206,13 @@ object MXEncryptedAttachments {
val decryptedStream = ByteArrayInputStream(outStream.toByteArray())
outStream.close()

Timber.v("Decrypt in ${System.currentTimeMillis() - t0} ms")
Timber.v("Decrypt in " + (System.currentTimeMillis() - t0) + " ms")

return decryptedStream
} catch (oom: OutOfMemoryError) {
Timber.e(oom, "## decryptAttachment() : failed ${oom.message}")
Timber.e(oom, "## decryptAttachment() : failed " + oom.message)
} catch (e: Exception) {
Timber.e(e, "## decryptAttachment() : failed ${e.message}")
Timber.e(e, "## decryptAttachment() : failed " + e.message)
}

try {
@ -226,20 +228,34 @@ object MXEncryptedAttachments {
* Base64 URL conversion methods
*/

private fun base64UrlToBase64(base64Url: String): String {
return base64Url.replace('-', '+')
.replace('_', '/')
private fun base64UrlToBase64(base64Url: String?): String? {
var result = base64Url
if (null != result) {
result = result.replace("-".toRegex(), "+")
result = result.replace("_".toRegex(), "/")
}

return result
}

private fun base64ToBase64Url(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("\\+".toRegex(), "-")
.replace('/', '_')
.replace("=", "")
private fun base64ToBase64Url(base64: String?): String? {
var result = base64
if (null != result) {
result = result.replace("\n".toRegex(), "")
result = result.replace("\\+".toRegex(), "-")
result = result.replace("/".toRegex(), "_")
result = result.replace("=".toRegex(), "")
}
return result
}

private fun base64ToUnpaddedBase64(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("=", "")
private fun base64ToUnpaddedBase64(base64: String?): String? {
var result = base64
if (null != result) {
result = result.replace("\n".toRegex(), "")
result = result.replace("=".toRegex(), "")
}

return result
}
}

View File

@ -66,8 +66,9 @@ import org.matrix.olm.OlmPkEncryption
import org.matrix.olm.OlmPkMessage
import timber.log.Timber
import java.security.InvalidParameterException
import java.util.*
import javax.inject.Inject
import kotlin.random.Random
import kotlin.collections.HashMap

/**
* A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
@ -113,6 +114,8 @@ internal class KeysBackup @Inject constructor(
// The backup key being used.
private var backupOlmPkEncryption: OlmPkEncryption? = null

private val random = Random()

private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null

private var keysBackupStateListener: KeysBackupStateListener? = null
@ -845,7 +848,7 @@ internal class KeysBackup @Inject constructor(
// Wait between 0 and 10 seconds, to avoid backup requests from
// different clients hitting the server all at the same time when a
// new key is sent
val delayInMs = Random.nextLong(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS)
val delayInMs = random.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong()

uiHandler.postDelayed({ backupKeys() }, delayInMs)
}
@ -1304,7 +1307,7 @@ internal class KeysBackup @Inject constructor(

// Make the request
storeSessionDataTask
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)) {
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){
this.callback = sendingRequestCallback
}
.executeBy(taskExecutor)
@ -1402,7 +1405,7 @@ internal class KeysBackup @Inject constructor(

companion object {
// Maximum delay in ms in {@link maybeBackupKeys}
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10_000L
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10000

// Maximum number of keys to send at a time to the homeserver.
private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100

View File

@ -22,7 +22,7 @@ package im.vector.matrix.android.internal.crypto.keysbackup
import androidx.annotation.WorkerThread
import im.vector.matrix.android.api.listeners.ProgressListener
import timber.log.Timber
import java.util.UUID
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.experimental.xor

View File

@ -20,6 +20,7 @@ import android.os.Handler
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import timber.log.Timber
import java.util.*

internal class KeysBackupStateManager(private val uiHandler: Handler) {


View File

@ -23,6 +23,7 @@ import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
import java.io.Serializable
import java.util.*

@JsonClass(generateAdapter = true)
data class MXDeviceInfo(

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.model

import im.vector.matrix.android.api.util.JsonDict
import timber.log.Timber
import java.util.*

data class MXKey(
/**
@ -45,7 +46,11 @@ data class MXKey(
* @return the signed data map
*/
fun signalableJSONDictionary(): Map<String, Any> {
return mapOf("key" to value)
val map = HashMap<String, Any>()

map["key"] = value

return map
}

/**

View File

@ -16,6 +16,7 @@

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

import java.util.*

class MXUsersDevicesMap<E> {

@ -26,7 +27,7 @@ class MXUsersDevicesMap<E> {
* @return the user Ids
*/
val userIds: List<String>
get() = map.keys.toList()
get() = ArrayList(map.keys)

val isEmpty: Boolean
get() = map.isEmpty()
@ -39,7 +40,7 @@ class MXUsersDevicesMap<E> {
* @return the device ids list
*/
fun getUserDeviceIds(userId: String?): List<String>? {
return if (!userId.isNullOrBlank() && map.containsKey(userId)) {
return if (userId?.isNotBlank() == true && map.containsKey(userId)) {
map[userId]!!.keys.toList()
} else null
}
@ -52,7 +53,7 @@ class MXUsersDevicesMap<E> {
* @return the object
*/
fun getObject(userId: String?, deviceId: String?): E? {
return if (!userId.isNullOrBlank() && !deviceId.isNullOrBlank()) {
return if (userId?.isNotBlank() == true && deviceId?.isNotBlank() == true && map.containsKey(userId)) {
map[userId]?.get(deviceId)
} else null
}
@ -66,8 +67,11 @@ class MXUsersDevicesMap<E> {
*/
fun setObject(userId: String?, deviceId: String?, o: E?) {
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
val devices = map.getOrPut(userId) { HashMap() }
devices[deviceId] = o
if (map[userId] == null) {
map[userId] = HashMap()
}

map[userId]?.put(deviceId, o)
}
}

@ -78,7 +82,7 @@ class MXUsersDevicesMap<E> {
* @param userId the user id
*/
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
if (!userId.isNullOrBlank()) {
if (userId?.isNotBlank() == true) {
if (null == objectsPerDevices) {
map.remove(userId)
} else {
@ -93,7 +97,7 @@ class MXUsersDevicesMap<E> {
* @param userId the user id.
*/
fun removeUserObjects(userId: String?) {
if (!userId.isNullOrBlank()) {
if (userId?.isNotBlank() == true) {
map.remove(userId)
}
}

View File

@ -21,8 +21,8 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import java.util.*
import javax.inject.Inject
import kotlin.random.Random

internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
data class Params(
@ -45,7 +45,7 @@ internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi
return executeRequest {
apiCall = cryptoApi.sendToDevice(
params.eventType,
params.transactionId ?: Random.nextInt(Integer.MAX_VALUE).toString(),
params.transactionId ?: Random().nextInt(Integer.MAX_VALUE).toString(),
sendToDeviceBody
)
}

View File

@ -44,7 +44,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID
import java.util.*
import javax.inject.Inject
import kotlin.collections.HashMap

@ -161,7 +161,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
cancelTransaction(
startReq.transactionID!!,
otherUserId!!,
startReq.fromDevice ?: event.getSenderKey()!!,
startReq?.fromDevice ?: event.getSenderKey()!!,
CancelCode.UnknownMethod
)
}
@ -388,13 +388,14 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
* This string must be unique for the pair of users performing verification for the duration that the transaction is valid
*/
private fun createUniqueIDForTransaction(userId: String, deviceID: String): String {
return buildString {
append(credentials.userId).append("|")
append(credentials.deviceId).append("|")
append(userId).append("|")
append(deviceID).append("|")
append(UUID.randomUUID().toString())
}
val buff = StringBuffer()
buff
.append(credentials.userId).append("|")
.append(credentials.deviceId).append("|")
.append(userId).append("|")
.append(deviceID).append("|")
.append(UUID.randomUUID().toString())
return buff.toString()
}



View File

@ -23,12 +23,9 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
@ -136,28 +133,6 @@ internal fun ChunkEntity.add(roomId: String,
}

val localId = TimelineEventEntity.nextId(realm)
val eventId = event.eventId ?: ""
val senderId = event.senderId ?: ""

val readReceiptsSummaryEntity = ReadReceiptsSummaryEntity.where(realm, eventId).findFirst()
?: ReadReceiptsSummaryEntity(eventId, roomId)

// Update RR for the sender of a new message with a dummy one

if (event.originServerTs != null) {
val timestampOfEvent = event.originServerTs.toDouble()
val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId)
// If the synced RR is older, update
if (timestampOfEvent > readReceiptOfSender.originServerTs) {
val previousReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId = readReceiptOfSender.eventId).findFirst()
readReceiptOfSender.eventId = eventId
readReceiptOfSender.originServerTs = timestampOfEvent
previousReceiptsSummary?.readReceipts?.remove(readReceiptOfSender)
readReceiptsSummaryEntity.readReceipts.add(readReceiptOfSender)
}
}


val eventEntity = TimelineEventEntity(localId).also {
it.root = event.toEntity(roomId).apply {
this.stateIndex = currentStateIndex
@ -165,10 +140,9 @@ internal fun ChunkEntity.add(roomId: String,
this.displayIndex = currentDisplayIndex
this.sendState = SendState.SYNCED
}
it.eventId = eventId
it.eventId = event.eventId ?: ""
it.roomId = roomId
it.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
it.readReceipts = readReceiptsSummaryEntity
it.annotations = EventAnnotationsSummaryEntity.where(realm, it.eventId).findFirst()
}
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.timelineEvents.size
timelineEvents.add(position, eventEntity)
@ -176,14 +150,14 @@ internal fun ChunkEntity.add(roomId: String,

internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) {
PaginationDirection.FORWARDS -> forwardsDisplayIndex
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
} ?: defaultValue
PaginationDirection.FORWARDS -> forwardsDisplayIndex
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
} ?: defaultValue
}

internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) {
PaginationDirection.FORWARDS -> forwardsStateIndex
PaginationDirection.BACKWARDS -> backwardsStateIndex
} ?: defaultValue
PaginationDirection.FORWARDS -> forwardsStateIndex
PaginationDirection.BACKWARDS -> backwardsStateIndex
} ?: defaultValue
}

View File

@ -1,45 +0,0 @@
/*
* 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.room.model.ReadReceipt
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.model.UserEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import io.realm.Realm
import io.realm.RealmConfiguration
import javax.inject.Inject

internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {

fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> {
if (readReceiptsSummaryEntity == null) {
return emptyList()
}
return Realm.getInstance(realmConfiguration).use { realm ->
val readReceipts = readReceiptsSummaryEntity.readReceipts
readReceipts
.mapNotNull {
val user = UserEntity.where(realm, it.userId).findFirst()
?: return@mapNotNull null
ReadReceipt(user.asDomain(), it.originServerTs.toLong())
}
}
}

}

View File

@ -22,12 +22,11 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import java.util.UUID
import java.util.*
import javax.inject.Inject

internal class RoomSummaryMapper @Inject constructor(
val cryptoService: CryptoService,
val timelineEventMapper: TimelineEventMapper
val cryptoService: CryptoService
) {

fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
@ -35,9 +34,7 @@ internal class RoomSummaryMapper @Inject constructor(
RoomTag(it.tagName, it.tagOrder)
}

val latestEvent = roomSummaryEntity.latestEvent?.let {
timelineEventMapper.map(it)
}
val latestEvent = roomSummaryEntity.latestEvent?.asDomain()
if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) {
//TODO use a global event decryptor? attache to session and that listen to new sessionId?
//for now decrypt sync

View File

@ -17,38 +17,29 @@
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.room.model.ReadReceipt

import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import javax.inject.Inject

internal class TimelineEventMapper @Inject constructor(private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper) {
internal object TimelineEventMapper {

fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent {

fun map(timelineEventEntity: TimelineEventEntity, buildReadReceipts: Boolean = true, correctedReadReceipts: List<ReadReceipt>? = null): TimelineEvent {
val readReceipts = if (buildReadReceipts) {
correctedReadReceipts ?: timelineEventEntity.readReceipts
?.let {
readReceiptsSummaryMapper.map(it)
}
} else {
null
}
return TimelineEvent(
root = timelineEventEntity.root?.asDomain()
?: Event("", timelineEventEntity.eventId),
?: Event("", timelineEventEntity.eventId),
annotations = timelineEventEntity.annotations?.asDomain(),
localId = timelineEventEntity.localId,
displayIndex = timelineEventEntity.root?.displayIndex ?: 0,
senderName = timelineEventEntity.senderName,
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
senderAvatar = timelineEventEntity.senderAvatar,
readReceipts = readReceipts?.sortedByDescending {
it.originServerTs
} ?: emptyList()
senderAvatar = timelineEventEntity.senderAvatar
)
}

}

internal fun TimelineEventEntity.asDomain(): TimelineEvent {
return TimelineEventMapper.map(this)
}



View File

@ -17,18 +17,13 @@
package im.vector.matrix.android.internal.database.model

import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey

internal open class ReadReceiptEntity(@PrimaryKey var primaryKey: String = "",
var eventId: String = "",
var roomId: String = "",
var userId: String = "",
var originServerTs: Double = 0.0
var userId: String = "",
var eventId: String = "",
var roomId: String = "",
var originServerTs: Double = 0.0
) : RealmObject() {
companion object

@LinkingObjects("readReceipts")
val summary: RealmResults<ReadReceiptsSummaryEntity>? = null
}

View File

@ -1,37 +0,0 @@
/*
* 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.RealmResults
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey

internal open class ReadReceiptsSummaryEntity(
@PrimaryKey
var eventId: String = "",
var roomId: String = "",
var readReceipts: RealmList<ReadReceiptEntity> = RealmList()
) : RealmObject() {

@LinkingObjects("readReceipts")
val timelineEvent: RealmResults<TimelineEventEntity>? = null

companion object

}

View File

@ -22,27 +22,26 @@ import io.realm.annotations.RealmModule
* Realm module for Session
*/
@RealmModule(library = true,
classes = [
ChunkEntity::class,
EventEntity::class,
TimelineEventEntity::class,
FilterEntity::class,
GroupEntity::class,
GroupSummaryEntity::class,
ReadReceiptEntity::class,
RoomEntity::class,
RoomSummaryEntity::class,
RoomTagEntity::class,
SyncEntity::class,
UserEntity::class,
EventAnnotationsSummaryEntity::class,
ReactionAggregatedSummaryEntity::class,
EditAggregatedSummaryEntity::class,
PushRulesEntity::class,
PushRuleEntity::class,
PushConditionEntity::class,
PusherEntity::class,
PusherDataEntity::class,
ReadReceiptsSummaryEntity::class
])
classes = [
ChunkEntity::class,
EventEntity::class,
TimelineEventEntity::class,
FilterEntity::class,
GroupEntity::class,
GroupSummaryEntity::class,
ReadReceiptEntity::class,
RoomEntity::class,
RoomSummaryEntity::class,
RoomTagEntity::class,
SyncEntity::class,
UserEntity::class,
EventAnnotationsSummaryEntity::class,
ReactionAggregatedSummaryEntity::class,
EditAggregatedSummaryEntity::class,
PushRulesEntity::class,
PushRuleEntity::class,
PushConditionEntity::class,
PusherEntity::class,
PusherDataEntity::class
])
internal class SessionRealmModule

View File

@ -30,8 +30,7 @@ internal open class TimelineEventEntity(var localId: Long = 0,
var senderName: String? = null,
var isUniqueDisplayName: Boolean = false,
var senderAvatar: String? = null,
var senderMembershipEvent: EventEntity? = null,
var readReceipts: ReadReceiptsSummaryEntity? = null
var senderMembershipEvent: EventEntity? = null
) : RealmObject() {

@LinkingObjects("timelineEvents")

View File

@ -1,23 +0,0 @@
/*
* 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.query

internal object FilterContent {

internal const val EDIT_TYPE = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""

}

View File

@ -26,22 +26,4 @@ internal fun ReadReceiptEntity.Companion.where(realm: Realm, roomId: String, use
return realm.where<ReadReceiptEntity>()
.equalTo(ReadReceiptEntityFields.ROOM_ID, roomId)
.equalTo(ReadReceiptEntityFields.USER_ID, userId)
}

internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId: String, userId: String, originServerTs: Double): ReadReceiptEntity {
return ReadReceiptEntity().apply {
this.primaryKey = "${roomId}_$userId"
this.eventId = eventId
this.roomId = roomId
this.userId = userId
this.originServerTs = originServerTs
}
}

internal fun ReadReceiptEntity.Companion.getOrCreate(realm: Realm, roomId: String, userId: String): ReadReceiptEntity {
return ReadReceiptEntity.where(realm, roomId, userId).findFirst()
?: realm.createObject(ReadReceiptEntity::class.java, "${roomId}_$userId").apply {
this.roomId = roomId
this.userId = userId
}
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.query

import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntityFields
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where

internal fun ReadReceiptsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<ReadReceiptsSummaryEntity> {
return realm.where<ReadReceiptsSummaryEntity>()
.equalTo(ReadReceiptsSummaryEntityFields.EVENT_ID, eventId)
}

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

View File

@ -1,24 +0,0 @@
/*
* 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.di

import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Module

@AssistedModule
@Module(includes = [AssistedInject_SessionAssistedInjectModule::class])
interface SessionAssistedInjectModule

View File

@ -24,6 +24,7 @@ import java.security.KeyStore
import java.security.MessageDigest
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.*
import javax.net.ssl.*
import kotlin.experimental.and


View File

@ -25,6 +25,7 @@ import java.net.UnknownHostException
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.util.*
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
@ -100,16 +101,25 @@ constructor(trustPinned: Array<TrustManager>, acceptedTlsVersions: List<TlsVersi
}

private fun enableTLSOnSocket(socket: Socket?): Socket? {
if (socket is SSLSocket) {
val supportedProtocols = socket.supportedProtocols.toSet()
val filteredEnabledProtocols = enabledProtocols.filter { it in supportedProtocols }
if (socket != null && socket is SSLSocket) {
val sslSocket = socket as SSLSocket?

if (filteredEnabledProtocols.isNotEmpty()) {
val supportedProtocols = Arrays.asList(*sslSocket!!.supportedProtocols)
val filteredEnabledProtocols = ArrayList<String>()

for (protocol in enabledProtocols) {
if (supportedProtocols.contains(protocol)) {
filteredEnabledProtocols.add(protocol)
}
}

if (!filteredEnabledProtocols.isEmpty()) {
try {
socket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
sslSocket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
} catch (e: Exception) {
Timber.e(e)
}

}
}
return socket

View File

@ -15,7 +15,6 @@
*/
package im.vector.matrix.android.internal.session

import androidx.annotation.StringRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import im.vector.matrix.android.api.session.InitialSyncProgressService
@ -26,33 +25,31 @@ import javax.inject.Inject
@SessionScope
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {

private var status = MutableLiveData<InitialSyncProgressService.Status>()
var status = MutableLiveData<InitialSyncProgressService.Status>()

private var rootTask: TaskInfo? = null
var rootTask: TaskInfo? = null

override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status?> {
override fun getLiveStatus(): LiveData<InitialSyncProgressService.Status?> {
return status
}

fun startTask(@StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
// Create a rootTask, or add a child to the leaf

fun startTask(nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
if (rootTask == null) {
rootTask = TaskInfo(nameRes, totalProgress)
} else {
val currentLeaf = rootTask!!.leaf()

val newTask = TaskInfo(nameRes,
totalProgress,
currentLeaf,
parentWeight)

val newTask = TaskInfo(nameRes, totalProgress)
newTask.parent = currentLeaf
newTask.offset = currentLeaf.currentProgress
currentLeaf.child = newTask
newTask.parentWeight = parentWeight
}
reportProgress(0)
}

fun reportProgress(progress: Int) {
rootTask?.leaf()?.setProgress(progress)
rootTask?.leaf()?.incrementProgress(progress)
}

fun endTask(nameRes: Int) {
@ -61,7 +58,7 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
//close it
val parent = endedTask.parent
parent?.child = null
parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
parent?.incrementProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
}
if (endedTask?.parent == null) {
status.postValue(null)
@ -74,17 +71,14 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
}


private inner class TaskInfo(@StringRes var nameRes: Int,
var totalProgress: Int,
var parent: TaskInfo? = null,
var parentWeight: Float = 1f,
var offset: Int = parent?.currentProgress ?: 0) {
inner class TaskInfo(var nameRes: Int,
var totalProgress: Int) {
var parent: TaskInfo? = null
var child: TaskInfo? = null
var parentWeight: Float = 1f
var currentProgress: Int = 0
var offset: Int = 0

/**
* Get the further child
*/
fun leaf(): TaskInfo {
var last = this
while (last.child != null) {
@ -93,27 +87,26 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
return last
}

/**
* Set progress of the parent if any (which will post value), or post the value
*/
fun setProgress(progress: Int) {
fun incrementProgress(progress: Int) {
currentProgress = progress
// val newProgress = Math.min(currentProgress + progress, totalProgress)
parent?.let {
val parentProgress = (currentProgress * parentWeight).toInt()
it.setProgress(offset + parentProgress)
} ?: run {
Timber.e("--- ${leaf().nameRes}: $currentProgress")
it.incrementProgress(offset + parentProgress)
}
if (parent == null) {
Timber.e("--- ${leaf().nameRes}: ${currentProgress}")
status.postValue(
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
)
}
}
}

}

inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?,
@StringRes nameRes: Int,
nameRes: Int,
totalProgress: Int,
parentWeight: Float = 1f,
block: () -> T): T {
@ -128,11 +121,11 @@ inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: DefaultInitialSyncP
taskId: Int,
weight: Float,
transform: (Map.Entry<K, V>) -> R): List<R> {
val total = count().toFloat()
val total = count()
var current = 0
reporter?.startTask(taskId, 100, weight)
return map {
reporter?.reportProgress((current / total * 100).toInt())
return this.map {
reporter?.reportProgress((current / total.toFloat() * 100).toInt())
current++
transform.invoke(it)
}.also {

View File

@ -40,7 +40,7 @@ import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.session.sync.job.SyncThread
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
@ -63,7 +63,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private val signOutService: Lazy<SignOutService>,
private val pushRuleService: Lazy<PushRuleService>,
private val pushersService: Lazy<PushersService>,
private val cryptoService: Lazy<DefaultCryptoService>,
private val cryptoService: Lazy<CryptoManager>,
private val fileService: Lazy<FileService>,
private val syncThreadProvider: Provider<SyncThread>,
private val contentUrlResolver: ContentUrlResolver,

View File

@ -22,7 +22,6 @@ import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.crypto.CryptoModule
import im.vector.matrix.android.internal.di.MatrixComponent
import im.vector.matrix.android.internal.di.SessionAssistedInjectModule
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.session.cache.CacheModule
import im.vector.matrix.android.internal.session.content.ContentModule
@ -60,8 +59,7 @@ import im.vector.matrix.android.internal.task.TaskExecutor
CacheModule::class,
CryptoModule::class,
PushersModule::class,
AccountDataModule::class,
SessionAssistedInjectModule::class
AccountDataModule::class
]
)
@SessionScope

View File

@ -36,9 +36,7 @@ import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
import im.vector.matrix.android.internal.session.room.DefaultRoomFactory
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
import im.vector.matrix.android.internal.session.room.RoomFactory
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
@ -116,6 +114,7 @@ internal abstract class SessionModule {
}
}


@Binds
abstract fun bindSession(session: DefaultSession): Session


View File

@ -30,8 +30,9 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
private val listeners = mutableMapOf<String, MutableList<ContentUploadStateTracker.UpdateListener>>()

override fun track(key: String, updateListener: ContentUploadStateTracker.UpdateListener) {
val listeners = listeners.getOrPut(key) { ArrayList() }
val listeners = listeners[key] ?: ArrayList()
listeners.add(updateListener)
this.listeners[key] = listeners
val currentState = states[key] ?: ContentUploadStateTracker.State.Idle
mainHandler.post { updateListener.onUpdate(currentState) }
}

View File

@ -32,7 +32,7 @@ import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.worker.WorkManagerUtil
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import java.util.UUID
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject


View File

@ -16,46 +16,70 @@

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

import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
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.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.FetchEditHistoryTask
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
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
import im.vector.matrix.android.internal.session.room.state.SendStateTask
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService
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.task.TaskExecutor
import javax.inject.Inject

internal class RoomFactory @Inject constructor(private val context: Context,
private val credentials: Credentials,
private val monarchy: Monarchy,
private val eventFactory: LocalEchoEventFactory,
private val roomSummaryMapper: RoomSummaryMapper,
private val taskExecutor: TaskExecutor,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val inviteTask: InviteTask,
private val sendStateTask: SendStateTask,
private val paginationTask: PaginationTask,
private val contextOfEventTask: GetContextOfEventTask,
private val setReadMarkersTask: SetReadMarkersTask,
private val cryptoService: CryptoService,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val fetchEditHistoryTask: FetchEditHistoryTask,
private val joinRoomTask: JoinRoomTask,
private val leaveRoomTask: LeaveRoomTask) {

internal interface RoomFactory {
fun create(roomId: String): Room
}
fun create(roomId: String): Room {
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, cryptoService, paginationTask)
val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy)
val stateService = DefaultStateService(roomId, monarchy.realmConfiguration, taskExecutor, sendStateTask)
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
val relationService = DefaultRelationService(context,
credentials, roomId, eventFactory, cryptoService, findReactionEventForUndoTask, fetchEditHistoryTask, monarchy, taskExecutor)

internal class DefaultRoomFactory @Inject constructor(private val monarchy: Monarchy,
private val roomSummaryMapper: RoomSummaryMapper,
private val cryptoService: CryptoService,
private val timelineServiceFactory: DefaultTimelineService.Factory,
private val sendServiceFactory: DefaultSendService.Factory,
private val stateServiceFactory: DefaultStateService.Factory,
private val readServiceFactory: DefaultReadService.Factory,
private val relationServiceFactory: DefaultRelationService.Factory,
private val membershipServiceFactory: DefaultMembershipService.Factory) :
RoomFactory {

override fun create(roomId: String): Room {
return DefaultRoom(
roomId,
monarchy,
roomSummaryMapper,
timelineServiceFactory.create(roomId),
sendServiceFactory.create(roomId),
stateServiceFactory.create(roomId),
readServiceFactory.create(roomId),
timelineService,
sendService,
stateService,
readService,
cryptoService,
relationServiceFactory.create(roomId),
membershipServiceFactory.create(roomId)
relationService,
roomMembersService
)
}


View File

@ -22,6 +22,12 @@ import dagger.Provides
import im.vector.matrix.android.api.session.file.FileService
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.members.MembershipService
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
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.internal.session.DefaultFileService
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.room.create.CreateRoomTask
@ -31,6 +37,7 @@ import im.vector.matrix.android.internal.session.room.directory.DefaultGetThirdP
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.DefaultLoadRoomMembersTask
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.joining.DefaultInviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.DefaultJoinRoomTask
@ -40,20 +47,15 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.Default
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.DefaultReadService
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.DefaultFetchEditHistoryTask
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.FetchEditHistoryTask
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.relation.*
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
import im.vector.matrix.android.internal.session.room.state.DefaultStateService
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.*
import retrofit2.Retrofit

@Module
@ -69,9 +71,6 @@ internal abstract class RoomModule {
}
}

@Binds
abstract fun bindRoomFactory(roomFactory: DefaultRoomFactory): RoomFactory

@Binds
abstract fun bindRoomService(roomService: DefaultRoomService): RoomService

@ -99,15 +98,24 @@ internal abstract class RoomModule {
@Binds
abstract fun bindLeaveRoomTask(leaveRoomTask: DefaultLeaveRoomTask): LeaveRoomTask

@Binds
abstract fun bindMembershipService(membershipService: DefaultMembershipService): MembershipService

@Binds
abstract fun bindLoadRoomMembersTask(loadRoomMembersTask: DefaultLoadRoomMembersTask): LoadRoomMembersTask

@Binds
abstract fun bindPruneEventTask(pruneEventTask: DefaultPruneEventTask): PruneEventTask

@Binds
abstract fun bindReadService(readService: DefaultReadService): ReadService

@Binds
abstract fun bindSetReadMarkersTask(setReadMarkersTask: DefaultSetReadMarkersTask): SetReadMarkersTask

@Binds
abstract fun bindRelationService(relationService: DefaultRelationService): RelationService

@Binds
abstract fun bindFindReactionEventForUndoTask(findReactionEventForUndoTask: DefaultFindReactionEventForUndoTask): FindReactionEventForUndoTask

@ -117,12 +125,21 @@ internal abstract class RoomModule {
@Binds
abstract fun bindSendStateTask(sendStateTask: DefaultSendStateTask): SendStateTask

@Binds
abstract fun bindSendService(sendService: DefaultSendService): SendService

@Binds
abstract fun bindStateService(stateService: DefaultStateService): StateService

@Binds
abstract fun bindGetContextOfEventTask(getContextOfEventTask: DefaultGetContextOfEventTask): GetContextOfEventTask

@Binds
abstract fun bindPaginationTask(paginationTask: DefaultPaginationTask): PaginationTask

@Binds
abstract fun bindTimelineService(timelineService: DefaultTimelineService): TimelineService

@Binds
abstract fun bindFileService(fileService: DefaultFileService): FileService


View File

@ -17,8 +17,6 @@
package im.vector.matrix.android.internal.session.room.membership

import androidx.lifecycle.LiveData
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.toModel
@ -33,21 +31,17 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRo
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.fetchCopied
import javax.inject.Inject

internal class DefaultMembershipService @AssistedInject constructor(@Assisted 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
internal class DefaultMembershipService @Inject constructor(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 {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): MembershipService
}

override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Unit>): Cancelable {
val params = LoadRoomMembersTask.Params(roomId, Membership.LEAVE)
return loadRoomMembersTask

View File

@ -16,38 +16,24 @@

package im.vector.matrix.android.internal.session.room.read

import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.internal.database.RealmLiveData
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
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 javax.inject.Inject

internal class DefaultReadService @AssistedInject constructor(@Assisted private val roomId: String,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val setReadMarkersTask: SetReadMarkersTask,
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
private val credentials: Credentials
) : ReadService {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): ReadService
}
internal class DefaultReadService @Inject constructor(private val roomId: String,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val setReadMarkersTask: SetReadMarkersTask,
private val credentials: Credentials) : ReadService {

override fun markAllAsRead(callback: MatrixCallback<Unit>) {
val params = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
@ -81,28 +67,16 @@ internal class DefaultReadService @AssistedInject constructor(@Assisted private
var isEventRead = false
monarchy.doWithRealm {
val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst()
?: return@doWithRealm
?: return@doWithRealm
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId)
?: return@doWithRealm
?: return@doWithRealm
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex
?: Int.MIN_VALUE
?: Int.MIN_VALUE
val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex
?: Int.MAX_VALUE
?: Int.MAX_VALUE
isEventRead = eventToCheckIndex <= readReceiptIndex
}
return isEventRead
}

override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
val liveEntity = RealmLiveData(monarchy.realmConfiguration) { realm ->
ReadReceiptsSummaryEntity.where(realm, eventId)
}
return Transformations.map(liveEntity) { realmResults ->
realmResults.firstOrNull()?.let {
readReceiptsSummaryMapper.map(it)
}?.sortedByDescending {
it.originServerTs
} ?: emptyList()
}
}
}

View File

@ -19,8 +19,6 @@ import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import androidx.work.OneTimeWorkRequest
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials
@ -47,33 +45,33 @@ import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.CancelableWork
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import timber.log.Timber
import javax.inject.Inject

internal class DefaultRelationService @AssistedInject constructor(@Assisted private val roomId: String,
private val context: Context,
private val credentials: Credentials,
private val eventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val fetchEditHistoryTask: FetchEditHistoryTask,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor)
internal class DefaultRelationService @Inject constructor(private val context: Context,
private val credentials: Credentials,
private val roomId: String,
private val eventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val fetchEditHistoryTask: FetchEditHistoryTask,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor)
: RelationService {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): RelationService
}

override fun sendReaction(reaction: String, targetEventId: String): Cancelable {
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
.also {
saveLocalEcho(it)
}
val sendRelationWork = createSendEventWork(event, true)
val sendRelationWork = createSendRelationWork(event)
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
return CancelableWork(context, sendRelationWork.id)
}

private fun createSendRelationWork(event: Event): OneTimeWorkRequest {
return createSendEventWork(event)
}

override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {

val params = FindReactionEventForUndoTask.Params(
@ -130,42 +128,42 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
.also {
saveLocalEcho(it)
}
return if (cryptoService.isRoomEncrypted(roomId)) {
if (cryptoService.isRoomEncrypted(roomId)) {
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
val workRequest = createSendEventWork(event, false)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
CancelableWork(context, encryptWork.id)
return CancelableWork(context, encryptWork.id)

} else {
val workRequest = createSendEventWork(event, true)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
CancelableWork(context, workRequest.id)
return CancelableWork(context, workRequest.id)
}

}

override fun editReply(replyToEdit: TimelineEvent,
originalTimelineEvent: TimelineEvent,
originalEvent: TimelineEvent,
newBodyText: String,
compatibilityBodyText: String): Cancelable {
val event = eventFactory
.createReplaceTextOfReply(roomId,
replyToEdit,
originalTimelineEvent,
originalEvent,
newBodyText, true, MessageType.MSGTYPE_TEXT, compatibilityBodyText)
.also {
saveLocalEcho(it)
}
return if (cryptoService.isRoomEncrypted(roomId)) {
if (cryptoService.isRoomEncrypted(roomId)) {
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
val workRequest = createSendEventWork(event, false)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
CancelableWork(context, encryptWork.id)
return CancelableWork(context, encryptWork.id)

} else {
val workRequest = createSendEventWork(event, true)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
CancelableWork(context, workRequest.id)
return CancelableWork(context, workRequest.id)
}
}

@ -183,16 +181,16 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
saveLocalEcho(it)
} ?: return null

return if (cryptoService.isRoomEncrypted(roomId)) {
if (cryptoService.isRoomEncrypted(roomId)) {
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
val workRequest = createSendEventWork(event, false)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
CancelableWork(context, encryptWork.id)
return CancelableWork(context, encryptWork.id)

} else {
val workRequest = createSendEventWork(event, true)
val workRequest = createSendEventWork(event)
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
CancelableWork(context, workRequest.id)
return CancelableWork(context, workRequest.id)
}

}
@ -204,10 +202,10 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
}

private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
private fun createSendEventWork(event: Event): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, true)
}

override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {

View File

@ -17,22 +17,12 @@
package im.vector.matrix.android.internal.session.room.send

import android.content.Context
import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.Operation
import androidx.work.WorkManager
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import androidx.work.*
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.crypto.CryptoService
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.isImageMessage
import im.vector.matrix.android.api.session.events.model.isTextMessage
import im.vector.matrix.android.api.session.events.model.toModel
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.message.MessageType
import im.vector.matrix.android.api.session.room.send.SendService
@ -57,22 +47,18 @@ import im.vector.matrix.android.internal.worker.startChain
import timber.log.Timber
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import javax.inject.Inject

private const val UPLOAD_WORK = "UPLOAD_WORK"
private const val BACKOFF_DELAY = 10_000L

internal class DefaultSendService @AssistedInject constructor(@Assisted private val roomId: String,
private val context: Context,
private val credentials: Credentials,
private val localEchoEventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val monarchy: Monarchy
) : SendService {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): SendService
}
internal class DefaultSendService @Inject constructor(private val context: Context,
private val credentials: Credentials,
private val roomId: String,
private val localEchoEventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val monarchy: Monarchy)
: SendService {

private val workerFutureListenerExecutor = Executors.newSingleThreadExecutor()
override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable {
@ -166,11 +152,11 @@ internal class DefaultSendService @AssistedInject constructor(@Assisted private
override fun deleteFailedEcho(localEcho: TimelineEvent) {
monarchy.writeAsync { realm ->
TimelineEventEntity.where(realm, eventId = localEcho.root.eventId
?: "").findFirst()?.let {
?: "").findFirst()?.let {
it.deleteFromRealm()
}
EventEntity.where(realm, eventId = localEcho.root.eventId
?: "").findFirst()?.let {
?: "").findFirst()?.let {
it.deleteFromRealm()
}
}

View File

@ -38,7 +38,7 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.util.StringProvider
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
import java.util.UUID
import java.util.*
import javax.inject.Inject

/**
@ -304,22 +304,17 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
}

private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String {
return buildString {
append("> <")
append(originalSenderId)
append(">")

val lines = body.text.split("\n")
lines.forEachIndexed { index, s ->
if (index == 0) {
append(" $s")
} else {
append("\n> $s")
}
val lines = body.text.split("\n")
val replyFallback = StringBuffer("> <$originalSenderId>")
lines.forEachIndexed { index, s ->
if (index == 0) {
replyFallback.append(" $s")
} else {
replyFallback.append("\n> $s")
}
append("\n\n")
append(newBodyText)
}
replyFallback.append("\n\n").append(newBodyText)
return replyFallback.toString()
}

/**

View File

@ -16,8 +16,6 @@

package im.vector.matrix.android.internal.session.room.state

import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
@ -31,18 +29,13 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import io.realm.Realm
import io.realm.RealmConfiguration
import javax.inject.Inject

internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val taskExecutor: TaskExecutor,
private val sendStateTask: SendStateTask
) : StateService {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): StateService
}
internal class DefaultStateService @Inject constructor(private val roomId: String,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val taskExecutor: TaskExecutor,
private val sendStateTask: SendStateTask) : StateService {

override fun getStateEvent(eventType: String): Event? {
return Realm.getInstance(realmConfiguration).use { realm ->
@ -52,10 +45,10 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private

override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
val params = SendStateTask.Params(roomId,
EventType.STATE_ROOM_TOPIC,
mapOf(
"topic" to topic
))
EventType.STATE_ROOM_TOPIC,
mapOf(
"topic" to topic
))


sendStateTask

View File

@ -19,41 +19,21 @@ package im.vector.matrix.android.internal.session.room.timeline
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.send.SendState
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.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.api.util.CancelableBag
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
import im.vector.matrix.android.internal.database.query.FilterContent
import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates
import im.vector.matrix.android.internal.database.query.findIncludingEvent
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.database.query.whereInRoom
import im.vector.matrix.android.internal.database.query.*
import im.vector.matrix.android.internal.task.TaskConstraints
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.Debouncer
import im.vector.matrix.android.internal.util.createBackgroundHandler
import im.vector.matrix.android.internal.util.createUIHandler
import io.realm.OrderedCollectionChangeSet
import io.realm.OrderedRealmCollectionChangeListener
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.Sort
import io.realm.*
import timber.log.Timber
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
@ -62,6 +42,7 @@ import kotlin.collections.ArrayList
import kotlin.collections.HashMap


private const val INITIAL_LOAD_SIZE = 30
private const val MIN_FETCHING_COUNT = 30
private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE

@ -72,11 +53,9 @@ internal class DefaultTimeline(
private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask,
private val paginationTask: PaginationTask,
private val cryptoService: CryptoService,
private val timelineEventMapper: TimelineEventMapper,
private val settings: TimelineSettings,
private val hiddenReadReceipts: TimelineHiddenReadReceipts
) : Timeline, TimelineHiddenReadReceipts.Delegate {
cryptoService: CryptoService,
private val allowedTypes: List<String>?
) : Timeline {

private companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
@ -98,8 +77,6 @@ internal class DefaultTimeline(
private val debouncer = Debouncer(mainHandler)

private lateinit var liveEvents: RealmResults<TimelineEventEntity>
private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>

private var roomEntity: RoomEntity? = null

private var prevDisplayIndex: Int = DISPLAY_INDEX_UNKNOWN
@ -110,8 +87,11 @@ internal class DefaultTimeline(
private val backwardsPaginationState = AtomicReference(PaginationState())
private val forwardsPaginationState = AtomicReference(PaginationState())


private val timelineID = UUID.randomUUID().toString()

private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>

private val eventDecryptor = TimelineEventDecryptor(realmConfiguration, timelineID, cryptoService)

private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
@ -150,9 +130,9 @@ internal class DefaultTimeline(
val eventEntity = results[index]
eventEntity?.eventId?.let { eventId ->
builtEventsIdMap[eventId]?.let { builtIndex ->
//Update an existing event
//Update the relation of existing event
builtEvents[builtIndex]?.let { te ->
builtEvents[builtIndex] = buildTimelineEvent(eventEntity)
builtEvents[builtIndex] = eventEntity.asDomain()
hasChanged = true
}
}
@ -182,8 +162,34 @@ internal class DefaultTimeline(
postSnapshot()
}

// private val newSessionListener = object : NewSessionListener {
// override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
// if (roomId == this@DefaultTimeline.roomId) {
// Timber.v("New session id detected for this room")
// BACKGROUND_HANDLER.post {
// val realm = backgroundRealm.get()
// var hasChange = false
// builtEvents.forEachIndexed { index, timelineEvent ->
// if (timelineEvent.isEncrypted()) {
// val eventContent = timelineEvent.root.content.toModel<EncryptedEventContent>()
// if (eventContent?.sessionId == sessionId
// && (timelineEvent.root.mClearEvent == null || timelineEvent.root.mCryptoError != null)) {
// //we need to rebuild this event
// EventEntity.where(realm, eventId = timelineEvent.root.eventId!!).findFirst()?.let {
// //builtEvents[index] = timelineEventFactory.create(it, realm)
// hasChange = true
// }
// }
// }
// }
// if (hasChange) postSnapshot()
// }
// }
// }
//
// }

// Public methods ******************************************************************************
// Public methods ******************************************************************************

override fun paginate(direction: Timeline.Direction, count: Int) {
BACKGROUND_HANDLER.post {
@ -228,20 +234,15 @@ internal class DefaultTimeline(
}

liveEvents = buildEventQuery(realm)
.filterEventsWithSettings()
.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
.findAllAsync()
.also { it.addChangeListener(eventsChangeListener) }

isReady.set(true)

eventRelations = EventAnnotationsSummaryEntity.whereInRoom(realm, roomId)
.findAllAsync()
.also { it.addChangeListener(relationsListener) }

if (settings.buildReadReceipts) {
hiddenReadReceipts.start(realm, liveEvents, this)
}

isReady.set(true)
}
}
}
@ -255,9 +256,6 @@ internal class DefaultTimeline(
roomEntity?.sendingTimelineEvents?.removeAllChangeListeners()
eventRelations.removeAllChangeListeners()
liveEvents.removeAllChangeListeners()
if (settings.buildReadReceipts) {
hiddenReadReceipts.dispose()
}
backgroundRealm.getAndSet(null).also {
it.close()
}
@ -269,28 +267,12 @@ internal class DefaultTimeline(
return hasMoreInCache(direction) || !hasReachedEnd(direction)
}

// TimelineHiddenReadReceipts.Delegate

override fun rebuildEvent(eventId: String, readReceipts: List<ReadReceipt>): Boolean {
return builtEventsIdMap[eventId]?.let { builtIndex ->
//Update the relation of existing event
builtEvents[builtIndex]?.let { te ->
builtEvents[builtIndex] = te.copy(readReceipts = readReceipts)
true
}
} ?: false
}

override fun onReadReceiptsUpdated() {
postSnapshot()
}

// Private methods *****************************************************************************

private fun hasMoreInCache(direction: Timeline.Direction): Boolean {
return Realm.getInstance(realmConfiguration).use { localRealm ->
val timelineEventEntity = buildEventQuery(localRealm).findFirst(direction)
?: return false
?: return false
if (direction == Timeline.Direction.FORWARDS) {
if (findCurrentChunk(localRealm)?.isLastForward == true) {
return false
@ -347,11 +329,9 @@ internal class DefaultTimeline(
val sendingEvents = ArrayList<TimelineEvent>()
if (hasReachedEnd(Timeline.Direction.FORWARDS)) {
roomEntity?.sendingTimelineEvents
?.where()
?.filterEventsWithSettings()
?.findAll()
?.filter { allowedTypes?.contains(it.root?.type) ?: false }
?.forEach {
sendingEvents.add(timelineEventMapper.map(it))
sendingEvents.add(it.asDomain())
}
}
return sendingEvents
@ -398,7 +378,7 @@ internal class DefaultTimeline(
if (initialEventId != null && shouldFetchInitialEvent) {
fetchEvent(initialEventId)
} else {
val count = Math.min(settings.initialSize, liveEvents.size)
val count = Math.min(INITIAL_LOAD_SIZE, liveEvents.size)
if (isLive) {
paginateInternal(initialDisplayIndex, Timeline.Direction.BACKWARDS, count)
} else {
@ -415,9 +395,9 @@ internal class DefaultTimeline(
private fun executePaginationTask(direction: Timeline.Direction, limit: Int) {
val token = getTokenLive(direction) ?: return
val params = PaginationTask.Params(roomId = roomId,
from = token,
direction = direction.toPaginationDirection(),
limit = limit)
from = token,
direction = direction.toPaginationDirection(),
limit = limit)

Timber.v("Should fetch $limit items $direction")
cancelableBag += paginationTask
@ -483,11 +463,10 @@ internal class DefaultTimeline(
nextDisplayIndex = offsetIndex + 1
}
offsetResults.forEach { eventEntity ->

val timelineEvent = buildTimelineEvent(eventEntity)
val timelineEvent = eventEntity.asDomain()

if (timelineEvent.isEncrypted()
&& timelineEvent.root.mxDecryptionResult == null) {
&& timelineEvent.root.mxDecryptionResult == null) {
timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) }
}

@ -502,12 +481,6 @@ internal class DefaultTimeline(
return offsetResults.size
}

private fun buildTimelineEvent(eventEntity: TimelineEventEntity) = timelineEventMapper.map(
timelineEventEntity = eventEntity,
buildReadReceipts = settings.buildReadReceipts,
correctedReadReceipts = hiddenReadReceipts.correctedReadReceipts(eventEntity.eventId)
)

/**
* This has to be called on TimelineThread as it access realm live results
*/
@ -525,6 +498,7 @@ internal class DefaultTimeline(
.greaterThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
}
return offsetQuery
.filterAllowedTypes()
.limit(count)
.findAll()
}
@ -571,7 +545,7 @@ internal class DefaultTimeline(
debouncer.debounce("post_snapshot", runnable, 50)
}

// Extension methods ***************************************************************************
// Extension methods ***************************************************************************

private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
@ -583,20 +557,16 @@ internal class DefaultTimeline(
} else {
sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
}
.filterEventsWithSettings()
.filterAllowedTypes()
.findFirst()
}

private fun RealmQuery<TimelineEventEntity>.filterEventsWithSettings(): RealmQuery<TimelineEventEntity> {
if (settings.filterTypes) {
`in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray())
}
if (settings.filterEdits) {
not().like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE)
private fun RealmQuery<TimelineEventEntity>.filterAllowedTypes(): RealmQuery<TimelineEventEntity> {
if (allowedTypes != null) {
`in`(TimelineEventEntityFields.ROOT.TYPE, allowedTypes.toTypedArray())
}
return this
}

}

private data class PaginationState(

View File

@ -18,38 +18,28 @@ package im.vector.matrix.android.internal.session.room.timeline

import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService
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.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.internal.database.RealmLiveData
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.fetchCopyMap
import javax.inject.Inject

internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask,
private val cryptoService: CryptoService,
private val paginationTask: PaginationTask,
private val timelineEventMapper: TimelineEventMapper,
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper
internal class DefaultTimelineService @Inject constructor(private val roomId: String,
private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask,
private val cryptoService: CryptoService,
private val paginationTask: PaginationTask
) : TimelineService {

@AssistedInject.Factory
interface Factory {
fun create(roomId: String): TimelineService
}

override fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline {
override fun createTimeline(eventId: String?, allowedTypes: List<String>?): Timeline {
return DefaultTimeline(roomId,
eventId,
monarchy.realmConfiguration,
@ -57,10 +47,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
contextOfEventTask,
paginationTask,
cryptoService,
timelineEventMapper,
settings,
TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings)
)
allowedTypes)
}

override fun getTimeLineEvent(eventId: String): TimelineEvent? {
@ -68,7 +55,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
.fetchCopyMap({
TimelineEventEntity.where(it, eventId = eventId).findFirst()
}, { entity, realm ->
timelineEventMapper.map(entity)
entity.asDomain()
})
}

@ -76,8 +63,8 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
val liveData = RealmLiveData(monarchy.realmConfiguration) {
TimelineEventEntity.where(it, eventId = eventId)
}
return Transformations.map(liveData) { events ->
events.firstOrNull()?.let { timelineEventMapper.map(it) }
return Transformations.map(liveData) {
it.firstOrNull()?.asDomain()
}
}


View File

@ -1,153 +0,0 @@
/*
* 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.timeline

import android.util.SparseArray
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntityFields
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
import im.vector.matrix.android.internal.database.query.FilterContent
import im.vector.matrix.android.internal.database.query.whereInRoom
import io.realm.OrderedRealmCollectionChangeListener
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults

/**
* This class is responsible for handling the read receipts for hidden events (check [TimelineSettings] to see filtering).
* When an hidden event has read receipts, we want to transfer these read receipts on the first older displayed event.
* It has to be used in [DefaultTimeline] and we should call the [start] and [dispose] methods to properly handle realm subscription.
*/
internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
private val roomId: String,
private val settings: TimelineSettings) {

interface Delegate {
fun rebuildEvent(eventId: String, readReceipts: List<ReadReceipt>): Boolean
fun onReadReceiptsUpdated()
}

private val correctedReadReceiptsEventByIndex = SparseArray<String>()
private val correctedReadReceiptsByEvent = HashMap<String, MutableList<ReadReceipt>>()

private lateinit var hiddenReadReceipts: RealmResults<ReadReceiptsSummaryEntity>
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
private lateinit var delegate: Delegate

private val hiddenReadReceiptsListener = OrderedRealmCollectionChangeListener<RealmResults<ReadReceiptsSummaryEntity>> { collection, changeSet ->
var hasChange = false
// Deletion here means we don't have any readReceipts for the given hidden events
changeSet.deletions.forEach {
val eventId = correctedReadReceiptsEventByIndex[it]
val timelineEvent = liveEvents.where()
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
.findFirst()

// We are rebuilding the corresponding event with only his own RR
val readReceipts = readReceiptsSummaryMapper.map(timelineEvent?.readReceipts)
hasChange = delegate.rebuildEvent(eventId, readReceipts) || hasChange
}
correctedReadReceiptsEventByIndex.clear()
correctedReadReceiptsByEvent.clear()
hiddenReadReceipts.forEachIndexed { index, summary ->
val timelineEvent = summary?.timelineEvent?.firstOrNull()
val displayIndex = timelineEvent?.root?.displayIndex
if (displayIndex != null) {
// Then we are looking for the first displayable event after the hidden one
val firstDisplayedEvent = liveEvents.where()
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
.findFirst()

// If we find one, we should
if (firstDisplayedEvent != null) {
correctedReadReceiptsEventByIndex.put(index, firstDisplayedEvent.eventId)
correctedReadReceiptsByEvent
.getOrPut(firstDisplayedEvent.eventId, {
ArrayList(readReceiptsSummaryMapper.map(firstDisplayedEvent.readReceipts))
})
.addAll(readReceiptsSummaryMapper.map(summary))
}
}
}
if (correctedReadReceiptsByEvent.isNotEmpty()) {
correctedReadReceiptsByEvent.forEach { (eventId, correctedReadReceipts) ->
val sortedReadReceipts = correctedReadReceipts.sortedByDescending {
it.originServerTs
}
hasChange = delegate.rebuildEvent(eventId, sortedReadReceipts) || hasChange
}
}
if (hasChange) {
delegate.onReadReceiptsUpdated()
}
}

/**
* Start the realm query subscription. Has to be called on an HandlerThread
*/
fun start(realm: Realm, liveEvents: RealmResults<TimelineEventEntity>, delegate: Delegate) {
this.liveEvents = liveEvents
this.delegate = delegate
// We are looking for read receipts set on hidden events.
// We only accept those with a timelineEvent (so coming from pagination/sync).
this.hiddenReadReceipts = ReadReceiptsSummaryEntity.whereInRoom(realm, roomId)
.isNotEmpty(ReadReceiptsSummaryEntityFields.TIMELINE_EVENT)
.isNotEmpty(ReadReceiptsSummaryEntityFields.READ_RECEIPTS.`$`)
.filterReceiptsWithSettings()
.findAllAsync()
.also { it.addChangeListener(hiddenReadReceiptsListener) }
}

/**
* Dispose the realm query subscription. Has to be called on an HandlerThread
*/
fun dispose() {
this.hiddenReadReceipts.removeAllChangeListeners()
}

/**
* Return the current corrected [ReadReceipt] list for an event, or null
*/
fun correctedReadReceipts(eventId: String?): List<ReadReceipt>? {
return correctedReadReceiptsByEvent[eventId]
}


/**
* We are looking for receipts related to filtered events. So, it's the opposite of [DefaultTimeline.filterEventsWithSettings] method.
*/
private fun RealmQuery<ReadReceiptsSummaryEntity>.filterReceiptsWithSettings(): RealmQuery<ReadReceiptsSummaryEntity> {
beginGroup()
if (settings.filterTypes) {
not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
}
if (settings.filterTypes && settings.filterEdits) {
or()
}
if (settings.filterEdits) {
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", FilterContent.EDIT_TYPE)
}
endGroup()
return this
}


}

View File

@ -22,7 +22,7 @@ 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.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
@ -33,7 +33,7 @@ import timber.log.Timber
import javax.inject.Inject


internal class CryptoSyncHandler @Inject constructor(private val cryptoService: DefaultCryptoService,
internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: CryptoManager,
private val sasVerificationService: DefaultSasVerificationService) {

fun handleToDevice(toDevice: ToDeviceSyncResponse, initialSyncProgressService: DefaultInitialSyncProgressService? = null) {
@ -47,13 +47,13 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
} else {
sasVerificationService.onToDeviceEvent(event)
cryptoService.onToDeviceEvent(event)
cryptoManager.onToDeviceEvent(event)
}
}
}

fun onSyncCompleted(syncResponse: SyncResponse) {
cryptoService.onSyncCompleted(syncResponse)
cryptoManager.onSyncCompleted(syncResponse)
}


@ -68,7 +68,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
if (event.getClearType() == EventType.ENCRYPTED) {
var result: MXEventDecryptionResult? = null
try {
result = cryptoService.decryptEvent(event, timelineId ?: "")
result = cryptoManager.decryptEvent(event, timelineId ?: "")
} catch (exception: MXCryptoError) {
event.mCryptoError = (exception as? MXCryptoError.Base)?.errorType //setCryptoError(exception.cryptoError)
}

View File

@ -17,10 +17,6 @@
package im.vector.matrix.android.internal.session.sync

import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.query.createUnmanaged
import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.database.query.where
import io.realm.Realm
import timber.log.Timber
import javax.inject.Inject
@ -33,70 +29,34 @@ import javax.inject.Inject
// dict value ts value
typealias ReadReceiptContent = Map<String, Map<String, Map<String, Map<String, Double>>>>

private const val READ_KEY = "m.read"
private const val TIMESTAMP_KEY = "ts"

internal class ReadReceiptHandler @Inject constructor() {

fun handle(realm: Realm, roomId: String, content: ReadReceiptContent?, isInitialSync: Boolean) {
fun handle(realm: Realm, roomId: String, content: ReadReceiptContent?) {
if (content == null) {
return
}
try {
handleReadReceiptContent(realm, roomId, content, isInitialSync)
val readReceipts = mapContentToReadReceiptEntities(roomId, content)
realm.insertOrUpdate(readReceipts)
} catch (exception: Exception) {
Timber.e("Fail to handle read receipt for room $roomId")
}
}

private fun handleReadReceiptContent(realm: Realm, roomId: String, content: ReadReceiptContent, isInitialSync: Boolean) {
if (isInitialSync) {
initialSyncStrategy(realm, roomId, content)
} else {
incrementalSyncStrategy(realm, roomId, content)
}
}


private fun initialSyncStrategy(realm: Realm, roomId: String, content: ReadReceiptContent) {
val readReceiptSummaries = ArrayList<ReadReceiptsSummaryEntity>()
for ((eventId, receiptDict) in content) {
val userIdsDict = receiptDict[READ_KEY] ?: continue
val readReceiptsSummary = ReadReceiptsSummaryEntity(eventId = eventId, roomId = roomId)

for ((userId, paramsDict) in userIdsDict) {
val ts = paramsDict[TIMESTAMP_KEY] ?: 0.0
val receiptEntity = ReadReceiptEntity.createUnmanaged(roomId, eventId, userId, ts)
readReceiptsSummary.readReceipts.add(receiptEntity)
}
readReceiptSummaries.add(readReceiptsSummary)
}
realm.insertOrUpdate(readReceiptSummaries)
}

private fun incrementalSyncStrategy(realm: Realm, roomId: String, content: ReadReceiptContent) {
for ((eventId, receiptDict) in content) {
val userIdsDict = receiptDict[READ_KEY] ?: continue
val readReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId).findFirst()
?: realm.createObject(ReadReceiptsSummaryEntity::class.java, eventId).apply {
this.roomId = roomId
}

for ((userId, paramsDict) in userIdsDict) {
val ts = paramsDict[TIMESTAMP_KEY] ?: 0.0
val receiptEntity = ReadReceiptEntity.getOrCreate(realm, roomId, userId)
// ensure new ts is superior to the previous one
if (ts > receiptEntity.originServerTs) {
ReadReceiptsSummaryEntity.where(realm, receiptEntity.eventId).findFirst()?.also {
it.readReceipts.remove(receiptEntity)
}
receiptEntity.eventId = eventId
receiptEntity.originServerTs = ts
readReceiptsSummary.readReceipts.add(receiptEntity)
private fun mapContentToReadReceiptEntities(roomId: String, content: ReadReceiptContent): List<ReadReceiptEntity> {
return content
.flatMap { (eventId, receiptDict) ->
receiptDict
.filterKeys { it == "m.read" }
.flatMap { (_, userIdsDict) ->
userIdsDict.map { (userId, paramsDict) ->
val ts = paramsDict.filterKeys { it == "ts" }
.values
.firstOrNull() ?: 0.0
val primaryKey = roomId + userId
ReadReceiptEntity(primaryKey, userId, eventId, roomId, ts)
}
}
}
}
}
}


}

View File

@ -23,7 +23,7 @@ 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.tag.RoomTagContent
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.helper.*
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
@ -50,7 +50,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
private val readReceiptHandler: ReadReceiptHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val roomTagHandler: RoomTagHandler,
private val cryptoService: DefaultCryptoService,
private val cryptoManager: CryptoManager,
private val tokenStore: SyncTokenStore,
private val pushRuleService: DefaultPushRuleService,
private val processForPushTask: ProcessEventForPushTask,
@ -62,11 +62,11 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
}

fun handle(roomsSyncResponse: RoomsSyncResponse, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService? = null) {
fun handle(roomsSyncResponse: RoomsSyncResponse, reporter: DefaultInitialSyncProgressService? = null) {
monarchy.runTransactionSync { realm ->
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, reporter)
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, reporter)
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, reporter)
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), reporter)
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), reporter)
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), reporter)
}

//handle event for bing rule checks
@ -89,20 +89,20 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch

// PRIVATE METHODS *****************************************************************************

private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService?) {
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy, reporter: DefaultInitialSyncProgressService?) {

val rooms = when (handlingStrategy) {
is HandlingStrategy.JOINED ->
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_joined_rooms, 0.6f) {
handleJoinedRoom(realm, it.key, it.value, isInitialSync)
handleJoinedRoom(realm, it.key, it.value)
}
is HandlingStrategy.INVITED ->
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.1f) {
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.4f) {
handleInvitedRoom(realm, it.key, it.value)
}

is HandlingStrategy.LEFT -> {
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.3f) {
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.2f) {
handleLeftRoom(realm, it.key, it.value)
}
}
@ -112,20 +112,12 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch

private fun handleJoinedRoom(realm: Realm,
roomId: String,
roomSync: RoomSync,
isInitalSync: Boolean): RoomEntity {
roomSync: RoomSync): RoomEntity {

Timber.v("Handle join sync for room $roomId")

if (roomSync.ephemeral != null && roomSync.ephemeral.events.isNotEmpty()) {
handleEphemeral(realm, roomId, roomSync.ephemeral, isInitalSync)
}

if (roomSync.accountData != null && roomSync.accountData.events.isNullOrEmpty().not()) {
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
}

val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)

if (roomEntity.membership == Membership.INVITE) {
roomEntity.chunks.deleteAllFromRealm()
@ -134,12 +126,13 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch

// State event
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt() ?: Int.MIN_VALUE
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
?: Int.MIN_VALUE
val untimelinedStateIndex = minStateIndex + 1
roomSync.state.events.forEach { event ->
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
// Give info to crypto module
cryptoService.onStateEvent(roomId, event)
cryptoManager.onStateEvent(roomId, event)
UserEntityFactory.createOrNull(event)?.also {
realm.insertOrUpdate(it)
}
@ -157,6 +150,14 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
roomEntity.addOrUpdate(chunkEntity)
}
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications)

if (roomSync.ephemeral != null && roomSync.ephemeral.events.isNotEmpty()) {
handleEphemeral(realm, roomId, roomSync.ephemeral)
}

if (roomSync.accountData != null && roomSync.accountData.events.isNullOrEmpty().not()) {
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
}
return roomEntity
}

@ -165,7 +166,8 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
roomSync:
InvitedRoomSync): RoomEntity {
Timber.v("Handle invited sync for room $roomId")
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
roomEntity.membership = Membership.INVITE
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
@ -178,7 +180,8 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
private fun handleLeftRoom(realm: Realm,
roomId: String,
roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)

roomEntity.membership = Membership.LEAVE
roomEntity.chunks.deleteAllFromRealm()
@ -210,7 +213,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
event.eventId?.also { eventIds.add(it) }
chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)
// Give info to crypto module
cryptoService.onLiveEvent(roomEntity.roomId, event)
cryptoManager.onLiveEvent(roomEntity.roomId, event)
// Try to remove local echo
event.unsignedData?.transactionId?.also {
val sendingEventEntity = roomEntity.sendingTimelineEvents.find(it)
@ -230,21 +233,17 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
}


@Suppress("UNCHECKED_CAST")
private fun handleEphemeral(realm: Realm,
roomId: String,
ephemeral: RoomSyncEphemeral,
isInitalSync: Boolean) {
for (event in ephemeral.events) {
if (event.type != EventType.RECEIPT) continue
val readReceiptContent = event.content as? ReadReceiptContent ?: continue
readReceiptHandler.handle(realm, roomId, readReceiptContent, isInitalSync)
}
ephemeral: RoomSyncEphemeral) {
ephemeral.events
.filter { it.getClearType() == EventType.RECEIPT }
.map { it.content.toModel<ReadReceiptContent>() }
.forEach { readReceiptHandler.handle(realm, roomId, it) }
}

private fun handleRoomAccountDataEvents(realm: Realm, roomId: String, accountData: RoomSyncAccountData) {
accountData.events
.asSequence()
.filter { it.getClearType() == EventType.TAG }
.map { it.content.toModel<RoomTagContent>() }
.forEach { roomTagHandler.handle(realm, roomId, it) }

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomTagEntity
import im.vector.matrix.android.internal.database.query.where
import io.realm.Realm
import java.util.*
import javax.inject.Inject

internal class RoomTagHandler @Inject constructor() {
@ -29,8 +30,16 @@ internal class RoomTagHandler @Inject constructor() {
if (content == null) {
return
}
val tags = content.tags.entries.map { (tagName, params) ->
RoomTagEntity(tagName, params["order"] as? Double)
val tags = ArrayList<RoomTagEntity>()
for (tagName in content.tags.keys) {
val params = content.tags[tagName]
val order = params?.get("order")
val tag = if (order is Double) {
RoomTagEntity(tagName, order)
} else {
RoomTagEntity(tagName, null)
}
tags.add(tag)
}
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: RoomSummaryEntity(roomId)

View File

@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.sync

import arrow.core.Try
import im.vector.matrix.android.R
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
import im.vector.matrix.android.internal.session.reportSubtask
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
@ -30,7 +30,7 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
private val groupSyncHandler: GroupSyncHandler,
private val cryptoSyncHandler: CryptoSyncHandler,
private val cryptoService: DefaultCryptoService,
private val cryptoManager: CryptoManager,
private val initialSyncProgressService: DefaultInitialSyncProgressService) {

fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
@ -40,12 +40,12 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl
val reporter = initialSyncProgressService.takeIf { isInitialSync }

measureTimeMillis {
if (!cryptoService.isStarted()) {
Timber.v("Should start cryptoService")
cryptoService.start(isInitialSync)
if (!cryptoManager.isStarted()) {
Timber.v("Should start cryptoManager")
cryptoManager.start(isInitialSync)
}
}.also {
Timber.v("Finish handling start cryptoService in $it ms")
Timber.v("Finish handling start cryptoManager in $it ms")
}
val measure = measureTimeMillis {
// Handle the to device events before the room ones
@ -66,7 +66,7 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl

reportSubtask(reporter, R.string.initial_sync_start_importing_account_rooms, 100, 0.7f) {
if (syncResponse.rooms != null) {
roomSyncHandler.handle(syncResponse.rooms, isInitialSync, reporter)
roomSyncHandler.handle(syncResponse.rooms, reporter)
}
}
}.also {

View File

@ -30,7 +30,6 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import kotlinx.coroutines.CancellationException
import timber.log.Timber
import java.net.SocketTimeoutException
import java.util.concurrent.CountDownLatch
@ -71,8 +70,6 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
if (state is SyncState.RUNNING) {
Timber.v("Pause sync...")
updateStateTo(SyncState.PAUSED)
cancelableTask?.cancel()
lock.notify()
}
}

@ -93,25 +90,18 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
backgroundDetectionObserver.register(this)

while (state != SyncState.KILLING) {
Timber.v("Entering loop, state: $state")

if (!networkConnectivityChecker.isConnected() || state == SyncState.PAUSED) {
Timber.v("No network or sync is Paused. Waiting...")
Timber.v("Sync is Paused. Waiting...")
synchronized(lock) {
lock.wait()
}
Timber.v("...unlocked")
} else {
if (state !is SyncState.RUNNING) {
updateStateTo(SyncState.RUNNING(afterPause = true))
}

// No timeout after a pause
val timeout = state.let { if (it is SyncState.RUNNING && it.afterPause) 0 else DEFAULT_LONG_POOL_TIMEOUT }

Timber.v("Execute sync request with timeout $timeout")
Timber.v("[$this] Execute sync request with timeout $DEFAULT_LONG_POOL_TIMEOUT")
val latch = CountDownLatch(1)
val params = SyncTask.Params(timeout)
val params = SyncTask.Params(DEFAULT_LONG_POOL_TIMEOUT)

cancelableTask = syncTask.configureWith(params) {
this.callbackThread = TaskThread.SYNC
@ -119,31 +109,29 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
this.callback = object : MatrixCallback<Unit> {

override fun onSuccess(data: Unit) {
Timber.v("onSuccess")
latch.countDown()
}

override fun onFailure(failure: Throwable) {
if (failure is Failure.NetworkConnection && failure.cause is SocketTimeoutException) {
if (failure is Failure.NetworkConnection
&& failure.cause is SocketTimeoutException) {
// Timeout are not critical
Timber.v("Timeout")
} else if (failure is Failure.Unknown && failure.throwable is CancellationException) {
Timber.v("Cancelled")
} else if (failure is Failure.ServerError
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
// No token or invalid token, stop the thread
Timber.w(failure)
updateStateTo(SyncState.KILLING)
} else {
Timber.e(failure)

if (failure !is Failure.NetworkConnection || failure.cause is JsonEncodingException) {
// Wait 10s before retrying
Timber.v("Wait 10s")
sleep(RETRY_WAIT_TIME_MS)
}
}

if (failure !is Failure.NetworkConnection
|| failure.cause is JsonEncodingException) {
// Wait 10s before retrying
sleep(RETRY_WAIT_TIME_MS)
}

if (failure is Failure.ServerError
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
// No token or invalid token, stop the thread
updateStateTo(SyncState.KILLING)
}
latch.countDown()
}
}
@ -151,10 +139,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
.executeBy(taskExecutor)

latch.await()
state.let {
if (it is SyncState.RUNNING && it.afterPause) {
updateStateTo(SyncState.RUNNING(afterPause = false))
}
if (state is SyncState.RUNNING) {
updateStateTo(SyncState.RUNNING(afterPause = false))
}

Timber.v("...Continue")

View File

@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.util
import android.content.Context
import androidx.work.WorkManager
import im.vector.matrix.android.api.util.Cancelable
import java.util.UUID
import java.util.*

internal class CancelableWork(private val context: Context,
private val workId: UUID) : Cancelable {

View File

@ -34,7 +34,7 @@ import java.security.*
import java.security.cert.CertificateException
import java.security.spec.AlgorithmParameterSpec
import java.security.spec.RSAKeyGenParameterSpec
import java.util.Calendar
import java.util.*
import java.util.zip.GZIPOutputStream
import javax.crypto.*
import javax.crypto.spec.GCMParameterSpec

View File

@ -22,7 +22,7 @@ import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import timber.log.Timber
import java.util.TreeSet
import java.util.*

/**
* Build canonical Json
@ -60,33 +60,43 @@ object JsonCanonicalizer {
when (any) {
is JSONArray -> {
// Canonicalize each element of the array
return (0 until any.length()).joinToString(separator = ",", prefix = "[", postfix = "]") {
canonicalizeRecursive(any.get(it))
val result = StringBuilder("[")

for (i in 0 until any.length()) {
result.append(canonicalizeRecursive(any.get(i)))
if (i < any.length() - 1) {
result.append(",")
}
}

result.append("]")

return result.toString()
}
is JSONObject -> {
// Sort the attributes by name, and the canonicalize each element of the JSONObject
val result = StringBuilder("{")

val attributes = TreeSet<String>()
for (entry in any.keys()) {
attributes.add(entry)
}

return buildString {
append("{")
for ((index, value) in attributes.withIndex()) {
append("\"")
append(value)
append("\"")
append(":")
append(canonicalizeRecursive(any[value]))
for (attribute in attributes.withIndex()) {
result.append("\"")
.append(attribute.value)
.append("\"")
.append(":")
.append(canonicalizeRecursive(any[attribute.value]))

if (index < attributes.size - 1) {
append(",")
}
if (attribute.index < attributes.size - 1) {
result.append(",")
}
append("}")
}

result.append("}")

return result.toString()
}
is String -> return JSONObject.quote(any)
else -> return any.toString()

View File

@ -167,9 +167,4 @@
<string name="initial_sync_start_importing_account_data">Начална синхронизация:
\nИмпортиране на данни за профила</string>

<string name="notice_room_update">%s обнови тази стая.</string>

<string name="event_status_sending_message">Изпращане на съобщение…</string>
<string name="clear_timeline_send_queue">Изчисти опашката за изпращане</string>

</resources>

View File

@ -105,65 +105,4 @@
<string name="verification_emoji_pig">Schwein</string>
<string name="verification_emoji_elephant">Elefant</string>
<string name="verification_emoji_rabbit">Hase</string>
<string name="notice_room_update">%s hat diesen Raum aufgewertet.</string>

<string name="verification_emoji_panda">Panda</string>
<string name="verification_emoji_rooster">Hahn</string>
<string name="verification_emoji_penguin">Pinguin</string>
<string name="verification_emoji_turtle">Schildkröte</string>
<string name="verification_emoji_fish">Fisch</string>
<string name="verification_emoji_octopus">Tintenfisch</string>
<string name="verification_emoji_butterfly">Schmetterling</string>
<string name="verification_emoji_flower">Blume</string>
<string name="verification_emoji_tree">Baum</string>
<string name="verification_emoji_cactus">Kaktus</string>
<string name="verification_emoji_mushroom">Pilz</string>
<string name="verification_emoji_globe">Globus</string>
<string name="verification_emoji_moon">Mond</string>
<string name="verification_emoji_cloud">Wolke</string>
<string name="verification_emoji_fire">Feuer</string>
<string name="verification_emoji_banana">Banane</string>
<string name="verification_emoji_apple">Apfel</string>
<string name="verification_emoji_strawberry">Erdbeere</string>
<string name="verification_emoji_corn">Mais</string>
<string name="verification_emoji_cake">Kuchen</string>
<string name="verification_emoji_heart">Herz</string>
<string name="verification_emoji_smiley">Lächeln</string>
<string name="verification_emoji_robot">Roboter</string>
<string name="verification_emoji_hat">Hut</string>
<string name="verification_emoji_glasses">Brille</string>
<string name="verification_emoji_wrench">Schraubenschlüssel</string>
<string name="verification_emoji_santa">Nikolaus</string>
<string name="verification_emoji_thumbsup">Daumen hoch</string>
<string name="verification_emoji_umbrella">Regenschirm</string>
<string name="verification_emoji_hourglass">Sanduhr</string>
<string name="verification_emoji_clock">Uhr</string>
<string name="verification_emoji_gift">Geschenk</string>
<string name="verification_emoji_lightbulb">Glühbirne</string>
<string name="verification_emoji_book">Buch</string>
<string name="verification_emoji_pencil">Stift</string>
<string name="verification_emoji_paperclip">Büroklammer</string>
<string name="verification_emoji_scissors">Scheren</string>
<string name="verification_emoji_lock">sperren</string>
<string name="verification_emoji_key">Schlüssel</string>
<string name="verification_emoji_hammer">Hammer</string>
<string name="verification_emoji_telephone">Telefon</string>
<string name="verification_emoji_flag">Flagge</string>
<string name="verification_emoji_train">Zug</string>
<string name="verification_emoji_bicycle">Fahrrad</string>
<string name="verification_emoji_airplane">Flugzeug</string>
<string name="verification_emoji_rocket">Rakete</string>
<string name="verification_emoji_trophy">Pokal</string>
<string name="verification_emoji_ball">Ball</string>
<string name="verification_emoji_guitar">Gitarre</string>
<string name="verification_emoji_trumpet">Trompete</string>
<string name="verification_emoji_bell">Glocke</string>
<string name="verification_emoji_anchor">Anker</string>
<string name="verification_emoji_headphone">Kopfhörer</string>
<string name="verification_emoji_folder">Ordner</string>
<string name="verification_emoji_pin">Stecknadel</string>

<string name="event_status_sending_message">Sende eine Nachricht…</string>
<string name="clear_timeline_send_queue">Sendewarteschlange leeren</string>

</resources>

View File

@ -167,9 +167,4 @@
<string name="initial_sync_start_importing_account_data">Hasierako sinkronizazioa:
\nKontuaren datuak inportatzen</string>

<string name="notice_room_update">%s erabiltzaileak gela hau eguneratu du.</string>

<string name="event_status_sending_message">Mezua bidaltzen…</string>
<string name="clear_timeline_send_queue">Garbitu bidalketa-ilara</string>

</resources>

View File

@ -168,9 +168,4 @@
<string name="initial_sync_start_importing_account_data">Alkusynkronointi:
\nTuodaan tilin tietoja</string>

<string name="notice_room_update">%s päivitti tämän huoneen.</string>

<string name="event_status_sending_message">Lähetetään viestiä…</string>
<string name="clear_timeline_send_queue">Tyhjennä lähetysjono</string>

</resources>

View File

@ -167,9 +167,4 @@
<string name="initial_sync_start_importing_account_data">Synchronisation initiale :
\nImportation des données du compte</string>

<string name="notice_room_update">%s a mis à niveau ce salon.</string>

<string name="event_status_sending_message">Envoi du message…</string>
<string name="clear_timeline_send_queue">Vider la file denvoi</string>

</resources>

View File

@ -166,9 +166,4 @@
<string name="initial_sync_start_importing_account_data">Induló szinkronizáció:
\nFiók adatok betöltése</string>

<string name="notice_room_update">%s frissítette ezt a szobát.</string>

<string name="event_status_sending_message">Üzenet küldése…</string>
<string name="clear_timeline_send_queue">Küldő sor ürítése</string>

</resources>

View File

@ -167,9 +167,4 @@
<string name="initial_sync_start_importing_account_data">Sync iniziale:
\nImportazione dati account</string>

<string name="notice_room_update">%s ha aggiornato questa stanza.</string>

<string name="event_status_sending_message">Invio messaggio in corso …</string>
<string name="clear_timeline_send_queue">Cancella la coda di invio</string>

</resources>

View File

@ -1,173 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="summary_message">%1$s: %2$s</string>
<string name="notice_room_invite_no_invitee">%s님의 초대</string>
<string name="notice_room_invite_no_invitee">%s\'의 초대</string>
<string name="verification_emoji_headphone">헤드폰</string>
<string name="summary_user_sent_image">%1$s님이 사진을 보냈습니다.</string>
<string name="summary_user_sent_sticker">%1$s님이 스티커를 보냈습니다.</string>

<string name="notice_room_invite">%1$s님이 %2$s님을 초대했습니다</string>
<string name="notice_room_invite_you">%1$s님이 당신을 초대했습니다</string>
<string name="notice_room_join">%1$s님이 참가했습니다</string>
<string name="notice_room_leave">%1$s님이 떠났습니다</string>
<string name="notice_room_reject">%1$s님이 초대를 거부했습니다</string>
<string name="notice_room_kick">%1$s님이 %2$s님을 추방했습니다</string>
<string name="notice_room_unban">%1$s님이 %2$s님의 차단을 풀었습니다</string>
<string name="notice_room_ban">%1$s님이 %2$s님을 차단했습니다</string>
<string name="notice_room_withdraw">%1$s님이 %2$s님의 초대를 취소했습니다</string>
<string name="notice_avatar_url_changed">%1$s님이 아바타를 변경했습니다</string>
<string name="notice_display_name_set">%1$s님이 표시 이름을 %2$s(으)로 설정했습니다</string>
<string name="notice_display_name_changed_from">%1$s님이 표시 이름을 %2$s에서 %3$s(으)로 변경했습니다</string>
<string name="notice_display_name_removed">%1$s님이 표시 이름을 삭제했습니다 (%2$s)</string>
<string name="notice_room_topic_changed">%1$s님이 주제를 다음으로 변경했습니다: %2$s</string>
<string name="notice_room_name_changed">%1$s님이 방 이름을 다음으로 변경했습니다: %2$s</string>
<string name="notice_placed_video_call">%s님이 영상 통화를 걸었습니다.</string>
<string name="notice_placed_voice_call">%s님이 음성 통화를 걸었습니다.</string>
<string name="notice_answered_call">%s님이 전화를 받았습니다.</string>
<string name="notice_ended_call">%s님이 전화를 끊었습니다.</string>
<string name="notice_made_future_room_visibility">%1$s님이 이후 %2$s에게 방 기록을 공개했습니다</string>
<string name="notice_room_visibility_invited">초대된 시점부터 모든 방 구성원.</string>
<string name="notice_room_visibility_joined">들어온 시점부터 모든 방 구성원.</string>
<string name="notice_room_visibility_shared">모든 방 구성원.</string>
<string name="notice_room_visibility_world_readable">누구나.</string>
<string name="notice_room_visibility_unknown">알 수 없음 (%s).</string>
<string name="notice_end_to_end">%1$s님이 종단 간 암호화를 켰습니다 (%2$s)</string>
<string name="notice_room_update">%s님이 방을 업그레이드했습니다.</string>

<string name="notice_requested_voip_conference">%1$s님이 VoIP 회의를 요청했습니다</string>
<string name="notice_voip_started">VoIP 회의가 시작했습니다</string>
<string name="notice_voip_finished">VoIP 회의가 끝났습니다</string>

<string name="notice_avatar_changed_too">(아바타도 변경됨)</string>
<string name="notice_room_name_removed">%1$s님이 방 이름을 삭제했습니다</string>
<string name="notice_room_topic_removed">%1$s님이 방 주제를 삭제했습니다</string>
<string name="notice_event_redacted">메시지가 삭제되었습니다</string>
<string name="notice_event_redacted_by">메시지가 %1$s님에 의해 삭제되었습니다</string>
<string name="notice_event_redacted_with_reason">메시지가 삭제되었습니다 [이유: %1$s]</string>
<string name="notice_event_redacted_by_with_reason">메시지가 %1$s님에 의해 삭제되었습니다 [이유: %2$s]</string>
<string name="notice_profile_change_redacted">%1$s님이 프로필 %2$s을(를) 업데이트했습니다</string>
<string name="notice_room_third_party_invite">%1$s님이 %2$s님에게 방 초대를 보냈습니다</string>
<string name="notice_room_third_party_registered_invite">%1$s님이 %2$s의 초대를 수락했습니다</string>

<string name="notice_crypto_unable_to_decrypt">** 암호를 해독할 수 없음: %s **</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">발신인의 기기에서 이 메시지의 키를 보내지 않았습니다.</string>

<string name="message_reply_to_prefix">이 답장의 질문</string>

<string name="could_not_redact">검열할 수 없습니다</string>
<string name="unable_to_send_message">메시지를 보낼 수 없습니다</string>

<string name="message_failed_to_upload">사진 업로드에 실패했습니다</string>

<string name="network_error">네트워크 오류</string>
<string name="matrix_error">Matrix 오류</string>

<string name="room_error_join_failed_empty_room">현재 빈 방에 다시 들어갈 수 없습니다.</string>

<string name="encrypted_message">암호화된 메시지</string>

<string name="medium_email">이메일 주소</string>
<string name="medium_phone_number">전화번호</string>

<string name="reply_to_an_image">사진을 보냈습니다.</string>
<string name="reply_to_a_video">동영상을 보냈습니다.</string>
<string name="reply_to_an_audio_file">오디오 파일을 보냈습니다.</string>
<string name="reply_to_a_file">파일을 보냈습니다.</string>

<string name="room_displayname_invite_from">%s에서 초대함</string>
<string name="room_displayname_room_invite">방 초대</string>

<string name="room_displayname_two_members">%1$s님과 %2$s님</string>

<plurals name="room_displayname_three_and_more_members">
<item quantity="other">%1$s님 외 %2$d명</item>
</plurals>

<string name="room_displayname_empty_room">빈 방</string>


<string name="verification_emoji_dog">개</string>
<string name="verification_emoji_cat">고양이</string>
<string name="verification_emoji_lion">사자</string>
<string name="verification_emoji_horse">말</string>
<string name="verification_emoji_unicorn">유니콘</string>
<string name="verification_emoji_pig">돼지</string>
<string name="verification_emoji_elephant">코끼리</string>
<string name="verification_emoji_rabbit">토끼</string>
<string name="verification_emoji_panda">판다</string>
<string name="verification_emoji_rooster">수탉</string>
<string name="verification_emoji_penguin">펭귄</string>
<string name="verification_emoji_turtle">거북</string>
<string name="verification_emoji_fish">물고기</string>
<string name="verification_emoji_octopus">문어</string>
<string name="verification_emoji_butterfly">나비</string>
<string name="verification_emoji_flower">꽃</string>
<string name="verification_emoji_tree">나무</string>
<string name="verification_emoji_cactus">선인장</string>
<string name="verification_emoji_mushroom">버섯</string>
<string name="verification_emoji_globe">지구본</string>
<string name="verification_emoji_moon">달</string>
<string name="verification_emoji_cloud">구름</string>
<string name="verification_emoji_fire">불</string>
<string name="verification_emoji_banana">바나나</string>
<string name="verification_emoji_apple">사과</string>
<string name="verification_emoji_strawberry">딸기</string>
<string name="verification_emoji_corn">옥수수</string>
<string name="verification_emoji_pizza">피자</string>
<string name="verification_emoji_cake">케이크</string>
<string name="verification_emoji_heart">하트</string>
<string name="verification_emoji_smiley">웃음</string>
<string name="verification_emoji_robot">로봇</string>
<string name="verification_emoji_hat">모자</string>
<string name="verification_emoji_glasses">안경</string>
<string name="verification_emoji_wrench">스패너</string>
<string name="verification_emoji_santa">산타클로스</string>
<string name="verification_emoji_thumbsup">좋아요</string>
<string name="verification_emoji_umbrella">우산</string>
<string name="verification_emoji_hourglass">모래시계</string>
<string name="verification_emoji_clock">시계</string>
<string name="verification_emoji_gift">선물</string>
<string name="verification_emoji_lightbulb">전구</string>
<string name="verification_emoji_book">책</string>
<string name="verification_emoji_pencil">연필</string>
<string name="verification_emoji_paperclip">클립</string>
<string name="verification_emoji_scissors">가위</string>
<string name="verification_emoji_lock">자물쇠</string>
<string name="verification_emoji_key">열쇠</string>
<string name="verification_emoji_hammer">망치</string>
<string name="verification_emoji_telephone">전화기</string>
<string name="verification_emoji_flag">깃발</string>
<string name="verification_emoji_train">기차</string>
<string name="verification_emoji_bicycle">자전거</string>
<string name="verification_emoji_airplane">비행기</string>
<string name="verification_emoji_rocket">로켓</string>
<string name="verification_emoji_trophy">트로피</string>
<string name="verification_emoji_ball">공</string>
<string name="verification_emoji_guitar">기타</string>
<string name="verification_emoji_trumpet">트럼펫</string>
<string name="verification_emoji_bell">종</string>
<string name="verification_emoji_anchor">닻</string>
<string name="verification_emoji_folder">폴더</string>
<string name="verification_emoji_pin">핀</string>

<string name="initial_sync_start_importing_account">초기 동기화:
\n계정 가져오는 중…</string>
<string name="initial_sync_start_importing_account_crypto">초기 동기화:
\n암호 가져오는 중</string>
<string name="initial_sync_start_importing_account_rooms">초기 동기화:
\n방 가져오는 중</string>
<string name="initial_sync_start_importing_account_joined_rooms">초기 동기화:
\n들어간 방 가져오는 중</string>
<string name="initial_sync_start_importing_account_invited_rooms">초기 동기화:
\n초대받은 방 가져오는 중</string>
<string name="initial_sync_start_importing_account_left_rooms">초기 동기화:
\n떠난 방 가져오는 중</string>
<string name="initial_sync_start_importing_account_groups">초기 동기화:
\n커뮤니티 가져오는 중</string>
<string name="initial_sync_start_importing_account_data">초기 동기화:
\n계정 데이터 가져오는 중</string>

<string name="event_status_sending_message">메시지 보내는 중…</string>
<string name="clear_timeline_send_queue">전송 대기 열 지우기</string>

</resources>

View File

@ -176,9 +176,4 @@
<string name="initial_sync_start_importing_account_data">Initiële synchronisatie:
\nAccountgegevens worden geïmporteerd</string>

<string name="notice_room_update">%s heeft dit gesprek opgewaardeerd.</string>

<string name="event_status_sending_message">Bericht wordt verstuurd…</string>
<string name="clear_timeline_send_queue">Uitgaande wachtrij legen</string>

</resources>

View File

@ -7,7 +7,7 @@
<string name="notice_room_invite">%1$s zaprosił(a) %2$s</string>
<string name="notice_room_invite_you">%1$s zaprosił(a) Cię</string>
<string name="notice_room_join">%1$s dołączył(a)</string>
<string name="notice_room_leave">%1$s opuścił(a)</string>
<string name="notice_room_leave">%1$s wyszedł(-ła)</string>
<string name="notice_room_reject">%1$s odrzucił(a) zaproszenie</string>
<string name="notice_room_kick">%1$s wyrzucił(a) %2$s</string>
<string name="notice_room_unban">%1$s odblokował(a) %2$s</string>
@ -17,11 +17,11 @@
<string name="notice_display_name_changed_from">%1$s zmienił(a) wyświetlaną nazwę z %2$s na %3$s</string>
<string name="notice_display_name_removed">%1$s usunął(-ęła) swoją wyświetlaną nazwę (%2$s)</string>
<string name="notice_room_topic_changed">%1$s zmienił(a) temat na: %2$s</string>
<string name="unable_to_send_message">Nie można wysłać wiadomości</string>
<string name="unable_to_send_message">Nie udało się wysłać wiadomości</string>

<string name="message_failed_to_upload">Przesyłanie zdjęcia nie powiodło się</string>
<string name="message_failed_to_upload">Nie udało się wysłać zdjęcia</string>

<string name="network_error">Błąd sieci</string>
<string name="network_error">ogólne błędy</string>
<string name="matrix_error">Błąd Matrixa</string>

<string name="encrypted_message">Wiadomość zaszyfrowana</string>
@ -31,7 +31,7 @@

<string name="notice_room_visibility_shared">wszyscy członkowie pokoju.</string>
<string name="notice_room_visibility_world_readable">wszyscy.</string>
<string name="notice_room_name_changed">%1$s zmienił(a) nazwę pokoju na: %2$s</string>
<string name="notice_room_name_changed">%1$s zmienił(a) znawę pokoju na: %2$s</string>
<string name="notice_ended_call">%s zakończył(a) rozmowę.</string>
<string name="notice_room_name_removed">%1$s usunął(-ęła) nazwę pokoju</string>
<string name="notice_room_topic_removed">%1$s usunął(-ęła) temat pokoju</string>
@ -57,9 +57,9 @@
</plurals>

<string name="notice_crypto_unable_to_decrypt">** Nie można odszyfrować: %s **</string>
<string name="notice_placed_video_call">%s wykonał(a) rozmowę wideo.</string>
<string name="notice_placed_voice_call">%s wykonał(a) połączenie głosowe.</string>
<string name="notice_made_future_room_visibility">%1$s uczynił(a) przyszłą historię pokoju widoczną dla %2$s</string>
<string name="notice_placed_video_call">%s umieścił wideo rozmowe.</string>
<string name="notice_placed_voice_call">%s umieścił połączenie głosowe.</string>
<string name="notice_made_future_room_visibility">%1$s uczynił historię pokoju widoczną do %2$s</string>
<string name="notice_room_visibility_invited">wszyscy członkowie pokoju, od momentu w którym zostali zaproszeni.</string>
<string name="notice_room_visibility_joined">wszyscy członkowie pokoju, od momentu w którym dołączyli.</string>
<string name="notice_room_visibility_unknown">nieznane (%s).</string>
@ -147,29 +147,4 @@
<string name="verification_emoji_santa">Mikołaj</string>
<string name="verification_emoji_gift">Prezent</string>
<string name="verification_emoji_hammer">Młotek</string>
<string name="notice_room_update">%s zakutalizował(a) ten pokój.</string>

<string name="verification_emoji_thumbsup">Kciuk w górę</string>
<string name="verification_emoji_lock">Zamek</string>
<string name="verification_emoji_ball">Piłka</string>
<string name="initial_sync_start_importing_account">Synchronizacja początkowa:
\nImportowanie konta…</string>
<string name="initial_sync_start_importing_account_crypto">Synchronizacja początkowa:
\nImportowanie kryptografii</string>
<string name="initial_sync_start_importing_account_rooms">Synchronizacja początkowa:
\nImportowanie Pokoi</string>
<string name="initial_sync_start_importing_account_joined_rooms">Synchronizacja początkowa:
\nImportowanie dołączonych Pokoi</string>
<string name="initial_sync_start_importing_account_invited_rooms">Synchronizacja początkowa:
\nImportowanie zaproszonych Pokoi</string>
<string name="initial_sync_start_importing_account_left_rooms">Synchronizacja początkowa:
\nImportowanie opuszczonych Pokoi</string>
<string name="initial_sync_start_importing_account_groups">Synchronizacja początkowa:
\nImportowanie Społeczności</string>
<string name="initial_sync_start_importing_account_data">Synchronizacja początkowa:
\nImportowanie danych Konta</string>

<string name="event_status_sending_message">Wysyłanie wiadomości…</string>
<string name="clear_timeline_send_queue">Wyczyść kolejkę wysyłania</string>

</resources>

View File

@ -78,10 +78,4 @@
<string name="room_displayname_empty_room">Sala vazia</string>


<string name="summary_user_sent_sticker">%1$s enviou um sticker.</string>

<string name="notice_room_update">%s fez o upgrade da sala.</string>

<string name="notice_event_redacted">Mensagem removida</string>
<string name="notice_event_redacted_by">Mensagem removida por %1$s</string>
</resources>

View File

@ -169,9 +169,9 @@
\nИмпорт криптографии</string>
<string name="initial_sync_start_importing_account_rooms">Начальная синхронизация:
\nИмпорт комнат</string>
<string name="initial_sync_start_importing_account_joined_rooms">Синхронизация начата:
<string name="initial_sync_start_importing_account_joined_rooms">Начальная синхронизация:
\nИмпорт присоединенных комнат</string>
<string name="initial_sync_start_importing_account_invited_rooms">Синхронизация начата:
<string name="initial_sync_start_importing_account_invited_rooms">Начальная синхронизация:
\nИмпорт приглашенных комнат</string>
<string name="initial_sync_start_importing_account_left_rooms">Начальная синхронизация:
\nИмпорт покинутых комнат</string>
@ -180,9 +180,4 @@
<string name="initial_sync_start_importing_account_data">Начальная синхронизация:
\nИмпорт данных учетной записи</string>

<string name="notice_room_update">%s обновил эту комнату.</string>

<string name="event_status_sending_message">Отправка сообщения…</string>
<string name="clear_timeline_send_queue">Очистить очередь отправки</string>

</resources>

View File

@ -82,95 +82,4 @@
</plurals>


<string name="notice_room_update">%s aktualizoval túto miestnosť.</string>

<string name="notice_event_redacted">Správa odstránená</string>
<string name="notice_event_redacted_by">Správa odstránená používateľom %1$s</string>
<string name="notice_event_redacted_with_reason">Správa odstránená [dôvod: %1$s]</string>
<string name="notice_event_redacted_by_with_reason">Správa odstránená používateľom %1$s [dôvod: %2$s]</string>
<string name="verification_emoji_dog">Pes</string>
<string name="verification_emoji_cat">Mačka</string>
<string name="verification_emoji_lion">Lev</string>
<string name="verification_emoji_horse">Kôň</string>
<string name="verification_emoji_unicorn">Jednorožec</string>
<string name="verification_emoji_pig">Prasa</string>
<string name="verification_emoji_elephant">Slon</string>
<string name="verification_emoji_rabbit">Zajac</string>
<string name="verification_emoji_panda">Panda</string>
<string name="verification_emoji_rooster">Kohút</string>
<string name="verification_emoji_penguin">Tučniak</string>
<string name="verification_emoji_turtle">Korytnačka</string>
<string name="verification_emoji_fish">Ryba</string>
<string name="verification_emoji_octopus">Chobotnica</string>
<string name="verification_emoji_butterfly">Motýľ</string>
<string name="verification_emoji_flower">Kvetina</string>
<string name="verification_emoji_tree">Strom</string>
<string name="verification_emoji_cactus">Kaktus</string>
<string name="verification_emoji_mushroom">Hríb</string>
<string name="verification_emoji_globe">Zemeguľa</string>
<string name="verification_emoji_moon">Mesiac</string>
<string name="verification_emoji_cloud">Oblak</string>
<string name="verification_emoji_fire">Oheň</string>
<string name="verification_emoji_banana">Banán</string>
<string name="verification_emoji_apple">Jablko</string>
<string name="verification_emoji_strawberry">Jahoda</string>
<string name="verification_emoji_corn">Kukurica</string>
<string name="verification_emoji_pizza">Pizza</string>
<string name="verification_emoji_cake">Koláč</string>
<string name="verification_emoji_heart">Srdce</string>
<string name="verification_emoji_smiley">Úsmev</string>
<string name="verification_emoji_robot">Robot</string>
<string name="verification_emoji_hat">Klobúk</string>
<string name="verification_emoji_glasses">Okuliare</string>
<string name="verification_emoji_wrench">Skrutkovač</string>
<string name="verification_emoji_santa">Mikuláš</string>
<string name="verification_emoji_thumbsup">Palec nahor</string>
<string name="verification_emoji_umbrella">Dáždnik</string>
<string name="verification_emoji_hourglass">Presýpacie hodiny</string>
<string name="verification_emoji_clock">Hodiny</string>
<string name="verification_emoji_gift">Darček</string>
<string name="verification_emoji_lightbulb">Žiarovka</string>
<string name="verification_emoji_book">Kniha</string>
<string name="verification_emoji_pencil">Ceruzka</string>
<string name="verification_emoji_paperclip">Kancelárska sponka</string>
<string name="verification_emoji_scissors">Nožnice</string>
<string name="verification_emoji_lock">Zámok</string>
<string name="verification_emoji_key">Kľúč</string>
<string name="verification_emoji_hammer">Kladivo</string>
<string name="verification_emoji_telephone">Telefón</string>
<string name="verification_emoji_flag">Vlajka</string>
<string name="verification_emoji_train">Vlak</string>
<string name="verification_emoji_bicycle">Bicykel</string>
<string name="verification_emoji_airplane">Lietadlo</string>
<string name="verification_emoji_rocket">Raketa</string>
<string name="verification_emoji_trophy">Trofej</string>
<string name="verification_emoji_ball">Lopta</string>
<string name="verification_emoji_guitar">Gitara</string>
<string name="verification_emoji_trumpet">Trúbka</string>
<string name="verification_emoji_bell">Zvonček</string>
<string name="verification_emoji_anchor">Kotva</string>
<string name="verification_emoji_headphone">Schlúchadlá</string>
<string name="verification_emoji_folder">Priečinok</string>
<string name="verification_emoji_pin">Pin</string>

<string name="initial_sync_start_importing_account">Úvodná synchronizácia:
\nPrebieha import účtu…</string>
<string name="initial_sync_start_importing_account_crypto">Úvodná synchronizácia:
\nPrebieha import šifrovacích kľúčov</string>
<string name="initial_sync_start_importing_account_rooms">Úvodná synchronizácia:
\nPrebieha import miestností</string>
<string name="initial_sync_start_importing_account_joined_rooms">Úvodná synchronizácia:
\nPrebieha import miestností, do ktorých ste vstúpili</string>
<string name="initial_sync_start_importing_account_invited_rooms">Úvodná synchronizácia:
\nPrebieha import pozvánok</string>
<string name="initial_sync_start_importing_account_left_rooms">Úvodná synchronizácia:
\nPrebieha import opustených miestností</string>
<string name="initial_sync_start_importing_account_groups">Úvodná synchronizácia:
\nPrebieha import komunít</string>
<string name="initial_sync_start_importing_account_data">Úvodná synchronizácia:
\nPrebieha import údajov účtu</string>

<string name="event_status_sending_message">Odosielanie správy…</string>
<string name="clear_timeline_send_queue">Vymazať správy na odoslanie</string>

</resources>

View File

@ -146,26 +146,4 @@
<string name="verification_emoji_anchor">Spirancë</string>
<string name="verification_emoji_headphone">Kufje</string>
<string name="verification_emoji_folder">Dosje</string>
<string name="notice_room_update">%s e përmirësoi këtë dhomë.</string>

<string name="initial_sync_start_importing_account">Njëkohësimi Fillestar:
\nPo importohet llogaria…</string>
<string name="initial_sync_start_importing_account_crypto">Njëkohësimi Fillestar:
\nPo importohet kriptografi</string>
<string name="initial_sync_start_importing_account_rooms">Njëkohësimi Fillestar:
\nPo importohen Dhoma</string>
<string name="initial_sync_start_importing_account_joined_rooms">Njëkohësimi Fillestar:
\nPo importohen Dhoma Ku Është Bërë Hyrje</string>
<string name="initial_sync_start_importing_account_invited_rooms">Njëkohësimi Fillestar:
\nPo importohen Dhoma Me Ftesë</string>
<string name="initial_sync_start_importing_account_left_rooms">Njëkohësimi Fillestar:
\nPo importohen Dhoma të Braktisura</string>
<string name="initial_sync_start_importing_account_groups">Njëkohësimi Fillestar:
\nPo importohen Bashkësi</string>
<string name="initial_sync_start_importing_account_data">Njëkohësimi Fillestar:
\nPo importohet të Dhëna Llogarie</string>

<string name="event_status_sending_message">Po dërgohet mesazh…</string>
<string name="clear_timeline_send_queue">Spastro radhë pritjeje</string>

</resources>

View File

@ -48,7 +48,7 @@
<string name="notice_room_third_party_registered_invite">%1$s èt duutnodigienge vo %2$s anveird</string>

<string name="notice_crypto_unable_to_decrypt">** Kun nie ountsleuteln: %s **</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">t Toestel van den afzender èt geen sleutels vo da bericht hier gesteurd.</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">t Toestel van den afzender èt geen sleutels vo dit bericht gesteurd.</string>

<string name="message_reply_to_prefix">Als antwoord ip</string>

@ -150,26 +150,21 @@
<string name="verification_emoji_folder">Mappe</string>
<string name="verification_emoji_pin">Pinne</string>

<string name="initial_sync_start_importing_account">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account">Initiële synchronisoatie:
\nAccount wor geïmporteerd…</string>
<string name="initial_sync_start_importing_account_crypto">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_crypto">Initiële synchronisoatie:
\nCrypto wor geïmporteerd</string>
<string name="initial_sync_start_importing_account_rooms">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_rooms">Initiële synchronisoatie:
\nGesprekkn wordn geïmporteerd</string>
<string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisoatie:
\nDeelgenoomn gesprekken wordn geïmporteerd</string>
<string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisoatie:
\nUutgenodigde gesprekkn wordn geïmporteerd</string>
<string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisoatie:
\nVerloatn gesprekkn wordn geïmporteerd</string>
<string name="initial_sync_start_importing_account_groups">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_groups">Initiële synchronisoatie:
\nGemeenschappn wordn geïmporteerd</string>
<string name="initial_sync_start_importing_account_data">Initiële synchronisoasje:
<string name="initial_sync_start_importing_account_data">Initiële synchronisoatie:
\nAccountgegeevns wordn geïmporteerd</string>

<string name="notice_room_update">%s èt da gesprek hier ipgewoardeerd.</string>

<string name="event_status_sending_message">Bericht wor verstuurd…</string>
<string name="clear_timeline_send_queue">Uutgoande wachtreeke leegn</string>

</resources>

View File

@ -162,9 +162,4 @@
<string name="initial_sync_start_importing_account_data">初始化同步:
\n正在导入账号数据</string>

<string name="notice_room_update">%s 升级了聊天室。</string>

<string name="event_status_sending_message">正在发送消息…</string>
<string name="clear_timeline_send_queue">清除正在发送队列</string>

</resources>

View File

@ -165,9 +165,4 @@
<string name="initial_sync_start_importing_account_data">初始化同步:
\n正在匯入帳號資料</string>

<string name="notice_room_update">%s 已升級此聊天室。</string>

<string name="event_status_sending_message">正在傳送訊息……</string>
<string name="clear_timeline_send_queue">清除傳送佇列</string>

</resources>

View File

@ -15,7 +15,7 @@ androidExtensions {
}

ext.versionMajor = 0
ext.versionMinor = 5
ext.versionMinor = 3
ext.versionPatch = 0

static def getGitTimestamp() {
@ -29,15 +29,7 @@ static def generateVersionCodeFromTimestamp() {
}

def generateVersionCodeFromVersionName() {
return versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch
}

def getVersionCode() {
if (gitBranchName() == "develop") {
return generateVersionCodeFromTimestamp()
} else {
return generateVersionCodeFromVersionName()
}
return versionMajor * 10000 + versionMinor * 100 + versionPatch
}

static def gitRevision() {
@ -51,18 +43,10 @@ static def gitRevisionDate() {
}

static def gitBranchName() {
def cmd = "git rev-parse --abbrev-ref HEAD"
def cmd = "git name-rev --name-only HEAD"
return cmd.execute().text.trim()
}

static def getVersionSuffix() {
if (gitBranchName() == "master") {
return ""
} else {
return "-dev"
}
}

project.android.buildTypes.all { buildType ->
buildType.javaCompileOptions.annotationProcessorOptions.arguments =
[
@ -87,11 +71,11 @@ android {
targetSdkVersion 28
multiDexEnabled true

// `develop` branch will have version code from timestamp, to ensure each build from CI has a incremented versionCode.
// Other branches (master, features, etc.) will have version code based on application version.
versionCode project.getVersionCode()
// For release, use generateVersionCodeFromVersionName()
versionCode generateVersionCodeFromTimestamp()
//versionCode generateVersionCodeFromVersionName()

versionName "${versionMajor}.${versionMinor}.${versionPatch}${getVersionSuffix()}"
versionName "${versionMajor}.${versionMinor}.${versionPatch}-dev"

buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
resValue "string", "git_revision", "\"${gitRevision()}\""
@ -133,10 +117,9 @@ android {
}
}

applicationVariants.all { variant ->
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def baseAbiVersionCode = project.ext.abiVersionCodes.get(output.getFilter(OutputFile.ABI))
// Known limitation: it does not modify the value in the BuildConfig.java generated file
output.versionCodeOverride = baseAbiVersionCode * 10_000_000 + variant.versionCode
}
}
@ -318,8 +301,6 @@ dependencies {

implementation 'diff_match_patch:diff_match_patch:current'

implementation "androidx.emoji:emoji-appcompat:1.0.0"

// TESTS
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'

View File

@ -15,6 +15,7 @@
*/
package im.vector.riotx.fdroid.features.settings.troubleshoot

import androidx.appcompat.app.AppCompatActivity
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.settings.VectorPreferences
@ -24,12 +25,12 @@ import javax.inject.Inject
/**
* Test that the application is started on boot
*/
class TestAutoStartBoot @Inject constructor(private val vectorPreferences: VectorPreferences,
class TestAutoStartBoot @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {

override fun perform() {
if (vectorPreferences.autoStartOnBoot()) {
if (VectorPreferences.autoStartOnBoot(context)) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
status = TestStatus.SUCCESS
quickFix = null
@ -37,7 +38,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
override fun doFix() {
vectorPreferences.setAutoStartOnBoot(true)
VectorPreferences.setAutoStartOnBoot(context, true)
manager?.retry()
}
}

View File

@ -63,9 +63,9 @@ object FcmHelper {
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}

fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
//We need to use alarm in this mode
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
if (VectorPreferences.areNotificationEnabledForDevice(context) && activeSessionHolder.hasActiveSession()) {
val currentSession = activeSessionHolder.getActiveSession()
AlarmSyncBroadcastReceiver.scheduleAlarm(context, currentSession.myUserId, 4_000L)
Timber.i("Alarm scheduled to restart service")

View File

@ -52,7 +52,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
private lateinit var notifiableEventResolver: NotifiableEventResolver
private lateinit var pusherManager: PushersManager
private lateinit var activeSessionHolder: ActiveSessionHolder
private lateinit var vectorPreferences: VectorPreferences

// UI handler
private val mUIHandler by lazy {
@ -65,7 +64,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
notifiableEventResolver = vectorComponent().notifiableEventResolver()
pusherManager = vectorComponent().pusherManager()
activeSessionHolder = vectorComponent().activeSessionHolder()
vectorPreferences = vectorComponent().vectorPreferences()
}

/**
@ -74,7 +72,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* @param message the message
*/
override fun onMessageReceived(message: RemoteMessage?) {
if (!vectorPreferences.areNotificationEnabledForDevice()) {
if (!VectorPreferences.areNotificationEnabledForDevice(applicationContext)) {
Timber.i("Notification are disabled for this device")
return
}
@ -109,7 +107,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
if (refreshedToken == null) {
Timber.w("onNewToken:received null token")
} else {
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
if (VectorPreferences.areNotificationEnabledForDevice(applicationContext) && activeSessionHolder.hasActiveSession()) {
pusherManager.registerPusherWithFcmKey(refreshedToken)
}
}

View File

@ -27,7 +27,6 @@ import com.google.firebase.iid.FirebaseInstanceId
import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.settings.VectorPreferences
import timber.log.Timber

/**
@ -106,7 +105,7 @@ object FcmHelper {
// No op
}

fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
// TODO FCM fallback
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.riotx

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import androidx.core.provider.FontRequest
import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.FontRequestEmojiCompatConfig
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class EmojiCompatWrapper @Inject constructor(private val context: Context) {

private var initialized = false

fun init(fontRequest: FontRequest) {

//Use emoji compat for the benefit of emoji spans
val config = FontRequestEmojiCompatConfig(context, fontRequest)
// we want to replace all emojis with selected font
.setReplaceAll(true)
//Debug options
// .setEmojiSpanIndicatorEnabled(true)
// .setEmojiSpanIndicatorColor(Color.GREEN)
EmojiCompat.init(config)
.registerInitCallback(object : EmojiCompat.InitCallback() {
override fun onInitialized() {
Timber.v("Emoji compat onInitialized success ")
initialized = true
}

override fun onFailed(throwable: Throwable?) {
Timber.e(throwable, "Failed to init EmojiCompat")
}
})
}

fun safeEmojiSpanify(sequence: CharSequence): CharSequence {
if (initialized) {
try {
return EmojiCompat.get().process(sequence)
} catch (throwable: Throwable) {
//Defensive coding against error (should not happend as it is initialized)
Timber.e(throwable, "Failed to init EmojiCompat")
return sequence
}
} else {
return sequence
}
}
}

View File

@ -42,7 +42,6 @@ import im.vector.riotx.core.di.DaggerVectorComponent
import im.vector.riotx.core.di.HasVectorInjector
import im.vector.riotx.core.di.VectorComponent
import im.vector.riotx.core.extensions.configureAndStart
import im.vector.riotx.core.utils.initKnownEmojiHashSet
import im.vector.riotx.features.configuration.VectorConfiguration
import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks
import im.vector.riotx.features.notifications.NotificationDrawerManager
@ -50,12 +49,12 @@ import im.vector.riotx.features.notifications.NotificationUtils
import im.vector.riotx.features.notifications.PushRuleTriggerListener
import im.vector.riotx.features.rageshake.VectorFileLogger
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.version.VersionProvider
import im.vector.riotx.features.version.getVersion
import im.vector.riotx.push.fcm.FcmHelper
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
import im.vector.riotx.core.utils.initKnownEmojiHashSet
import javax.inject.Inject

class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider {
@ -66,13 +65,10 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
@Inject lateinit var authenticator: Authenticator
@Inject lateinit var vectorConfiguration: VectorConfiguration
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var pushRuleTriggerListener: PushRuleTriggerListener
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var versionProvider: VersionProvider
lateinit var vectorComponent: VectorComponent
private var fontThreadHandler: Handler? = null

@ -86,12 +82,9 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
vectorComponent = DaggerVectorComponent.factory().create(this)
vectorComponent.inject(this)
vectorUncaughtExceptionHandler.activate(this)

if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Timber.plant(vectorComponent.vectorFileLogger())

// Log
VectorFileLogger.init(this)
Timber.plant(Timber.DebugTree(), VectorFileLogger)
if (BuildConfig.DEBUG) {
Stetho.initializeWithDefaults(this)
}
@ -109,9 +102,6 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
)
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
vectorConfiguration.initConfiguration()

emojiCompatWrapper.init(fontRequest)

NotificationUtils.createNotificationChannels(applicationContext)
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
@ -132,7 +122,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
fun entersBackground() {
Timber.i("App entered background") // call persistInfo
notificationDrawerManager.persistInfo()
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
FcmHelper.onEnterBackground(appContext, activeSessionHolder)
}
})
//This should be done as early as possible
@ -148,7 +138,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
}

private fun logInfo() {
val appVersion = versionProvider.getVersion(longFormat = true, useBuildNumber = true)
val appVersion = getVersion(longFormat = true, useBuildNumber = true)
val sdkVersion = Matrix.getSdkVersion()
val date = SimpleDateFormat("MM-dd HH:mm:ss.SSSZ", Locale.US).format(Date())


View File

@ -1,24 +0,0 @@
/*
* 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.riotx.core.di

import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Module

@AssistedModule
@Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule

View File

@ -41,12 +41,7 @@ import im.vector.riotx.features.home.createdirect.CreateDirectRoomDirectoryUsers
import im.vector.riotx.features.home.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.home.group.GroupListFragment
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.MessageMenuFragment
import im.vector.riotx.features.home.room.detail.timeline.action.QuickReactionFragment
import im.vector.riotx.features.home.room.detail.timeline.action.ViewEditHistoryBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.ViewReactionBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.*
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.invite.VectorInviteView
@ -58,7 +53,6 @@ import im.vector.riotx.features.rageshake.BugReportActivity
import im.vector.riotx.features.rageshake.BugReporter
import im.vector.riotx.features.rageshake.RageShake
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.reactions.widget.ReactionButton
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
@ -66,15 +60,12 @@ import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotx.features.settings.VectorSettingsActivity
import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment
import im.vector.riotx.features.settings.VectorSettingsNotificationsTroubleshootFragment
import im.vector.riotx.features.settings.VectorSettingsPreferencesFragment
import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
import im.vector.riotx.features.settings.push.PushGatewaysFragment

@Component(dependencies = [VectorComponent::class], modules = [AssistedInjectModule::class, ViewModelModule::class, HomeModule::class])
@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
@ScreenScope
interface ScreenComponent {

@ -162,12 +153,6 @@ interface ScreenComponent {

fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)

fun inject(vectorSettingsAdvancedNotificationPreferenceFragment: VectorSettingsAdvancedNotificationPreferenceFragment)

fun inject(vectorSettingsSecurityPrivacyFragment: VectorSettingsSecurityPrivacyFragment)

fun inject(vectorSettingsHelpAboutFragment: VectorSettingsHelpAboutFragment)

fun inject(userAvatarPreference: UserAvatarPreference)

fun inject(vectorSettingsNotificationsTroubleshootFragment: VectorSettingsNotificationsTroubleshootFragment)
@ -180,10 +165,6 @@ interface ScreenComponent {

fun inject(createDirectRoomActivity: CreateDirectRoomActivity)

fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet)

fun inject(reactionButton: ReactionButton)

@Component.Factory
interface Factory {
fun create(vectorComponent: VectorComponent,

View File

@ -24,7 +24,6 @@ import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.VectorApplication
import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.configuration.VectorConfiguration
@ -41,9 +40,7 @@ import im.vector.riotx.features.notifications.NotificationBroadcastReceiver
import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.notifications.PushRuleTriggerListener
import im.vector.riotx.features.rageshake.BugReporter
import im.vector.riotx.features.rageshake.VectorFileLogger
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.settings.VectorPreferences
import javax.inject.Singleton

@Component(modules = [VectorModule::class])
@ -72,8 +69,6 @@ interface VectorComponent {

fun emojiCompatFontProvider(): EmojiCompatFontProvider

fun emojiCompatWrapper() : EmojiCompatWrapper

fun eventHtmlRenderer(): EventHtmlRenderer

fun navigator(): Navigator
@ -100,10 +95,6 @@ interface VectorComponent {

fun notifiableEventResolver(): NotifiableEventResolver

fun vectorPreferences(): VectorPreferences

fun vectorFileLogger(): VectorFileLogger

@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): VectorComponent

View File

@ -25,17 +25,41 @@ import im.vector.riotx.core.platform.ConfigurationViewModel
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyViewModel
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel_AssistedFactory
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
import im.vector.riotx.features.home.HomeNavigationViewModel
import im.vector.riotx.features.home.*
import im.vector.riotx.features.home.createdirect.CreateDirectRoomNavigationViewModel
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel
import im.vector.riotx.features.home.createdirect.CreateDirectRoomViewModel_AssistedFactory
import im.vector.riotx.features.home.group.GroupListViewModel
import im.vector.riotx.features.home.group.GroupListViewModel_AssistedFactory
import im.vector.riotx.features.home.room.detail.RoomDetailViewModel
import im.vector.riotx.features.home.room.detail.RoomDetailViewModel_AssistedFactory
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewModel
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewModel_AssistedFactory
import im.vector.riotx.features.home.room.detail.timeline.action.*
import im.vector.riotx.features.home.room.list.RoomListViewModel
import im.vector.riotx.features.home.room.list.RoomListViewModel_AssistedFactory
import im.vector.riotx.features.reactions.EmojiChooserViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryNavigationViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryViewModel_AssistedFactory
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomViewModel
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomViewModel_AssistedFactory
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerViewModel
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerViewModel_AssistedFactory
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewViewModel
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewViewModel_AssistedFactory
import im.vector.riotx.features.settings.push.PushGatewaysViewModel
import im.vector.riotx.features.settings.push.PushGatewaysViewModel_AssistedFactory
import im.vector.riotx.features.workers.signout.SignOutViewModel

@Module
interface ViewModelModule {


/**
* ViewModels with @IntoMap will be injected by this factory
*/
@ -45,7 +69,6 @@ interface ViewModelModule {
/**
* Below are bindings for the androidx view models (which extend ViewModel). Will be converted to MvRx ViewModel in the future.
*/

@Binds
@IntoMap
@ViewModelKey(SignOutViewModel::class)
@ -101,4 +124,62 @@ interface ViewModelModule {
@ViewModelKey(CreateDirectRoomNavigationViewModel::class)
fun bindCreateDirectRoomNavigationViewModel(viewModel: CreateDirectRoomNavigationViewModel): ViewModel

/**
* Below are bindings for the MvRx view models (which extend VectorViewModel). Will be the only usage in the future.
*/

@Binds
fun bindHomeActivityViewModelFactory(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory

@Binds
fun bindTextComposerViewModelFactory(factory: TextComposerViewModel_AssistedFactory): TextComposerViewModel.Factory

@Binds
fun bindRoomDetailViewModelFactory(factory: RoomDetailViewModel_AssistedFactory): RoomDetailViewModel.Factory

@Binds
fun bindQuickReactionViewModelFactory(factory: QuickReactionViewModel_AssistedFactory): QuickReactionViewModel.Factory

@Binds
fun bindMessageActionsViewModelFactory(factory: MessageActionsViewModel_AssistedFactory): MessageActionsViewModel.Factory

@Binds
fun bindMessageMenuViewModelFactory(factory: MessageMenuViewModel_AssistedFactory): MessageMenuViewModel.Factory

@Binds
fun bindRoomListViewModelFactory(factory: RoomListViewModel_AssistedFactory): RoomListViewModel.Factory

@Binds
fun bindGroupListViewModelFactory(factory: GroupListViewModel_AssistedFactory): GroupListViewModel.Factory

@Binds
fun bindHomeDetailViewModelFactory(factory: HomeDetailViewModel_AssistedFactory): HomeDetailViewModel.Factory

@Binds
fun bindKeysBackupSettingsViewModelFactory(factory: KeysBackupSettingsViewModel_AssistedFactory): KeysBackupSettingsViewModel.Factory

@Binds
fun bindRoomDirectoryPickerViewModelFactory(factory: RoomDirectoryPickerViewModel_AssistedFactory): RoomDirectoryPickerViewModel.Factory

@Binds
fun bindRoomDirectoryViewModelFactory(factory: RoomDirectoryViewModel_AssistedFactory): RoomDirectoryViewModel.Factory

@Binds
fun bindRoomPreviewViewModelFactory(factory: RoomPreviewViewModel_AssistedFactory): RoomPreviewViewModel.Factory

@Binds
fun bindViewReactionViewModelFactory(factory: ViewReactionViewModel_AssistedFactory): ViewReactionViewModel.Factory

@Binds
fun bindViewEditHistoryViewModelFactory(factory: ViewEditHistoryViewModel_AssistedFactory): ViewEditHistoryViewModel.Factory

@Binds
fun bindCreateRoomViewModelFactory(factory: CreateRoomViewModel_AssistedFactory): CreateRoomViewModel.Factory

@Binds
fun bindCreateDirectRoomViewModelFactory(factory: CreateDirectRoomViewModel_AssistedFactory): CreateDirectRoomViewModel.Factory

@Binds
fun bindPushGatewaysViewModelFactory(factory: PushGatewaysViewModel_AssistedFactory): PushGatewaysViewModel.Factory

}

View File

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

package im.vector.riotx.core.ui.views
package im.vector.riotx.core.platform

import android.content.Context
import android.graphics.Color
@ -32,7 +32,10 @@ import androidx.core.content.ContextCompat
import butterknife.BindView
import butterknife.ButterKnife
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.permalinks.PermalinkFactory
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
import im.vector.riotx.R
import im.vector.riotx.core.error.ResourceLimitErrorFormatter
import im.vector.riotx.features.themes.ThemeUtils

View File

@ -161,6 +161,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {

override fun onDestroy() {
super.onDestroy()

unBinder?.unbind()
unBinder = null


View File

@ -16,17 +16,13 @@

package im.vector.riotx.core.resources

import android.content.Context
import im.vector.riotx.features.settings.VectorPreferences
import javax.inject.Inject

class UserPreferencesProvider @Inject constructor(private val vectorPreferences: VectorPreferences) {
class UserPreferencesProvider @Inject constructor(private val context: Context) {

fun shouldShowHiddenEvents(): Boolean {
return vectorPreferences.shouldShowHiddenEvents()
return VectorPreferences.shouldShowHiddenEvents(context)
}

fun shouldShowReadReceipts(): Boolean {
return vectorPreferences.showReadReceipts()
}

}

View File

@ -1,40 +0,0 @@
/*
* 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.riotx.core.resources

import android.content.Context
import android.os.Build
import androidx.annotation.NonNull
import javax.inject.Inject

class VersionCodeProvider @Inject constructor(private val context: Context) {

/**
* Returns the version code, read from the Manifest. It is not the same than BuildConfig.VERSION_CODE due to versionCodeOverride
*/
@NonNull
fun getVersionCode(): Long {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)

return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode
} else {
@Suppress("DEPRECATION")
packageInfo.versionCode.toLong()
}
}
}

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