forked from GitHub-Mirror/riotX-android
Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
Benoit Marty | a89f0ddd1d | |
Benoit Marty | 9cd69d1e33 | |
Benoit Marty | df6080b1da |
23
CHANGES.md
23
CHANGES.md
|
@ -1,26 +1,3 @@
|
|||
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)
|
||||
===================================================
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.matrix.android.api.comparators
|
||||
|
||||
import im.vector.matrix.android.api.interfaces.DatedObject
|
||||
import java.util.*
|
||||
|
||||
object DatedObjectComparators {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 + ")"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ 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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -97,12 +97,12 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
handleJoinedRoom(realm, it.key, it.value, isInitialSync)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,8 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||
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 +135,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)
|
||||
}
|
||||
|
@ -165,7 +167,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 +181,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 +214,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -15,7 +15,7 @@ androidExtensions {
|
|||
}
|
||||
|
||||
ext.versionMajor = 0
|
||||
ext.versionMinor = 5
|
||||
ext.versionMinor = 4
|
||||
ext.versionPatch = 0
|
||||
|
||||
static def getGitTimestamp() {
|
||||
|
@ -51,7 +51,7 @@ 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -86,12 +86,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)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ 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
|
||||
|
@ -102,8 +101,6 @@ interface VectorComponent {
|
|||
|
||||
fun vectorPreferences(): VectorPreferences
|
||||
|
||||
fun vectorFileLogger(): VectorFileLogger
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(@BindsInstance context: Context): VectorComponent
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.appcompat.widget.Toolbar
|
|||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.airbnb.mvrx.viewModel
|
||||
|
@ -35,10 +36,12 @@ import im.vector.riotx.core.di.ScreenComponent
|
|||
import im.vector.riotx.core.extensions.hideKeyboard
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.extensions.replaceFragment
|
||||
import im.vector.riotx.core.platform.OnBackPressed
|
||||
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.pushers.PushersManager
|
||||
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
||||
|
@ -116,22 +119,22 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
||||
}
|
||||
|
||||
activeSessionHolder.getSafeActiveSession()?.getInitialSyncProgressStatus()?.observe(this, Observer { status ->
|
||||
if (status == null) {
|
||||
activeSessionHolder.getSafeActiveSession()?.getLiveStatus()?.observe(this, Observer { sprogress ->
|
||||
Timber.e("${sprogress?.statusText?.let { getString(it) }} ${sprogress?.percentProgress}")
|
||||
if (sprogress == null) {
|
||||
waiting_view.isVisible = false
|
||||
} else {
|
||||
Timber.e("${getString(status.statusText)} ${status.percentProgress}")
|
||||
waiting_view.setOnClickListener {
|
||||
//block interactions
|
||||
}
|
||||
waiting_view_status_horizontal_progress.apply {
|
||||
isIndeterminate = false
|
||||
max = 100
|
||||
progress = status.percentProgress
|
||||
progress = sprogress.percentProgress
|
||||
isVisible = true
|
||||
}
|
||||
waiting_view_status_text.apply {
|
||||
text = getString(status.statusText)
|
||||
text = sprogress.statusText?.let { getString(it) }
|
||||
isVisible = true
|
||||
}
|
||||
waiting_view.isVisible = true
|
||||
|
@ -210,6 +213,8 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_CLEAR_EXISTING_NOTIFICATION = "EXTRA_CLEAR_EXISTING_NOTIFICATION"
|
||||
|
||||
|
|
|
@ -28,7 +28,15 @@ import im.vector.matrix.android.api.permalinks.MatrixLinkify
|
|||
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
|
||||
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||
|
@ -39,12 +47,26 @@ import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
|||
import im.vector.riotx.core.linkify.VectorLinkify
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.resources.UserPreferencesProvider
|
||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.*
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.senderAvatar
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.BlankItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem_
|
||||
import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
|
@ -81,28 +103,32 @@ class MessageItemFactory @Inject constructor(
|
|||
|
||||
val messageContent: MessageContent =
|
||||
event.getLastMessageContent()
|
||||
?: //Malformed content, we should echo something on screen
|
||||
return buildNotHandledMessageItem(stringProvider.getString(R.string.malformed_message),
|
||||
informationData, highlight, callback)
|
||||
?: //Malformed content, we should echo something on screen
|
||||
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
|
||||
|
||||
if (messageContent.relatesTo?.type == RelationType.REPLACE
|
||||
|| event.isEncrypted() && event.root.content.toModel<EncryptedEventContent>()?.relatesTo?.type == RelationType.REPLACE
|
||||
|| event.isEncrypted() && event.root.content.toModel<EncryptedEventContent>()?.relatesTo?.type == RelationType.REPLACE
|
||||
) {
|
||||
// This is an edit event, we should display it when debugging as a notice event
|
||||
// This is an edit event, we should it when debugging as a notice event
|
||||
return noticeItemFactory.create(event, highlight, callback)
|
||||
}
|
||||
// val all = event.root.toContent()
|
||||
// val ev = all.toModel<Event>()
|
||||
return when (messageContent) {
|
||||
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageTextContent -> buildTextMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
|
||||
informationData,
|
||||
highlight,
|
||||
callback)
|
||||
is MessageTextContent -> buildTextMessageItem(messageContent,
|
||||
informationData,
|
||||
highlight,
|
||||
callback)
|
||||
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageFileContent -> buildFileMessageItem(messageContent, informationData, highlight, callback)
|
||||
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, callback)
|
||||
else -> buildNotHandledMessageItem("${messageContent.type} message events are not yet handled",
|
||||
informationData, highlight, callback)
|
||||
else -> buildNotHandledMessageItem(messageContent, highlight)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +157,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +182,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
.clickListener(
|
||||
DebouncedClickListener(View.OnClickListener { _ ->
|
||||
|
@ -164,17 +190,11 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
}
|
||||
|
||||
private fun buildNotHandledMessageItem(text: String,
|
||||
informationData: MessageInformationData,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): DefaultItem? {
|
||||
private fun buildNotHandledMessageItem(messageContent: MessageContent, highlight: Boolean): DefaultItem? {
|
||||
val text = "${messageContent.type} message events are not yet handled"
|
||||
return DefaultItem_()
|
||||
.text(text)
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.highlighted(highlight)
|
||||
.informationData(informationData)
|
||||
.baseCallback(callback)
|
||||
.readReceiptsCallback(callback)
|
||||
}
|
||||
|
||||
private fun buildImageMessageItem(messageContent: MessageImageContent,
|
||||
|
@ -217,7 +237,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +250,7 @@ class MessageItemFactory @Inject constructor(
|
|||
val thumbnailData = ImageContentRenderer.Data(
|
||||
filename = messageContent.body,
|
||||
url = messageContent.videoInfo?.thumbnailFile?.url
|
||||
?: messageContent.videoInfo?.thumbnailUrl,
|
||||
?: messageContent.videoInfo?.thumbnailUrl,
|
||||
elementToDecrypt = messageContent.videoInfo?.thumbnailFile?.toElementToDecrypt(),
|
||||
height = messageContent.videoInfo?.height,
|
||||
maxHeight = maxHeight,
|
||||
|
@ -266,7 +286,7 @@ class MessageItemFactory @Inject constructor(
|
|||
.clickListener { view -> callback?.onVideoMessageClicked(messageContent, videoData, view) }
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,7 +326,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,9 +356,9 @@ class MessageItemFactory @Inject constructor(
|
|||
//nop
|
||||
}
|
||||
},
|
||||
editStart,
|
||||
editEnd,
|
||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
editStart,
|
||||
editEnd,
|
||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
|
||||
|
@ -376,7 +396,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +433,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,7 +453,7 @@ class MessageItemFactory @Inject constructor(
|
|||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, null, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.riotx.features.home.room.detail.timeline.format
|
||||
|
||||
import android.text.TextUtils
|
||||
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
|
||||
|
@ -23,14 +24,12 @@ import im.vector.matrix.android.api.session.room.model.*
|
|||
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.senderName
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class NoticeEventFormatter @Inject constructor(private val sessionHolder: ActiveSessionHolder,
|
||||
private val stringProvider: StringProvider) {
|
||||
class NoticeEventFormatter @Inject constructor(private val stringProvider: StringProvider) {
|
||||
|
||||
fun format(timelineEvent: TimelineEvent): CharSequence? {
|
||||
return when (val type = timelineEvent.root.getClearType()) {
|
||||
|
@ -75,10 +74,10 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
|
||||
private fun formatRoomNameEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val content = event.getClearContent().toModel<RoomNameContent>() ?: return null
|
||||
return if (content.name.isNullOrBlank()) {
|
||||
stringProvider.getString(R.string.notice_room_name_removed, senderName)
|
||||
} else {
|
||||
return if (!TextUtils.isEmpty(content.name)) {
|
||||
stringProvider.getString(R.string.notice_room_name_changed, senderName, content.name)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_name_removed, senderName)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,8 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
}
|
||||
|
||||
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility ?: return null
|
||||
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
||||
?: return null
|
||||
|
||||
val formattedVisibility = when (historyVisibility) {
|
||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||
|
@ -138,7 +138,7 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val displayText = StringBuilder()
|
||||
// Check display name has been changed
|
||||
if (eventContent?.displayName != prevEventContent?.displayName) {
|
||||
if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) {
|
||||
val displayNameText = when {
|
||||
prevEventContent?.displayName.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_display_name_set, event.senderId, eventContent?.displayName)
|
||||
|
@ -146,12 +146,12 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
stringProvider.getString(R.string.notice_display_name_removed, event.senderId, prevEventContent?.displayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_display_name_changed_from,
|
||||
event.senderId, prevEventContent?.displayName, eventContent?.displayName)
|
||||
event.senderId, prevEventContent?.displayName, eventContent?.displayName)
|
||||
}
|
||||
displayText.append(displayNameText)
|
||||
}
|
||||
// Check whether the avatar has been changed
|
||||
if (eventContent?.avatarUrl != prevEventContent?.avatarUrl) {
|
||||
if (!TextUtils.equals(eventContent?.avatarUrl, prevEventContent?.avatarUrl)) {
|
||||
val displayAvatarText = if (displayText.isNotEmpty()) {
|
||||
displayText.append(" ")
|
||||
stringProvider.getString(R.string.notice_avatar_changed_too)
|
||||
|
@ -168,18 +168,17 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
val targetDisplayName = eventContent?.displayName ?: prevEventContent?.displayName ?: ""
|
||||
return when {
|
||||
Membership.INVITE == eventContent?.membership -> {
|
||||
val selfUserId = sessionHolder.getSafeActiveSession()?.myUserId
|
||||
// TODO get userId
|
||||
val selfUserId = ""
|
||||
when {
|
||||
eventContent.thirdPartyInvite != null -> {
|
||||
val userWhoHasAccepted = eventContent.thirdPartyInvite?.signed?.mxid ?: event.stateKey
|
||||
eventContent.thirdPartyInvite != null ->
|
||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||
userWhoHasAccepted, eventContent.thirdPartyInvite?.displayName)
|
||||
}
|
||||
event.stateKey == selfUserId ->
|
||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
||||
TextUtils.equals(event.stateKey, selfUserId) ->
|
||||
stringProvider.getString(R.string.notice_room_invite_you, senderDisplayName)
|
||||
event.stateKey.isNullOrEmpty() ->
|
||||
event.stateKey.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_room_invite_no_invitee, senderDisplayName)
|
||||
else ->
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_room_invite, senderDisplayName, targetDisplayName)
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +186,7 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
|||
stringProvider.getString(R.string.notice_room_join, senderDisplayName)
|
||||
Membership.LEAVE == eventContent?.membership ->
|
||||
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
|
||||
return if (event.senderId == event.stateKey) {
|
||||
return if (TextUtils.equals(event.senderId, event.stateKey)) {
|
||||
if (prevEventContent?.membership == Membership.INVITE) {
|
||||
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
||||
} else {
|
||||
|
|
|
@ -52,8 +52,7 @@ import javax.inject.Singleton
|
|||
*/
|
||||
@Singleton
|
||||
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val versionProvider: VersionProvider,
|
||||
private val vectorFileLogger : VectorFileLogger) {
|
||||
private val versionProvider: VersionProvider) {
|
||||
var inMultiWindowMode = false
|
||||
|
||||
companion object {
|
||||
|
@ -163,7 +162,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||
val gzippedFiles = ArrayList<File>()
|
||||
|
||||
if (withDevicesLogs) {
|
||||
val files = vectorFileLogger.getLogFiles()
|
||||
val files = VectorFileLogger.getLogFiles()
|
||||
|
||||
for (f in files) {
|
||||
if (!mIsCancelled) {
|
||||
|
@ -349,20 +348,20 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||
} else if (null == response || null == response.body()) {
|
||||
serverError = "Failed with error $responseCode"
|
||||
} else {
|
||||
var inputStream: InputStream? = null
|
||||
var `is`: InputStream? = null
|
||||
|
||||
try {
|
||||
inputStream = response.body()!!.byteStream()
|
||||
`is` = response.body()!!.byteStream()
|
||||
|
||||
if (null != inputStream) {
|
||||
var ch = inputStream.read()
|
||||
if (null != `is`) {
|
||||
var ch = `is`.read()
|
||||
val b = StringBuilder()
|
||||
while (ch != -1) {
|
||||
b.append(ch.toChar())
|
||||
ch = inputStream.read()
|
||||
ch = `is`.read()
|
||||
}
|
||||
serverError = b.toString()
|
||||
inputStream.close()
|
||||
`is`.close()
|
||||
|
||||
// check if the error message
|
||||
try {
|
||||
|
@ -381,7 +380,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||
Timber.e(e, "## sendBugReport() : failed to parse error " + e.message)
|
||||
} finally {
|
||||
try {
|
||||
inputStream?.close()
|
||||
`is`?.close()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## sendBugReport() : failed to close the error stream " + e.message)
|
||||
}
|
||||
|
|
|
@ -17,74 +17,43 @@
|
|||
package im.vector.riotx.features.rageshake
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import android.text.TextUtils
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.logging.*
|
||||
import java.util.logging.Formatter
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
private const val LOG_SIZE_BYTES = 20 * 1024 * 1024 // 20MB
|
||||
object VectorFileLogger : Timber.DebugTree() {
|
||||
|
||||
private const val LOG_ROTATION_COUNT = 3
|
||||
|
||||
|
||||
@Singleton
|
||||
class VectorFileLogger @Inject constructor(val context: Context, private val vectorPreferences: VectorPreferences) : Timber.DebugTree() {
|
||||
private const val LOG_SIZE_BYTES = 50 * 1024 * 1024 // 50MB
|
||||
|
||||
// relatively large rotation count because closing > opening the app rotates the log (!)
|
||||
private const val LOG_ROTATION_COUNT = 15
|
||||
|
||||
private val sLogger = Logger.getLogger("im.vector.riotx")
|
||||
private var sFileHandler: FileHandler? = null
|
||||
private var sCacheDirectory: File? = null
|
||||
private var sFileName = "riotxlogs"
|
||||
private lateinit var sFileHandler: FileHandler
|
||||
private lateinit var sCacheDirectory: File
|
||||
private var sFileName = "riotx"
|
||||
|
||||
private val prioPrefixes = mapOf(
|
||||
Log.VERBOSE to "V/ ",
|
||||
Log.DEBUG to "D/ ",
|
||||
Log.INFO to "I/ ",
|
||||
Log.WARN to "W/ ",
|
||||
Log.ERROR to "E/ ",
|
||||
Log.ASSERT to "WTF/ "
|
||||
)
|
||||
|
||||
init {
|
||||
fun init(context: Context) {
|
||||
val logsDirectoryFile = context.cacheDir.absolutePath + "/logs"
|
||||
|
||||
setLogDirectory(File(logsDirectoryFile))
|
||||
try {
|
||||
if (sCacheDirectory != null) {
|
||||
sFileHandler = FileHandler(sCacheDirectory!!.absolutePath + "/" + sFileName + ".%g.txt", LOG_SIZE_BYTES, LOG_ROTATION_COUNT)
|
||||
sFileHandler?.formatter = LogFormatter()
|
||||
sLogger.useParentHandlers = false
|
||||
sLogger.level = Level.ALL
|
||||
sLogger.addHandler(sFileHandler)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Failed to initialize FileLogger")
|
||||
}
|
||||
init("RiotXLog")
|
||||
}
|
||||
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
if (sFileHandler == null) return
|
||||
if (skipLog(priority)) return
|
||||
if (t != null) {
|
||||
logToFile(t)
|
||||
}
|
||||
logToFile(prioPrefixes[priority] ?: "$priority ", tag ?: "Tag", message)
|
||||
}
|
||||
|
||||
private fun skipLog(priority: Int): Boolean {
|
||||
return if (vectorPreferences.labAllowedExtendedLogging()) {
|
||||
false
|
||||
} else {
|
||||
priority < Log.ERROR
|
||||
}
|
||||
logToFile("$priority ", tag ?: "Tag", message)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,6 +68,24 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
|
|||
sCacheDirectory = cacheDir
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the logger. Should be called AFTER [Log.setLogDirectory].
|
||||
*
|
||||
* @param fileName the base file name
|
||||
*/
|
||||
private fun init(fileName: String) {
|
||||
try {
|
||||
if (!TextUtils.isEmpty(fileName)) {
|
||||
sFileName = fileName
|
||||
}
|
||||
sFileHandler = FileHandler(sCacheDirectory.absolutePath + "/" + sFileName + ".%g.txt", LOG_SIZE_BYTES, LOG_ROTATION_COUNT)
|
||||
sFileHandler.formatter = LogFormatter()
|
||||
sLogger.useParentHandlers = false
|
||||
sLogger.level = Level.ALL
|
||||
sLogger.addHandler(sFileHandler)
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds our own log files to the provided list of files.
|
||||
|
@ -112,8 +99,8 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
|
|||
try {
|
||||
// reported by GA
|
||||
if (null != sFileHandler) {
|
||||
sFileHandler!!.flush()
|
||||
val absPath = sCacheDirectory?.absolutePath ?: return emptyList()
|
||||
sFileHandler.flush()
|
||||
val absPath = sCacheDirectory.absolutePath
|
||||
|
||||
for (i in 0..LOG_ROTATION_COUNT) {
|
||||
val filepath = "$absPath/$sFileName.$i.txt"
|
||||
|
@ -124,7 +111,7 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
|
|||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## addLogFiles() failed : %s", e.message)
|
||||
Timber.e(e, "## addLogFiles() failed : " + e.message)
|
||||
}
|
||||
|
||||
return files
|
||||
|
|
|
@ -149,8 +149,6 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
private const val SETTINGS_USE_NATIVE_CAMERA_PREFERENCE_KEY = "SETTINGS_USE_NATIVE_CAMERA_PREFERENCE_KEY"
|
||||
private const val SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY = "SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY"
|
||||
|
||||
const val SETTINGS_LABS_ALLOW_EXTENDED_LOGS = "SETTINGS_LABS_ALLOW_EXTENDED_LOGS"
|
||||
|
||||
private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||
|
||||
|
@ -259,10 +257,6 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true)
|
||||
}
|
||||
|
||||
fun labAllowedExtendedLogging(): Boolean {
|
||||
return defaultPrefs.getBoolean(SETTINGS_LABS_ALLOW_EXTENDED_LOGS, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we have already asked the user to disable battery optimisations on android >= M devices.
|
||||
*
|
||||
|
|
|
@ -2,7 +2,5 @@
|
|||
<resources>
|
||||
|
||||
<!-- Strings not defined in Riot -->
|
||||
<string name="labs_allow_extended_logging">Enable verbose logs.</string>
|
||||
<string name="labs_allow_extended_logging_summary">Verbose logs will help developers by providing more logs when you send a RageShake. Even when enabled, the application does not log message contents or any other private data.</string>
|
||||
|
||||
</resources>
|
|
@ -45,13 +45,6 @@
|
|||
android:key="SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||
android:title="@string/labs_swipe_to_reply_in_timeline" />
|
||||
|
||||
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="SETTINGS_LABS_ALLOW_EXTENDED_LOGS"
|
||||
android:summary="@string/labs_allow_extended_logging_summary"
|
||||
android:title="@string/labs_allow_extended_logging" />
|
||||
|
||||
<!--</im.vector.riotx.core.preference.VectorPreferenceCategory>-->
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue