forked from GitHub-Mirror/riotX-android
Compare commits
55 Commits
feature/re
...
develop
Author | SHA1 | Date | |
---|---|---|---|
|
f2c8d4ad02 | ||
|
be524472ec | ||
|
1b82a1a24d | ||
|
cf0b331c3b | ||
|
2a92a3dc80 | ||
|
012840abba | ||
|
a5975a099e | ||
|
38da4b9ee5 | ||
|
242e60fcaa | ||
|
a23be05cbf | ||
|
ed39b02924 | ||
|
fe931b5361 | ||
|
90d9cd0587 | ||
|
9cedb18921 | ||
|
e89ba7b87b | ||
|
902657c22a | ||
|
eec2abf164 | ||
|
6879cc8ca8 | ||
|
fd6bbbd3b5 | ||
|
0ff0b014a9 | ||
|
fdc9e84dd5 | ||
|
58f878fca9 | ||
|
88095e4bd9 | ||
|
47d22a3d5e | ||
|
28e82cb8ea | ||
|
35817245cb | ||
|
75266f42bb | ||
|
95c4c9ce56 | ||
|
ce5570105d | ||
|
188a9aebfa | ||
|
c95223f5d2 | ||
|
ef0362ba9c | ||
|
ea242f6737 | ||
|
cbc08d834b | ||
|
0ab6b33fb6 | ||
|
1b394527b6 | ||
|
a8f1388721 | ||
|
166be4e289 | ||
|
b49ccefe63 | ||
|
825760d17e | ||
|
b5af62c3ea | ||
|
a51d96bf00 | ||
|
7e142d201d | ||
|
2be6058971 | ||
|
49d73f360e | ||
|
bd88d85a21 | ||
|
be4fc5cce6 | ||
|
704da1be55 | ||
|
5d002532d3 | ||
|
d4161e9a1a | ||
|
7966ebef03 | ||
|
ed5faca5d2 | ||
|
456908c851 | ||
|
215324a03e | ||
|
02e342849f |
42
CHANGES.md
42
CHANGES.md
@ -1,3 +1,26 @@
|
|||||||
|
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)
|
Changes in RiotX 0.4.0 (2019-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
@ -5,19 +28,16 @@ Features:
|
|||||||
- Display read receipts in timeline (#81)
|
- Display read receipts in timeline (#81)
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
-
|
- Reactions: Reinstate the ability to react with non-unicode keys (#307)
|
||||||
|
|
||||||
Other changes:
|
|
||||||
-
|
|
||||||
|
|
||||||
Bugfix:
|
Bugfix:
|
||||||
-
|
- Fix text diff linebreak display (#441)
|
||||||
|
- Date change message repeats for each redaction until a normal message (#358)
|
||||||
Translations:
|
- Slide-in reply icon is distorted (#423)
|
||||||
-
|
- Regression / e2e replies not encrypted
|
||||||
|
- Some video won't play
|
||||||
Build:
|
- 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-08-08)
|
||||||
===================================================
|
===================================================
|
||||||
|
@ -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.RealmCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import java.util.*
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class CryptoStoreHelper {
|
internal class CryptoStoreHelper {
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ internal class CryptoStoreHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createCredential() = Credentials(
|
fun createCredential() = Credentials(
|
||||||
userId = "userId_" + Random().nextInt(),
|
userId = "userId_" + Random.nextInt(),
|
||||||
homeServer = "http://matrix.org",
|
homeServer = "http://matrix.org",
|
||||||
accessToken = "access_token",
|
accessToken = "access_token",
|
||||||
refreshToken = null,
|
refreshToken = null,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package im.vector.matrix.android.api.comparators
|
package im.vector.matrix.android.api.comparators
|
||||||
|
|
||||||
import im.vector.matrix.android.api.interfaces.DatedObject
|
import im.vector.matrix.android.api.interfaces.DatedObject
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object DatedObjectComparators {
|
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.api.comparators.DatedObjectComparators
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
import java.util.*
|
import java.util.Collections
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* MXDeviceInfo
|
* MXDeviceInfo
|
||||||
|
@ -18,18 +18,17 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")
|
private val regex = Regex("^(==|<=|>=|<|>)?(\\d*)$")
|
||||||
|
|
||||||
class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {
|
class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_count) {
|
||||||
|
|
||||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||||
return conditionResolver.resolveRoomMemberCountCondition(this)
|
return conditionResolver.resolveRoomMemberCountCondition(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun technicalDescription(): String {
|
override fun technicalDescription(): String {
|
||||||
return "Room member count is $`is`"
|
return "Room member count is $iz"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
||||||
@ -56,12 +55,9 @@ class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_co
|
|||||||
*/
|
*/
|
||||||
private fun parseIsField(): Pair<String?, Int>? {
|
private fun parseIsField(): Pair<String?, Int>? {
|
||||||
try {
|
try {
|
||||||
val match = regex.matcher(`is`)
|
val match = regex.find(iz) ?: return null
|
||||||
if (match.find()) {
|
val (prefix, count) = match.destructured
|
||||||
val prefix = match.group(1)
|
return prefix to count.toInt()
|
||||||
val count = match.group(2).toInt()
|
|
||||||
return prefix to count
|
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Timber.d(t)
|
Timber.d(t)
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ import androidx.lifecycle.LiveData
|
|||||||
|
|
||||||
interface InitialSyncProgressService {
|
interface InitialSyncProgressService {
|
||||||
|
|
||||||
fun getLiveStatus() : LiveData<Status?>
|
fun getInitialSyncProgressStatus() : LiveData<Status?>
|
||||||
|
|
||||||
data class Status(
|
data class Status(
|
||||||
@StringRes val statusText: Int?,
|
@StringRes val statusText: Int,
|
||||||
val percentProgress: Int = 0
|
val percentProgress: Int = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.pushers
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
interface PushersService {
|
interface PushersService {
|
||||||
|
@ -29,7 +29,6 @@ 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.RoomDirectoryVisibility
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
||||||
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameter to create a room, with facilities functions to configure it
|
* Parameter to create a room, with facilities functions to configure it
|
||||||
@ -133,7 +132,7 @@ class CreateRoomParams {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (null == initialStates) {
|
if (null == initialStates) {
|
||||||
initialStates = Arrays.asList<Event>(algoEvent)
|
initialStates = mutableListOf(algoEvent)
|
||||||
} else {
|
} else {
|
||||||
initialStates!!.add(algoEvent)
|
initialStates!!.add(algoEvent)
|
||||||
}
|
}
|
||||||
@ -166,7 +165,7 @@ class CreateRoomParams {
|
|||||||
content = contentMap.toContent())
|
content = contentMap.toContent())
|
||||||
|
|
||||||
if (null == initialStates) {
|
if (null == initialStates) {
|
||||||
initialStates = Arrays.asList<Event>(historyVisibilityEvent)
|
initialStates = mutableListOf(historyVisibilityEvent)
|
||||||
} else {
|
} else {
|
||||||
initialStates!!.add(historyVisibilityEvent)
|
initialStates!!.add(historyVisibilityEvent)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import java.math.BigInteger
|
|||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
import java.util.Calendar
|
||||||
import javax.crypto.*
|
import javax.crypto.*
|
||||||
import javax.crypto.spec.GCMParameterSpec
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
@ -479,12 +479,7 @@ object SecretStoringUtils {
|
|||||||
val output = Cipher.getInstance(RSA_MODE)
|
val output = Cipher.getInstance(RSA_MODE)
|
||||||
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)
|
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)
|
||||||
|
|
||||||
val bos = ByteArrayOutputStream()
|
return CipherInputStream(encrypted, output).use { it.readBytes() }
|
||||||
CipherInputStream(encrypted, output).use {
|
|
||||||
it.copyTo(bos)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bos.toByteArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatMExtract(bis: InputStream): Pair<ByteArray, ByteArray> {
|
private fun formatMExtract(bis: InputStream): Pair<ByteArray, ByteArray> {
|
||||||
@ -495,14 +490,7 @@ object SecretStoringUtils {
|
|||||||
val iv = ByteArray(ivSize)
|
val iv = ByteArray(ivSize)
|
||||||
bis.read(iv, 0, 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)
|
return Pair(iv, encrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,14 +518,7 @@ object SecretStoringUtils {
|
|||||||
val iv = ByteArray(ivSize)
|
val iv = ByteArray(ivSize)
|
||||||
bis.read(iv)
|
bis.read(iv)
|
||||||
|
|
||||||
val bos = ByteArrayOutputStream()
|
val encrypted = bis.readBytes()
|
||||||
|
|
||||||
var next = bis.read()
|
|
||||||
while (next != -1) {
|
|
||||||
bos.write(next)
|
|
||||||
next = bis.read()
|
|
||||||
}
|
|
||||||
val encrypted = bos.toByteArray()
|
|
||||||
return Triple(encryptedKey, iv, encrypted)
|
return Triple(encryptedKey, iv, encrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,14 +560,7 @@ object SecretStoringUtils {
|
|||||||
val iv = ByteArray(ivSize)
|
val iv = ByteArray(ivSize)
|
||||||
bis.read(iv)
|
bis.read(iv)
|
||||||
|
|
||||||
val bos = ByteArrayOutputStream()
|
val encrypted = bis.readBytes()
|
||||||
|
|
||||||
var next = bis.read()
|
|
||||||
while (next != -1) {
|
|
||||||
bos.write(next)
|
|
||||||
next = bis.read()
|
|
||||||
}
|
|
||||||
val encrypted = bos.toByteArray()
|
|
||||||
return Triple(salt, iv, encrypted)
|
return Triple(salt, iv, encrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -105,7 +105,7 @@ internal abstract class CryptoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCryptoService(cryptoManager: CryptoManager): CryptoService
|
abstract fun bindCryptoService(cryptoService: DefaultCryptoService): CryptoService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask
|
abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask
|
||||||
|
@ -93,7 +93,7 @@ import kotlin.math.max
|
|||||||
* Specially, it tracks all room membership changes events in order to do keys updates.
|
* Specially, it tracks all room membership changes events in order to do keys updates.
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class CryptoManager @Inject constructor(
|
internal class DefaultCryptoService @Inject constructor(
|
||||||
// Olm Manager
|
// Olm Manager
|
||||||
private val olmManager: OlmManager,
|
private val olmManager: OlmManager,
|
||||||
// The credentials,
|
// The credentials,
|
||||||
@ -1067,6 +1067,6 @@ internal class CryptoManager @Inject constructor(
|
|||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")"
|
return "DefaultCryptoService of " + credentials.userId + " (" + credentials.deviceId + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class ObjectSigner @Inject constructor(private val credentials: Credentials,
|
internal class ObjectSigner @Inject constructor(private val credentials: Credentials,
|
||||||
|
@ -31,7 +31,6 @@ import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent
|
|||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.util.convertFromUTF8
|
import im.vector.matrix.android.internal.util.convertFromUTF8
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class MXOlmDecryption(
|
internal class MXOlmDecryption(
|
||||||
// The olm device interface
|
// The olm device interface
|
||||||
@ -158,33 +157,14 @@ internal class MXOlmDecryption(
|
|||||||
* @return payload, if decrypted successfully.
|
* @return payload, if decrypted successfully.
|
||||||
*/
|
*/
|
||||||
private fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
|
private fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
|
||||||
val sessionIdsSet = olmDevice.getSessionIds(theirDeviceIdentityKey)
|
val sessionIds = olmDevice.getSessionIds(theirDeviceIdentityKey) ?: emptySet()
|
||||||
|
|
||||||
val sessionIds: List<String>
|
val messageBody = message["body"] as? String ?: return null
|
||||||
|
val messageType = when (val typeAsVoid = message["type"]) {
|
||||||
if (null == sessionIdsSet) {
|
is Double -> typeAsVoid.toInt()
|
||||||
sessionIds = ArrayList()
|
is Int -> typeAsVoid
|
||||||
} else {
|
is Long -> typeAsVoid.toInt()
|
||||||
sessionIds = ArrayList(sessionIdsSet)
|
else -> return null
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Try each session in turn
|
||||||
|
@ -26,7 +26,6 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
@ -59,8 +58,7 @@ object MXEncryptedAttachments {
|
|||||||
// Half of the IV is random, the lower order bits are zeroed
|
// Half of the IV is random, the lower order bits are zeroed
|
||||||
// such that the counter never wraps.
|
// such that the counter never wraps.
|
||||||
// See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75
|
// See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75
|
||||||
val initVectorBytes = ByteArray(16)
|
val initVectorBytes = ByteArray(16) { 0.toByte() }
|
||||||
Arrays.fill(initVectorBytes, 0.toByte())
|
|
||||||
|
|
||||||
val ivRandomPart = ByteArray(8)
|
val ivRandomPart = ByteArray(8)
|
||||||
secureRandom.nextBytes(ivRandomPart)
|
secureRandom.nextBytes(ivRandomPart)
|
||||||
@ -115,7 +113,7 @@ object MXEncryptedAttachments {
|
|||||||
encryptedByteArray = outStream.toByteArray()
|
encryptedByteArray = outStream.toByteArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
Timber.v("Encrypt in " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("Encrypt in ${System.currentTimeMillis() - t0} ms")
|
||||||
return Try.just(result)
|
return Try.just(result)
|
||||||
} catch (oom: OutOfMemoryError) {
|
} catch (oom: OutOfMemoryError) {
|
||||||
Timber.e(oom, "## encryptAttachment failed")
|
Timber.e(oom, "## encryptAttachment failed")
|
||||||
@ -206,13 +204,13 @@ object MXEncryptedAttachments {
|
|||||||
val decryptedStream = ByteArrayInputStream(outStream.toByteArray())
|
val decryptedStream = ByteArrayInputStream(outStream.toByteArray())
|
||||||
outStream.close()
|
outStream.close()
|
||||||
|
|
||||||
Timber.v("Decrypt in " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("Decrypt in ${System.currentTimeMillis() - t0} ms")
|
||||||
|
|
||||||
return decryptedStream
|
return decryptedStream
|
||||||
} catch (oom: OutOfMemoryError) {
|
} catch (oom: OutOfMemoryError) {
|
||||||
Timber.e(oom, "## decryptAttachment() : failed " + oom.message)
|
Timber.e(oom, "## decryptAttachment() : failed ${oom.message}")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## decryptAttachment() : failed " + e.message)
|
Timber.e(e, "## decryptAttachment() : failed ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -228,34 +226,20 @@ object MXEncryptedAttachments {
|
|||||||
* Base64 URL conversion methods
|
* Base64 URL conversion methods
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private fun base64UrlToBase64(base64Url: String?): String? {
|
private fun base64UrlToBase64(base64Url: String): String {
|
||||||
var result = base64Url
|
return base64Url.replace('-', '+')
|
||||||
if (null != result) {
|
.replace('_', '/')
|
||||||
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? {
|
private fun base64ToUnpaddedBase64(base64: String): String {
|
||||||
var result = base64
|
return base64.replace("\n".toRegex(), "")
|
||||||
if (null != result) {
|
.replace("=", "")
|
||||||
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? {
|
|
||||||
var result = base64
|
|
||||||
if (null != result) {
|
|
||||||
result = result.replace("\n".toRegex(), "")
|
|
||||||
result = result.replace("=".toRegex(), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,8 @@ import org.matrix.olm.OlmPkEncryption
|
|||||||
import org.matrix.olm.OlmPkMessage
|
import org.matrix.olm.OlmPkMessage
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.security.InvalidParameterException
|
import java.security.InvalidParameterException
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
|
* A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
|
||||||
@ -114,8 +113,6 @@ internal class KeysBackup @Inject constructor(
|
|||||||
// The backup key being used.
|
// The backup key being used.
|
||||||
private var backupOlmPkEncryption: OlmPkEncryption? = null
|
private var backupOlmPkEncryption: OlmPkEncryption? = null
|
||||||
|
|
||||||
private val random = Random()
|
|
||||||
|
|
||||||
private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null
|
private var backupAllGroupSessionsCallback: MatrixCallback<Unit>? = null
|
||||||
|
|
||||||
private var keysBackupStateListener: KeysBackupStateListener? = null
|
private var keysBackupStateListener: KeysBackupStateListener? = null
|
||||||
@ -848,7 +845,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
// Wait between 0 and 10 seconds, to avoid backup requests from
|
// Wait between 0 and 10 seconds, to avoid backup requests from
|
||||||
// different clients hitting the server all at the same time when a
|
// different clients hitting the server all at the same time when a
|
||||||
// new key is sent
|
// new key is sent
|
||||||
val delayInMs = random.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong()
|
val delayInMs = Random.nextLong(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS)
|
||||||
|
|
||||||
uiHandler.postDelayed({ backupKeys() }, delayInMs)
|
uiHandler.postDelayed({ backupKeys() }, delayInMs)
|
||||||
}
|
}
|
||||||
@ -1307,7 +1304,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
// Make the request
|
// Make the request
|
||||||
storeSessionDataTask
|
storeSessionDataTask
|
||||||
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){
|
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)) {
|
||||||
this.callback = sendingRequestCallback
|
this.callback = sendingRequestCallback
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
@ -1405,7 +1402,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Maximum delay in ms in {@link maybeBackupKeys}
|
// Maximum delay in ms in {@link maybeBackupKeys}
|
||||||
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10000
|
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10_000L
|
||||||
|
|
||||||
// Maximum number of keys to send at a time to the homeserver.
|
// Maximum number of keys to send at a time to the homeserver.
|
||||||
private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100
|
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 androidx.annotation.WorkerThread
|
||||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
@ -20,7 +20,6 @@ import android.os.Handler
|
|||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class KeysBackupStateManager(private val uiHandler: Handler) {
|
internal class KeysBackupStateManager(private val uiHandler: Handler) {
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import com.squareup.moshi.JsonClass
|
|||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MXDeviceInfo(
|
data class MXDeviceInfo(
|
||||||
|
@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.model
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class MXKey(
|
data class MXKey(
|
||||||
/**
|
/**
|
||||||
@ -46,11 +45,7 @@ data class MXKey(
|
|||||||
* @return the signed data map
|
* @return the signed data map
|
||||||
*/
|
*/
|
||||||
fun signalableJSONDictionary(): Map<String, Any> {
|
fun signalableJSONDictionary(): Map<String, Any> {
|
||||||
val map = HashMap<String, Any>()
|
return mapOf("key" to value)
|
||||||
|
|
||||||
map["key"] = value
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.model
|
package im.vector.matrix.android.internal.crypto.model
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MXUsersDevicesMap<E> {
|
class MXUsersDevicesMap<E> {
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the user Ids
|
* @return the user Ids
|
||||||
*/
|
*/
|
||||||
val userIds: List<String>
|
val userIds: List<String>
|
||||||
get() = ArrayList(map.keys)
|
get() = map.keys.toList()
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = map.isEmpty()
|
get() = map.isEmpty()
|
||||||
@ -40,7 +39,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the device ids list
|
* @return the device ids list
|
||||||
*/
|
*/
|
||||||
fun getUserDeviceIds(userId: String?): List<String>? {
|
fun getUserDeviceIds(userId: String?): List<String>? {
|
||||||
return if (userId?.isNotBlank() == true && map.containsKey(userId)) {
|
return if (!userId.isNullOrBlank() && map.containsKey(userId)) {
|
||||||
map[userId]!!.keys.toList()
|
map[userId]!!.keys.toList()
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
@ -53,7 +52,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @return the object
|
* @return the object
|
||||||
*/
|
*/
|
||||||
fun getObject(userId: String?, deviceId: String?): E? {
|
fun getObject(userId: String?, deviceId: String?): E? {
|
||||||
return if (userId?.isNotBlank() == true && deviceId?.isNotBlank() == true && map.containsKey(userId)) {
|
return if (!userId.isNullOrBlank() && !deviceId.isNullOrBlank()) {
|
||||||
map[userId]?.get(deviceId)
|
map[userId]?.get(deviceId)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
@ -67,11 +66,8 @@ class MXUsersDevicesMap<E> {
|
|||||||
*/
|
*/
|
||||||
fun setObject(userId: String?, deviceId: String?, o: E?) {
|
fun setObject(userId: String?, deviceId: String?, o: E?) {
|
||||||
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
|
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
|
||||||
if (map[userId] == null) {
|
val devices = map.getOrPut(userId) { HashMap() }
|
||||||
map[userId] = HashMap()
|
devices[deviceId] = o
|
||||||
}
|
|
||||||
|
|
||||||
map[userId]?.put(deviceId, o)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +78,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @param userId the user id
|
* @param userId the user id
|
||||||
*/
|
*/
|
||||||
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
|
fun setObjects(userId: String?, objectsPerDevices: Map<String, E>?) {
|
||||||
if (userId?.isNotBlank() == true) {
|
if (!userId.isNullOrBlank()) {
|
||||||
if (null == objectsPerDevices) {
|
if (null == objectsPerDevices) {
|
||||||
map.remove(userId)
|
map.remove(userId)
|
||||||
} else {
|
} else {
|
||||||
@ -97,7 +93,7 @@ class MXUsersDevicesMap<E> {
|
|||||||
* @param userId the user id.
|
* @param userId the user id.
|
||||||
*/
|
*/
|
||||||
fun removeUserObjects(userId: String?) {
|
fun removeUserObjects(userId: String?) {
|
||||||
if (userId?.isNotBlank() == true) {
|
if (!userId.isNullOrBlank()) {
|
||||||
map.remove(userId)
|
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.crypto.model.rest.SendToDeviceBody
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||||
data class Params(
|
data class Params(
|
||||||
@ -45,7 +45,7 @@ internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi
|
|||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.sendToDevice(
|
apiCall = cryptoApi.sendToDevice(
|
||||||
params.eventType,
|
params.eventType,
|
||||||
params.transactionId ?: Random().nextInt(Integer.MAX_VALUE).toString(),
|
params.transactionId ?: Random.nextInt(Integer.MAX_VALUE).toString(),
|
||||||
sendToDeviceBody
|
sendToDeviceBody
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
cancelTransaction(
|
cancelTransaction(
|
||||||
startReq.transactionID!!,
|
startReq.transactionID!!,
|
||||||
otherUserId!!,
|
otherUserId!!,
|
||||||
startReq?.fromDevice ?: event.getSenderKey()!!,
|
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||||
CancelCode.UnknownMethod
|
CancelCode.UnknownMethod
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -388,14 +388,13 @@ 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
|
* 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 {
|
private fun createUniqueIDForTransaction(userId: String, deviceID: String): String {
|
||||||
val buff = StringBuffer()
|
return buildString {
|
||||||
buff
|
append(credentials.userId).append("|")
|
||||||
.append(credentials.userId).append("|")
|
append(credentials.deviceId).append("|")
|
||||||
.append(credentials.deviceId).append("|")
|
append(userId).append("|")
|
||||||
.append(userId).append("|")
|
append(deviceID).append("|")
|
||||||
.append(deviceID).append("|")
|
append(UUID.randomUUID().toString())
|
||||||
.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.api.session.room.model.tag.RoomTag
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryMapper @Inject constructor(
|
internal class RoomSummaryMapper @Inject constructor(
|
||||||
|
@ -24,7 +24,6 @@ import java.security.KeyStore
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import java.net.UnknownHostException
|
|||||||
import java.security.KeyManagementException
|
import java.security.KeyManagementException
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.SSLSocket
|
import javax.net.ssl.SSLSocket
|
||||||
import javax.net.ssl.SSLSocketFactory
|
import javax.net.ssl.SSLSocketFactory
|
||||||
@ -101,25 +100,16 @@ constructor(trustPinned: Array<TrustManager>, acceptedTlsVersions: List<TlsVersi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun enableTLSOnSocket(socket: Socket?): Socket? {
|
private fun enableTLSOnSocket(socket: Socket?): Socket? {
|
||||||
if (socket != null && socket is SSLSocket) {
|
if (socket is SSLSocket) {
|
||||||
val sslSocket = socket as SSLSocket?
|
val supportedProtocols = socket.supportedProtocols.toSet()
|
||||||
|
val filteredEnabledProtocols = enabledProtocols.filter { it in supportedProtocols }
|
||||||
|
|
||||||
val supportedProtocols = Arrays.asList(*sslSocket!!.supportedProtocols)
|
if (filteredEnabledProtocols.isNotEmpty()) {
|
||||||
val filteredEnabledProtocols = ArrayList<String>()
|
|
||||||
|
|
||||||
for (protocol in enabledProtocols) {
|
|
||||||
if (supportedProtocols.contains(protocol)) {
|
|
||||||
filteredEnabledProtocols.add(protocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filteredEnabledProtocols.isEmpty()) {
|
|
||||||
try {
|
try {
|
||||||
sslSocket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
|
socket.enabledProtocols = filteredEnabledProtocols.toTypedArray()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return socket
|
return socket
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session
|
package im.vector.matrix.android.internal.session
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
@ -25,31 +26,33 @@ import javax.inject.Inject
|
|||||||
@SessionScope
|
@SessionScope
|
||||||
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
|
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
|
||||||
|
|
||||||
var status = MutableLiveData<InitialSyncProgressService.Status>()
|
private var status = MutableLiveData<InitialSyncProgressService.Status>()
|
||||||
|
|
||||||
var rootTask: TaskInfo? = null
|
private var rootTask: TaskInfo? = null
|
||||||
|
|
||||||
override fun getLiveStatus(): LiveData<InitialSyncProgressService.Status?> {
|
override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status?> {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startTask(@StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
|
||||||
fun startTask(nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
|
// Create a rootTask, or add a child to the leaf
|
||||||
if (rootTask == null) {
|
if (rootTask == null) {
|
||||||
rootTask = TaskInfo(nameRes, totalProgress)
|
rootTask = TaskInfo(nameRes, totalProgress)
|
||||||
} else {
|
} else {
|
||||||
val currentLeaf = rootTask!!.leaf()
|
val currentLeaf = rootTask!!.leaf()
|
||||||
val newTask = TaskInfo(nameRes, totalProgress)
|
|
||||||
newTask.parent = currentLeaf
|
val newTask = TaskInfo(nameRes,
|
||||||
newTask.offset = currentLeaf.currentProgress
|
totalProgress,
|
||||||
|
currentLeaf,
|
||||||
|
parentWeight)
|
||||||
|
|
||||||
currentLeaf.child = newTask
|
currentLeaf.child = newTask
|
||||||
newTask.parentWeight = parentWeight
|
|
||||||
}
|
}
|
||||||
reportProgress(0)
|
reportProgress(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reportProgress(progress: Int) {
|
fun reportProgress(progress: Int) {
|
||||||
rootTask?.leaf()?.incrementProgress(progress)
|
rootTask?.leaf()?.setProgress(progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endTask(nameRes: Int) {
|
fun endTask(nameRes: Int) {
|
||||||
@ -58,7 +61,7 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
//close it
|
//close it
|
||||||
val parent = endedTask.parent
|
val parent = endedTask.parent
|
||||||
parent?.child = null
|
parent?.child = null
|
||||||
parent?.incrementProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
|
parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
|
||||||
}
|
}
|
||||||
if (endedTask?.parent == null) {
|
if (endedTask?.parent == null) {
|
||||||
status.postValue(null)
|
status.postValue(null)
|
||||||
@ -71,14 +74,17 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inner class TaskInfo(var nameRes: Int,
|
private inner class TaskInfo(@StringRes var nameRes: Int,
|
||||||
var totalProgress: Int) {
|
var totalProgress: Int,
|
||||||
var parent: TaskInfo? = null
|
var parent: TaskInfo? = null,
|
||||||
|
var parentWeight: Float = 1f,
|
||||||
|
var offset: Int = parent?.currentProgress ?: 0) {
|
||||||
var child: TaskInfo? = null
|
var child: TaskInfo? = null
|
||||||
var parentWeight: Float = 1f
|
|
||||||
var currentProgress: Int = 0
|
var currentProgress: Int = 0
|
||||||
var offset: Int = 0
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the further child
|
||||||
|
*/
|
||||||
fun leaf(): TaskInfo {
|
fun leaf(): TaskInfo {
|
||||||
var last = this
|
var last = this
|
||||||
while (last.child != null) {
|
while (last.child != null) {
|
||||||
@ -87,26 +93,27 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
|
|||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
fun incrementProgress(progress: Int) {
|
/**
|
||||||
|
* Set progress of the parent if any (which will post value), or post the value
|
||||||
|
*/
|
||||||
|
fun setProgress(progress: Int) {
|
||||||
currentProgress = progress
|
currentProgress = progress
|
||||||
// val newProgress = Math.min(currentProgress + progress, totalProgress)
|
// val newProgress = Math.min(currentProgress + progress, totalProgress)
|
||||||
parent?.let {
|
parent?.let {
|
||||||
val parentProgress = (currentProgress * parentWeight).toInt()
|
val parentProgress = (currentProgress * parentWeight).toInt()
|
||||||
it.incrementProgress(offset + parentProgress)
|
it.setProgress(offset + parentProgress)
|
||||||
}
|
} ?: run {
|
||||||
if (parent == null) {
|
Timber.e("--- ${leaf().nameRes}: $currentProgress")
|
||||||
Timber.e("--- ${leaf().nameRes}: ${currentProgress}")
|
|
||||||
status.postValue(
|
status.postValue(
|
||||||
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
|
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?,
|
inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?,
|
||||||
nameRes: Int,
|
@StringRes nameRes: Int,
|
||||||
totalProgress: Int,
|
totalProgress: Int,
|
||||||
parentWeight: Float = 1f,
|
parentWeight: Float = 1f,
|
||||||
block: () -> T): T {
|
block: () -> T): T {
|
||||||
@ -121,11 +128,11 @@ inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: DefaultInitialSyncP
|
|||||||
taskId: Int,
|
taskId: Int,
|
||||||
weight: Float,
|
weight: Float,
|
||||||
transform: (Map.Entry<K, V>) -> R): List<R> {
|
transform: (Map.Entry<K, V>) -> R): List<R> {
|
||||||
val total = count()
|
val total = count().toFloat()
|
||||||
var current = 0
|
var current = 0
|
||||||
reporter?.startTask(taskId, 100, weight)
|
reporter?.startTask(taskId, 100, weight)
|
||||||
return this.map {
|
return map {
|
||||||
reporter?.reportProgress((current / total.toFloat() * 100).toInt())
|
reporter?.reportProgress((current / total * 100).toInt())
|
||||||
current++
|
current++
|
||||||
transform.invoke(it)
|
transform.invoke(it)
|
||||||
}.also {
|
}.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.sync.SyncState
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
|
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
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.SyncThread
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
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 signOutService: Lazy<SignOutService>,
|
||||||
private val pushRuleService: Lazy<PushRuleService>,
|
private val pushRuleService: Lazy<PushRuleService>,
|
||||||
private val pushersService: Lazy<PushersService>,
|
private val pushersService: Lazy<PushersService>,
|
||||||
private val cryptoService: Lazy<CryptoManager>,
|
private val cryptoService: Lazy<DefaultCryptoService>,
|
||||||
private val fileService: Lazy<FileService>,
|
private val fileService: Lazy<FileService>,
|
||||||
private val syncThreadProvider: Provider<SyncThread>,
|
private val syncThreadProvider: Provider<SyncThread>,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
|
@ -30,9 +30,8 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
|
|||||||
private val listeners = mutableMapOf<String, MutableList<ContentUploadStateTracker.UpdateListener>>()
|
private val listeners = mutableMapOf<String, MutableList<ContentUploadStateTracker.UpdateListener>>()
|
||||||
|
|
||||||
override fun track(key: String, updateListener: ContentUploadStateTracker.UpdateListener) {
|
override fun track(key: String, updateListener: ContentUploadStateTracker.UpdateListener) {
|
||||||
val listeners = listeners[key] ?: ArrayList()
|
val listeners = listeners.getOrPut(key) { ArrayList() }
|
||||||
listeners.add(updateListener)
|
listeners.add(updateListener)
|
||||||
this.listeners[key] = listeners
|
|
||||||
val currentState = states[key] ?: ContentUploadStateTracker.State.Idle
|
val currentState = states[key] ?: ContentUploadStateTracker.State.Idle
|
||||||
mainHandler.post { updateListener.onUpdate(currentState) }
|
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
|
||||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -69,15 +69,11 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
.also {
|
.also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
}
|
}
|
||||||
val sendRelationWork = createSendRelationWork(event)
|
val sendRelationWork = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
||||||
return CancelableWork(context, sendRelationWork.id)
|
return CancelableWork(context, sendRelationWork.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendRelationWork(event: Event): OneTimeWorkRequest {
|
|
||||||
return createSendEventWork(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {
|
override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {
|
||||||
|
|
||||||
val params = FindReactionEventForUndoTask.Params(
|
val params = FindReactionEventForUndoTask.Params(
|
||||||
@ -134,42 +130,42 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
.also {
|
.also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
}
|
}
|
||||||
if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||||
return CancelableWork(context, encryptWork.id)
|
CancelableWork(context, encryptWork.id)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
return CancelableWork(context, workRequest.id)
|
CancelableWork(context, workRequest.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun editReply(replyToEdit: TimelineEvent,
|
override fun editReply(replyToEdit: TimelineEvent,
|
||||||
originalEvent: TimelineEvent,
|
originalTimelineEvent: TimelineEvent,
|
||||||
newBodyText: String,
|
newBodyText: String,
|
||||||
compatibilityBodyText: String): Cancelable {
|
compatibilityBodyText: String): Cancelable {
|
||||||
val event = eventFactory
|
val event = eventFactory
|
||||||
.createReplaceTextOfReply(roomId,
|
.createReplaceTextOfReply(roomId,
|
||||||
replyToEdit,
|
replyToEdit,
|
||||||
originalEvent,
|
originalTimelineEvent,
|
||||||
newBodyText, true, MessageType.MSGTYPE_TEXT, compatibilityBodyText)
|
newBodyText, true, MessageType.MSGTYPE_TEXT, compatibilityBodyText)
|
||||||
.also {
|
.also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
}
|
}
|
||||||
if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||||
return CancelableWork(context, encryptWork.id)
|
CancelableWork(context, encryptWork.id)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
return CancelableWork(context, workRequest.id)
|
CancelableWork(context, workRequest.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,16 +183,16 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, false)
|
||||||
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||||
return CancelableWork(context, encryptWork.id)
|
CancelableWork(context, encryptWork.id)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val workRequest = createSendEventWork(event)
|
val workRequest = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
return CancelableWork(context, workRequest.id)
|
CancelableWork(context, workRequest.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -208,10 +204,10 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(event: Event): OneTimeWorkRequest {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, true)
|
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {
|
override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {
|
||||||
|
@ -38,7 +38,7 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
|||||||
import im.vector.matrix.android.internal.util.StringProvider
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
import org.commonmark.renderer.html.HtmlRenderer
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -304,17 +304,22 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String {
|
private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String {
|
||||||
|
return buildString {
|
||||||
|
append("> <")
|
||||||
|
append(originalSenderId)
|
||||||
|
append(">")
|
||||||
|
|
||||||
val lines = body.text.split("\n")
|
val lines = body.text.split("\n")
|
||||||
val replyFallback = StringBuffer("> <$originalSenderId>")
|
|
||||||
lines.forEachIndexed { index, s ->
|
lines.forEachIndexed { index, s ->
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
replyFallback.append(" $s")
|
append(" $s")
|
||||||
} else {
|
} else {
|
||||||
replyFallback.append("\n> $s")
|
append("\n> $s")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyFallback.append("\n\n").append(newBodyText)
|
append("\n\n")
|
||||||
return replyFallback.toString()
|
append(newBodyText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
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.api.session.room.model.message.MessageContent
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
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.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||||
@ -33,7 +33,7 @@ import timber.log.Timber
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: CryptoManager,
|
internal class CryptoSyncHandler @Inject constructor(private val cryptoService: DefaultCryptoService,
|
||||||
private val sasVerificationService: DefaultSasVerificationService) {
|
private val sasVerificationService: DefaultSasVerificationService) {
|
||||||
|
|
||||||
fun handleToDevice(toDevice: ToDeviceSyncResponse, initialSyncProgressService: DefaultInitialSyncProgressService? = null) {
|
fun handleToDevice(toDevice: ToDeviceSyncResponse, initialSyncProgressService: DefaultInitialSyncProgressService? = null) {
|
||||||
@ -47,13 +47,13 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager:
|
|||||||
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
||||||
} else {
|
} else {
|
||||||
sasVerificationService.onToDeviceEvent(event)
|
sasVerificationService.onToDeviceEvent(event)
|
||||||
cryptoManager.onToDeviceEvent(event)
|
cryptoService.onToDeviceEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSyncCompleted(syncResponse: SyncResponse) {
|
fun onSyncCompleted(syncResponse: SyncResponse) {
|
||||||
cryptoManager.onSyncCompleted(syncResponse)
|
cryptoService.onSyncCompleted(syncResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager:
|
|||||||
if (event.getClearType() == EventType.ENCRYPTED) {
|
if (event.getClearType() == EventType.ENCRYPTED) {
|
||||||
var result: MXEventDecryptionResult? = null
|
var result: MXEventDecryptionResult? = null
|
||||||
try {
|
try {
|
||||||
result = cryptoManager.decryptEvent(event, timelineId ?: "")
|
result = cryptoService.decryptEvent(event, timelineId ?: "")
|
||||||
} catch (exception: MXCryptoError) {
|
} catch (exception: MXCryptoError) {
|
||||||
event.mCryptoError = (exception as? MXCryptoError.Base)?.errorType //setCryptoError(exception.cryptoError)
|
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.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
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.api.session.room.model.tag.RoomTagContent
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.database.helper.*
|
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.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
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 readReceiptHandler: ReadReceiptHandler,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||||
private val roomTagHandler: RoomTagHandler,
|
private val roomTagHandler: RoomTagHandler,
|
||||||
private val cryptoManager: CryptoManager,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val tokenStore: SyncTokenStore,
|
private val tokenStore: SyncTokenStore,
|
||||||
private val pushRuleService: DefaultPushRuleService,
|
private val pushRuleService: DefaultPushRuleService,
|
||||||
private val processForPushTask: ProcessEventForPushTask,
|
private val processForPushTask: ProcessEventForPushTask,
|
||||||
@ -97,12 +97,12 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
handleJoinedRoom(realm, it.key, it.value, isInitialSync)
|
handleJoinedRoom(realm, it.key, it.value, isInitialSync)
|
||||||
}
|
}
|
||||||
is HandlingStrategy.INVITED ->
|
is HandlingStrategy.INVITED ->
|
||||||
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.4f) {
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.1f) {
|
||||||
handleInvitedRoom(realm, it.key, it.value)
|
handleInvitedRoom(realm, it.key, it.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
is HandlingStrategy.LEFT -> {
|
is HandlingStrategy.LEFT -> {
|
||||||
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.2f) {
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.3f) {
|
||||||
handleLeftRoom(realm, it.key, it.value)
|
handleLeftRoom(realm, it.key, it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,8 +125,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
|
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
|
||||||
}
|
}
|
||||||
|
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||||
?: realm.createObject(roomId)
|
|
||||||
|
|
||||||
if (roomEntity.membership == Membership.INVITE) {
|
if (roomEntity.membership == Membership.INVITE) {
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
@ -135,13 +134,12 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
|
|
||||||
// State event
|
// State event
|
||||||
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
||||||
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt() ?: Int.MIN_VALUE
|
||||||
?: Int.MIN_VALUE
|
|
||||||
val untimelinedStateIndex = minStateIndex + 1
|
val untimelinedStateIndex = minStateIndex + 1
|
||||||
roomSync.state.events.forEach { event ->
|
roomSync.state.events.forEach { event ->
|
||||||
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||||
// Give info to crypto module
|
// Give info to crypto module
|
||||||
cryptoManager.onStateEvent(roomId, event)
|
cryptoService.onStateEvent(roomId, event)
|
||||||
UserEntityFactory.createOrNull(event)?.also {
|
UserEntityFactory.createOrNull(event)?.also {
|
||||||
realm.insertOrUpdate(it)
|
realm.insertOrUpdate(it)
|
||||||
}
|
}
|
||||||
@ -167,8 +165,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
roomSync:
|
roomSync:
|
||||||
InvitedRoomSync): RoomEntity {
|
InvitedRoomSync): RoomEntity {
|
||||||
Timber.v("Handle invited sync for room $roomId")
|
Timber.v("Handle invited sync for room $roomId")
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||||
?: realm.createObject(roomId)
|
|
||||||
roomEntity.membership = Membership.INVITE
|
roomEntity.membership = Membership.INVITE
|
||||||
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
||||||
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
||||||
@ -181,8 +178,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
private fun handleLeftRoom(realm: Realm,
|
private fun handleLeftRoom(realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
roomSync: RoomSync): RoomEntity {
|
roomSync: RoomSync): RoomEntity {
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||||
?: realm.createObject(roomId)
|
|
||||||
|
|
||||||
roomEntity.membership = Membership.LEAVE
|
roomEntity.membership = Membership.LEAVE
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
@ -214,7 +210,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
event.eventId?.also { eventIds.add(it) }
|
event.eventId?.also { eventIds.add(it) }
|
||||||
chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)
|
chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)
|
||||||
// Give info to crypto module
|
// Give info to crypto module
|
||||||
cryptoManager.onLiveEvent(roomEntity.roomId, event)
|
cryptoService.onLiveEvent(roomEntity.roomId, event)
|
||||||
// Try to remove local echo
|
// Try to remove local echo
|
||||||
event.unsignedData?.transactionId?.also {
|
event.unsignedData?.transactionId?.also {
|
||||||
val sendingEventEntity = roomEntity.sendingTimelineEvents.find(it)
|
val sendingEventEntity = roomEntity.sendingTimelineEvents.find(it)
|
||||||
|
@ -21,7 +21,6 @@ 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.model.RoomTagEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomTagHandler @Inject constructor() {
|
internal class RoomTagHandler @Inject constructor() {
|
||||||
@ -30,16 +29,8 @@ internal class RoomTagHandler @Inject constructor() {
|
|||||||
if (content == null) {
|
if (content == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val tags = ArrayList<RoomTagEntity>()
|
val tags = content.tags.entries.map { (tagName, params) ->
|
||||||
for (tagName in content.tags.keys) {
|
RoomTagEntity(tagName, params["order"] as? Double)
|
||||||
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()
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: RoomSummaryEntity(roomId)
|
?: RoomSummaryEntity(roomId)
|
||||||
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.sync
|
|||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import im.vector.matrix.android.R
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
import im.vector.matrix.android.internal.session.reportSubtask
|
import im.vector.matrix.android.internal.session.reportSubtask
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
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 userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||||
private val groupSyncHandler: GroupSyncHandler,
|
private val groupSyncHandler: GroupSyncHandler,
|
||||||
private val cryptoSyncHandler: CryptoSyncHandler,
|
private val cryptoSyncHandler: CryptoSyncHandler,
|
||||||
private val cryptoManager: CryptoManager,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val initialSyncProgressService: DefaultInitialSyncProgressService) {
|
private val initialSyncProgressService: DefaultInitialSyncProgressService) {
|
||||||
|
|
||||||
fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
|
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 }
|
val reporter = initialSyncProgressService.takeIf { isInitialSync }
|
||||||
|
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
if (!cryptoManager.isStarted()) {
|
if (!cryptoService.isStarted()) {
|
||||||
Timber.v("Should start cryptoManager")
|
Timber.v("Should start cryptoService")
|
||||||
cryptoManager.start(isInitialSync)
|
cryptoService.start(isInitialSync)
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
Timber.v("Finish handling start cryptoManager in $it ms")
|
Timber.v("Finish handling start cryptoService in $it ms")
|
||||||
}
|
}
|
||||||
val measure = measureTimeMillis {
|
val measure = measureTimeMillis {
|
||||||
// Handle the to device events before the room ones
|
// 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 android.content.Context
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
internal class CancelableWork(private val context: Context,
|
internal class CancelableWork(private val context: Context,
|
||||||
private val workId: UUID) : Cancelable {
|
private val workId: UUID) : Cancelable {
|
||||||
|
@ -34,7 +34,7 @@ import java.security.*
|
|||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.spec.AlgorithmParameterSpec
|
import java.security.spec.AlgorithmParameterSpec
|
||||||
import java.security.spec.RSAKeyGenParameterSpec
|
import java.security.spec.RSAKeyGenParameterSpec
|
||||||
import java.util.*
|
import java.util.Calendar
|
||||||
import java.util.zip.GZIPOutputStream
|
import java.util.zip.GZIPOutputStream
|
||||||
import javax.crypto.*
|
import javax.crypto.*
|
||||||
import javax.crypto.spec.GCMParameterSpec
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
|
@ -22,7 +22,7 @@ import org.json.JSONArray
|
|||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.TreeSet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build canonical Json
|
* Build canonical Json
|
||||||
@ -60,43 +60,33 @@ object JsonCanonicalizer {
|
|||||||
when (any) {
|
when (any) {
|
||||||
is JSONArray -> {
|
is JSONArray -> {
|
||||||
// Canonicalize each element of the array
|
// Canonicalize each element of the array
|
||||||
val result = StringBuilder("[")
|
return (0 until any.length()).joinToString(separator = ",", prefix = "[", postfix = "]") {
|
||||||
|
canonicalizeRecursive(any.get(it))
|
||||||
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 -> {
|
is JSONObject -> {
|
||||||
// Sort the attributes by name, and the canonicalize each element of the JSONObject
|
// Sort the attributes by name, and the canonicalize each element of the JSONObject
|
||||||
val result = StringBuilder("{")
|
|
||||||
|
|
||||||
val attributes = TreeSet<String>()
|
val attributes = TreeSet<String>()
|
||||||
for (entry in any.keys()) {
|
for (entry in any.keys()) {
|
||||||
attributes.add(entry)
|
attributes.add(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (attribute in attributes.withIndex()) {
|
return buildString {
|
||||||
result.append("\"")
|
append("{")
|
||||||
.append(attribute.value)
|
for ((index, value) in attributes.withIndex()) {
|
||||||
.append("\"")
|
append("\"")
|
||||||
.append(":")
|
append(value)
|
||||||
.append(canonicalizeRecursive(any[attribute.value]))
|
append("\"")
|
||||||
|
append(":")
|
||||||
|
append(canonicalizeRecursive(any[value]))
|
||||||
|
|
||||||
if (attribute.index < attributes.size - 1) {
|
if (index < attributes.size - 1) {
|
||||||
result.append(",")
|
append(",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
append("}")
|
||||||
result.append("}")
|
}
|
||||||
|
|
||||||
return result.toString()
|
|
||||||
}
|
}
|
||||||
is String -> return JSONObject.quote(any)
|
is String -> return JSONObject.quote(any)
|
||||||
else -> return any.toString()
|
else -> return any.toString()
|
||||||
|
@ -167,4 +167,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Начална синхронизация:
|
<string name="initial_sync_start_importing_account_data">Начална синхронизация:
|
||||||
\nИмпортиране на данни за профила</string>
|
\nИмпортиране на данни за профила</string>
|
||||||
|
|
||||||
|
<string name="notice_room_update">%s обнови тази стая.</string>
|
||||||
|
|
||||||
|
<string name="event_status_sending_message">Изпращане на съобщение…</string>
|
||||||
|
<string name="clear_timeline_send_queue">Изчисти опашката за изпращане</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -105,4 +105,65 @@
|
|||||||
<string name="verification_emoji_pig">Schwein</string>
|
<string name="verification_emoji_pig">Schwein</string>
|
||||||
<string name="verification_emoji_elephant">Elefant</string>
|
<string name="verification_emoji_elephant">Elefant</string>
|
||||||
<string name="verification_emoji_rabbit">Hase</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>
|
</resources>
|
||||||
|
@ -167,4 +167,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Hasierako sinkronizazioa:
|
<string name="initial_sync_start_importing_account_data">Hasierako sinkronizazioa:
|
||||||
\nKontuaren datuak inportatzen</string>
|
\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>
|
</resources>
|
||||||
|
@ -168,4 +168,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Alkusynkronointi:
|
<string name="initial_sync_start_importing_account_data">Alkusynkronointi:
|
||||||
\nTuodaan tilin tietoja</string>
|
\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>
|
</resources>
|
||||||
|
@ -167,4 +167,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Synchronisation initiale :
|
<string name="initial_sync_start_importing_account_data">Synchronisation initiale :
|
||||||
\nImportation des données du compte</string>
|
\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 d’envoi</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -166,4 +166,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Induló szinkronizáció:
|
<string name="initial_sync_start_importing_account_data">Induló szinkronizáció:
|
||||||
\nFiók adatok betöltése</string>
|
\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>
|
</resources>
|
||||||
|
@ -167,4 +167,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Sync iniziale:
|
<string name="initial_sync_start_importing_account_data">Sync iniziale:
|
||||||
\nImportazione dati account</string>
|
\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>
|
</resources>
|
||||||
|
@ -1,6 +1,173 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="summary_message">%1$s: %2$s</string>
|
<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="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>
|
</resources>
|
||||||
|
@ -176,4 +176,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Initiële synchronisatie:
|
<string name="initial_sync_start_importing_account_data">Initiële synchronisatie:
|
||||||
\nAccountgegevens worden geïmporteerd</string>
|
\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>
|
</resources>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<string name="notice_room_invite">%1$s zaprosił(a) %2$s</string>
|
<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_invite_you">%1$s zaprosił(a) Cię</string>
|
||||||
<string name="notice_room_join">%1$s dołączył(a)</string>
|
<string name="notice_room_join">%1$s dołączył(a)</string>
|
||||||
<string name="notice_room_leave">%1$s wyszedł(-ła)</string>
|
<string name="notice_room_leave">%1$s opuścił(a)</string>
|
||||||
<string name="notice_room_reject">%1$s odrzucił(a) zaproszenie</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_kick">%1$s wyrzucił(a) %2$s</string>
|
||||||
<string name="notice_room_unban">%1$s odblokował(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_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_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="notice_room_topic_changed">%1$s zmienił(a) temat na: %2$s</string>
|
||||||
<string name="unable_to_send_message">Nie udało się wysłać wiadomości</string>
|
<string name="unable_to_send_message">Nie można wysłać wiadomości</string>
|
||||||
|
|
||||||
<string name="message_failed_to_upload">Nie udało się wysłać zdjęcia</string>
|
<string name="message_failed_to_upload">Przesyłanie zdjęcia nie powiodło się</string>
|
||||||
|
|
||||||
<string name="network_error">ogólne błędy</string>
|
<string name="network_error">Błąd sieci</string>
|
||||||
<string name="matrix_error">Błąd Matrixa</string>
|
<string name="matrix_error">Błąd Matrixa</string>
|
||||||
|
|
||||||
<string name="encrypted_message">Wiadomość zaszyfrowana</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_shared">wszyscy członkowie pokoju.</string>
|
||||||
<string name="notice_room_visibility_world_readable">wszyscy.</string>
|
<string name="notice_room_visibility_world_readable">wszyscy.</string>
|
||||||
<string name="notice_room_name_changed">%1$s zmienił(a) znawę pokoju na: %2$s</string>
|
<string name="notice_room_name_changed">%1$s zmienił(a) nazwę pokoju na: %2$s</string>
|
||||||
<string name="notice_ended_call">%s zakończył(a) rozmowę.</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_name_removed">%1$s usunął(-ęła) nazwę pokoju</string>
|
||||||
<string name="notice_room_topic_removed">%1$s usunął(-ęła) temat pokoju</string>
|
<string name="notice_room_topic_removed">%1$s usunął(-ęła) temat pokoju</string>
|
||||||
@ -57,9 +57,9 @@
|
|||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
<string name="notice_crypto_unable_to_decrypt">** Nie można odszyfrować: %s **</string>
|
<string name="notice_crypto_unable_to_decrypt">** Nie można odszyfrować: %s **</string>
|
||||||
<string name="notice_placed_video_call">%s umieścił wideo rozmowe.</string>
|
<string name="notice_placed_video_call">%s wykonał(a) rozmowę wideo.</string>
|
||||||
<string name="notice_placed_voice_call">%s umieścił połączenie głosowe.</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ł historię pokoju widoczną do %2$s</string>
|
<string name="notice_made_future_room_visibility">%1$s uczynił(a) przyszłą historię pokoju widoczną dla %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_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_joined">wszyscy członkowie pokoju, od momentu w którym dołączyli.</string>
|
||||||
<string name="notice_room_visibility_unknown">nieznane (%s).</string>
|
<string name="notice_room_visibility_unknown">nieznane (%s).</string>
|
||||||
@ -147,4 +147,29 @@
|
|||||||
<string name="verification_emoji_santa">Mikołaj</string>
|
<string name="verification_emoji_santa">Mikołaj</string>
|
||||||
<string name="verification_emoji_gift">Prezent</string>
|
<string name="verification_emoji_gift">Prezent</string>
|
||||||
<string name="verification_emoji_hammer">Młotek</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>
|
</resources>
|
||||||
|
@ -78,4 +78,10 @@
|
|||||||
<string name="room_displayname_empty_room">Sala vazia</string>
|
<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>
|
</resources>
|
||||||
|
@ -169,9 +169,9 @@
|
|||||||
\nИмпорт криптографии</string>
|
\nИмпорт криптографии</string>
|
||||||
<string name="initial_sync_start_importing_account_rooms">Начальная синхронизация:
|
<string name="initial_sync_start_importing_account_rooms">Начальная синхронизация:
|
||||||
\nИмпорт комнат</string>
|
\nИмпорт комнат</string>
|
||||||
<string name="initial_sync_start_importing_account_joined_rooms">Начальная синхронизация:
|
<string name="initial_sync_start_importing_account_joined_rooms">Синхронизация начата:
|
||||||
\nИмпорт присоединенных комнат</string>
|
\nИмпорт присоединенных комнат</string>
|
||||||
<string name="initial_sync_start_importing_account_invited_rooms">Начальная синхронизация:
|
<string name="initial_sync_start_importing_account_invited_rooms">Синхронизация начата:
|
||||||
\nИмпорт приглашенных комнат</string>
|
\nИмпорт приглашенных комнат</string>
|
||||||
<string name="initial_sync_start_importing_account_left_rooms">Начальная синхронизация:
|
<string name="initial_sync_start_importing_account_left_rooms">Начальная синхронизация:
|
||||||
\nИмпорт покинутых комнат</string>
|
\nИмпорт покинутых комнат</string>
|
||||||
@ -180,4 +180,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">Начальная синхронизация:
|
<string name="initial_sync_start_importing_account_data">Начальная синхронизация:
|
||||||
\nИмпорт данных учетной записи</string>
|
\nИмпорт данных учетной записи</string>
|
||||||
|
|
||||||
|
<string name="notice_room_update">%s обновил эту комнату.</string>
|
||||||
|
|
||||||
|
<string name="event_status_sending_message">Отправка сообщения…</string>
|
||||||
|
<string name="clear_timeline_send_queue">Очистить очередь отправки</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -82,4 +82,95 @@
|
|||||||
</plurals>
|
</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>
|
</resources>
|
||||||
|
@ -146,4 +146,26 @@
|
|||||||
<string name="verification_emoji_anchor">Spirancë</string>
|
<string name="verification_emoji_anchor">Spirancë</string>
|
||||||
<string name="verification_emoji_headphone">Kufje</string>
|
<string name="verification_emoji_headphone">Kufje</string>
|
||||||
<string name="verification_emoji_folder">Dosje</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>
|
</resources>
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<string name="notice_room_third_party_registered_invite">%1$s èt d’uutnodigienge vo %2$s anveird</string>
|
<string name="notice_room_third_party_registered_invite">%1$s èt d’uutnodigienge vo %2$s anveird</string>
|
||||||
|
|
||||||
<string name="notice_crypto_unable_to_decrypt">** Kun nie ountsleuteln: %s **</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 dit bericht gesteurd.</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="message_reply_to_prefix">Als antwoord ip</string>
|
<string name="message_reply_to_prefix">Als antwoord ip</string>
|
||||||
|
|
||||||
@ -150,21 +150,26 @@
|
|||||||
<string name="verification_emoji_folder">Mappe</string>
|
<string name="verification_emoji_folder">Mappe</string>
|
||||||
<string name="verification_emoji_pin">Pinne</string>
|
<string name="verification_emoji_pin">Pinne</string>
|
||||||
|
|
||||||
<string name="initial_sync_start_importing_account">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account">Initiële synchronisoasje:
|
||||||
\nAccount wor geïmporteerd…</string>
|
\nAccount wor geïmporteerd…</string>
|
||||||
<string name="initial_sync_start_importing_account_crypto">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_crypto">Initiële synchronisoasje:
|
||||||
\nCrypto wor geïmporteerd</string>
|
\nCrypto wor geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_rooms">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_rooms">Initiële synchronisoasje:
|
||||||
\nGesprekkn wordn geïmporteerd</string>
|
\nGesprekkn wordn geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisoasje:
|
||||||
\nDeelgenoomn gesprekken wordn geïmporteerd</string>
|
\nDeelgenoomn gesprekken wordn geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisoasje:
|
||||||
\nUutgenodigde gesprekkn wordn geïmporteerd</string>
|
\nUutgenodigde gesprekkn wordn geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisoasje:
|
||||||
\nVerloatn gesprekkn wordn geïmporteerd</string>
|
\nVerloatn gesprekkn wordn geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_groups">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_groups">Initiële synchronisoasje:
|
||||||
\nGemeenschappn wordn geïmporteerd</string>
|
\nGemeenschappn wordn geïmporteerd</string>
|
||||||
<string name="initial_sync_start_importing_account_data">Initiële synchronisoatie:
|
<string name="initial_sync_start_importing_account_data">Initiële synchronisoasje:
|
||||||
\nAccountgegeevns wordn geïmporteerd</string>
|
\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>
|
</resources>
|
||||||
|
@ -162,4 +162,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">初始化同步:
|
<string name="initial_sync_start_importing_account_data">初始化同步:
|
||||||
\n正在导入账号数据</string>
|
\n正在导入账号数据</string>
|
||||||
|
|
||||||
|
<string name="notice_room_update">%s 升级了聊天室。</string>
|
||||||
|
|
||||||
|
<string name="event_status_sending_message">正在发送消息…</string>
|
||||||
|
<string name="clear_timeline_send_queue">清除正在发送队列</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -165,4 +165,9 @@
|
|||||||
<string name="initial_sync_start_importing_account_data">初始化同步:
|
<string name="initial_sync_start_importing_account_data">初始化同步:
|
||||||
\n正在匯入帳號資料</string>
|
\n正在匯入帳號資料</string>
|
||||||
|
|
||||||
|
<string name="notice_room_update">%s 已升級此聊天室。</string>
|
||||||
|
|
||||||
|
<string name="event_status_sending_message">正在傳送訊息……</string>
|
||||||
|
<string name="clear_timeline_send_queue">清除傳送佇列</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -15,7 +15,7 @@ androidExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext.versionMajor = 0
|
ext.versionMajor = 0
|
||||||
ext.versionMinor = 3
|
ext.versionMinor = 5
|
||||||
ext.versionPatch = 0
|
ext.versionPatch = 0
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
@ -51,7 +51,7 @@ static def gitRevisionDate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static def gitBranchName() {
|
static def gitBranchName() {
|
||||||
def cmd = "git name-rev --name-only HEAD"
|
def cmd = "git rev-parse --abbrev-ref HEAD"
|
||||||
return cmd.execute().text.trim()
|
return cmd.execute().text.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +318,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'diff_match_patch:diff_match_patch:current'
|
implementation 'diff_match_patch:diff_match_patch:current'
|
||||||
|
|
||||||
|
implementation "androidx.emoji:emoji-appcompat:1.0.0"
|
||||||
|
|
||||||
// TESTS
|
// TESTS
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
|
67
vector/src/main/java/im/vector/riotx/EmojiCompatWrapper.kt
Normal file
67
vector/src/main/java/im/vector/riotx/EmojiCompatWrapper.kt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -66,6 +66,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
@Inject lateinit var authenticator: Authenticator
|
@Inject lateinit var authenticator: Authenticator
|
||||||
@Inject lateinit var vectorConfiguration: VectorConfiguration
|
@Inject lateinit var vectorConfiguration: VectorConfiguration
|
||||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
||||||
|
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
|
||||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||||
@ -85,9 +86,12 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
vectorComponent = DaggerVectorComponent.factory().create(this)
|
vectorComponent = DaggerVectorComponent.factory().create(this)
|
||||||
vectorComponent.inject(this)
|
vectorComponent.inject(this)
|
||||||
vectorUncaughtExceptionHandler.activate(this)
|
vectorUncaughtExceptionHandler.activate(this)
|
||||||
// Log
|
|
||||||
VectorFileLogger.init(this)
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(Timber.DebugTree(), VectorFileLogger)
|
Timber.plant(Timber.DebugTree())
|
||||||
|
}
|
||||||
|
Timber.plant(vectorComponent.vectorFileLogger())
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Stetho.initializeWithDefaults(this)
|
Stetho.initializeWithDefaults(this)
|
||||||
}
|
}
|
||||||
@ -105,6 +109,9 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
)
|
)
|
||||||
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
||||||
vectorConfiguration.initConfiguration()
|
vectorConfiguration.initConfiguration()
|
||||||
|
|
||||||
|
emojiCompatWrapper.init(fontRequest)
|
||||||
|
|
||||||
NotificationUtils.createNotificationChannels(applicationContext)
|
NotificationUtils.createNotificationChannels(applicationContext)
|
||||||
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
||||||
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
|
val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
|
||||||
|
@ -58,6 +58,7 @@ import im.vector.riotx.features.rageshake.BugReportActivity
|
|||||||
import im.vector.riotx.features.rageshake.BugReporter
|
import im.vector.riotx.features.rageshake.BugReporter
|
||||||
import im.vector.riotx.features.rageshake.RageShake
|
import im.vector.riotx.features.rageshake.RageShake
|
||||||
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
|
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.PublicRoomsFragment
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
|
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
|
||||||
@ -181,6 +182,8 @@ interface ScreenComponent {
|
|||||||
|
|
||||||
fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet)
|
fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet)
|
||||||
|
|
||||||
|
fun inject(reactionButton: ReactionButton)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(vectorComponent: VectorComponent,
|
fun create(vectorComponent: VectorComponent,
|
||||||
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.Matrix
|
|||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.Authenticator
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.riotx.EmojiCompatFontProvider
|
import im.vector.riotx.EmojiCompatFontProvider
|
||||||
|
import im.vector.riotx.EmojiCompatWrapper
|
||||||
import im.vector.riotx.VectorApplication
|
import im.vector.riotx.VectorApplication
|
||||||
import im.vector.riotx.core.pushers.PushersManager
|
import im.vector.riotx.core.pushers.PushersManager
|
||||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||||
@ -40,6 +41,7 @@ import im.vector.riotx.features.notifications.NotificationBroadcastReceiver
|
|||||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||||
import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
||||||
import im.vector.riotx.features.rageshake.BugReporter
|
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.rageshake.VectorUncaughtExceptionHandler
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -70,6 +72,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun emojiCompatFontProvider(): EmojiCompatFontProvider
|
fun emojiCompatFontProvider(): EmojiCompatFontProvider
|
||||||
|
|
||||||
|
fun emojiCompatWrapper() : EmojiCompatWrapper
|
||||||
|
|
||||||
fun eventHtmlRenderer(): EventHtmlRenderer
|
fun eventHtmlRenderer(): EventHtmlRenderer
|
||||||
|
|
||||||
fun navigator(): Navigator
|
fun navigator(): Navigator
|
||||||
@ -98,6 +102,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun vectorPreferences(): VectorPreferences
|
fun vectorPreferences(): VectorPreferences
|
||||||
|
|
||||||
|
fun vectorFileLogger(): VectorFileLogger
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(@BindsInstance context: Context): VectorComponent
|
fun create(@BindsInstance context: Context): VectorComponent
|
||||||
|
@ -21,11 +21,8 @@ import android.util.AttributeSet
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.core.view.isInvisible
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import butterknife.ButterKnife
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
||||||
import kotlinx.android.synthetic.main.view_read_receipts.view.*
|
import kotlinx.android.synthetic.main.view_read_receipts.view.*
|
||||||
@ -48,7 +45,6 @@ class ReadReceiptsView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private fun setupView() {
|
private fun setupView() {
|
||||||
inflate(context, R.layout.view_read_receipts, this)
|
inflate(context, R.layout.view_read_receipts, this)
|
||||||
ButterKnife.bind(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(readReceipts: List<ReadReceiptData>, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) {
|
fun render(readReceipts: List<ReadReceiptData>, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) {
|
||||||
|
@ -26,7 +26,6 @@ import androidx.appcompat.widget.Toolbar
|
|||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
@ -36,12 +35,10 @@ import im.vector.riotx.core.di.ScreenComponent
|
|||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.hideKeyboard
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.extensions.replaceFragment
|
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.ToolbarConfigurable
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.core.pushers.PushersManager
|
import im.vector.riotx.core.pushers.PushersManager
|
||||||
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
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.notifications.NotificationDrawerManager
|
||||||
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||||
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
||||||
@ -119,22 +116,22 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
activeSessionHolder.getSafeActiveSession()?.getLiveStatus()?.observe(this, Observer { sprogress ->
|
activeSessionHolder.getSafeActiveSession()?.getInitialSyncProgressStatus()?.observe(this, Observer { status ->
|
||||||
Timber.e("${sprogress?.statusText?.let { getString(it) }} ${sprogress?.percentProgress}")
|
if (status == null) {
|
||||||
if (sprogress == null) {
|
|
||||||
waiting_view.isVisible = false
|
waiting_view.isVisible = false
|
||||||
} else {
|
} else {
|
||||||
|
Timber.e("${getString(status.statusText)} ${status.percentProgress}")
|
||||||
waiting_view.setOnClickListener {
|
waiting_view.setOnClickListener {
|
||||||
//block interactions
|
//block interactions
|
||||||
}
|
}
|
||||||
waiting_view_status_horizontal_progress.apply {
|
waiting_view_status_horizontal_progress.apply {
|
||||||
isIndeterminate = false
|
isIndeterminate = false
|
||||||
max = 100
|
max = 100
|
||||||
progress = sprogress.percentProgress
|
progress = status.percentProgress
|
||||||
isVisible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
waiting_view_status_text.apply {
|
waiting_view_status_text.apply {
|
||||||
text = sprogress.statusText?.let { getString(it) }
|
text = getString(status.statusText)
|
||||||
isVisible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
waiting_view.isVisible = true
|
waiting_view.isVisible = true
|
||||||
@ -213,8 +210,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_CLEAR_EXISTING_NOTIFICATION = "EXTRA_CLEAR_EXISTING_NOTIFICATION"
|
private const val EXTRA_CLEAR_EXISTING_NOTIFICATION = "EXTRA_CLEAR_EXISTING_NOTIFICATION"
|
||||||
|
|
||||||
|
@ -191,12 +191,13 @@ class RoomMessageTouchHelperCallback(private val context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val y = (itemView.top + itemView.measuredHeight / 2).toFloat()
|
val y = (itemView.top + itemView.measuredHeight / 2).toFloat()
|
||||||
//magic numbers?
|
val hw = imageDrawable.intrinsicWidth / 2f
|
||||||
|
val hh = imageDrawable.intrinsicHeight / 2f
|
||||||
imageDrawable.setBounds(
|
imageDrawable.setBounds(
|
||||||
(x - convertToPx(12) * scale).toInt(),
|
(x - hw * scale).toInt(),
|
||||||
(y - convertToPx(11) * scale).toInt(),
|
(y - hh * scale).toInt(),
|
||||||
(x + convertToPx(12) * scale).toInt(),
|
(x + hw * scale).toInt(),
|
||||||
(y + convertToPx(10) * scale).toInt()
|
(y + hh * scale).toInt()
|
||||||
)
|
)
|
||||||
imageDrawable.draw(canvas)
|
imageDrawable.draw(canvas)
|
||||||
imageDrawable.alpha = 255
|
imageDrawable.alpha = 255
|
||||||
|
@ -69,7 +69,7 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
|
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
|
||||||
LinearLayout.VERTICAL)
|
LinearLayout.VERTICAL)
|
||||||
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
||||||
bottomSheetTitle.text = getString(R.string.read_receipts_list)
|
bottomSheetTitle.text = getString(R.string.read_at)
|
||||||
epoxyController.setData(displayReadReceiptArgs.readReceipts)
|
epoxyController.setData(displayReadReceiptArgs.readReceipts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.extensions.canReact
|
import im.vector.riotx.core.extensions.canReact
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.core.utils.isSingleEmoji
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
|
||||||
|
|
||||||
@ -244,7 +243,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
|||||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||||
//TODO if user is admin or moderator
|
//TODO if user is admin or moderator
|
||||||
return event.annotations?.reactionsSummary?.any { isSingleEmoji(it.key) } ?: false
|
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,12 +38,8 @@ abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleI
|
|||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var timeStamp: CharSequence? = null
|
var timeStamp: CharSequence? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
|
||||||
var emojiTypeFace: Typeface? = null
|
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.emojiReactionView.text = reactionKey
|
holder.emojiReactionView.text = reactionKey
|
||||||
holder.emojiReactionView.typeface = emojiTypeFace ?: Typeface.DEFAULT
|
|
||||||
holder.displayNameView.text = authorDisplayName
|
holder.displayNameView.text = authorDisplayName
|
||||||
timeStamp?.let {
|
timeStamp?.let {
|
||||||
holder.timeStampView.text = it
|
holder.timeStampView.text = it
|
||||||
|
@ -113,7 +113,7 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
when (it.operation) {
|
when (it.operation) {
|
||||||
diff_match_patch.Operation.DELETE -> {
|
diff_match_patch.Operation.DELETE -> {
|
||||||
span {
|
span {
|
||||||
text = it.text
|
text = it.text.replace("\n"," ")
|
||||||
textColor = ContextCompat.getColor(context, R.color.vector_error_color)
|
textColor = ContextCompat.getColor(context, R.color.vector_error_color)
|
||||||
textDecorationLine = "line-through"
|
textDecorationLine = "line-through"
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import com.airbnb.epoxy.EpoxyRecyclerView
|
|||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.EmojiCompatFontProvider
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
@ -43,14 +42,11 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
private val viewModel: ViewReactionViewModel by fragmentViewModel(ViewReactionViewModel::class)
|
private val viewModel: ViewReactionViewModel by fragmentViewModel(ViewReactionViewModel::class)
|
||||||
|
|
||||||
@Inject lateinit var viewReactionViewModelFactory: ViewReactionViewModel.Factory
|
@Inject lateinit var viewReactionViewModelFactory: ViewReactionViewModel.Factory
|
||||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
|
||||||
|
|
||||||
@BindView(R.id.bottom_sheet_display_reactions_list)
|
@BindView(R.id.bottom_sheet_display_reactions_list)
|
||||||
lateinit var epoxyRecyclerView: EpoxyRecyclerView
|
lateinit var epoxyRecyclerView: EpoxyRecyclerView
|
||||||
|
|
||||||
private val epoxyController by lazy {
|
@Inject lateinit var epoxyController: ViewReactionsEpoxyController
|
||||||
ViewReactionsEpoxyController(requireContext(), emojiCompatFontProvider.typeface)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(screenComponent: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
screenComponent.inject(this)
|
||||||
|
@ -90,7 +90,7 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
|
|||||||
.flatMapSingle { summaries ->
|
.flatMapSingle { summaries ->
|
||||||
Observable
|
Observable
|
||||||
.fromIterable(summaries.reactionsSummary)
|
.fromIterable(summaries.reactionsSummary)
|
||||||
.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
//.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
||||||
.toReactionInfoList()
|
.toReactionInfoList()
|
||||||
}
|
}
|
||||||
.execute {
|
.execute {
|
||||||
|
@ -17,20 +17,23 @@
|
|||||||
package im.vector.riotx.features.home.room.detail.timeline.action
|
package im.vector.riotx.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.format.DateUtils
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Incomplete
|
import com.airbnb.mvrx.Incomplete
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
|
import im.vector.riotx.EmojiCompatWrapper
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
import im.vector.riotx.core.ui.list.genericLoaderItem
|
import im.vector.riotx.core.ui.list.genericLoaderItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Epoxy controller for reaction event list
|
* Epoxy controller for reaction event list
|
||||||
*/
|
*/
|
||||||
class ViewReactionsEpoxyController(private val context: Context, private val emojiCompatTypeface: Typeface?)
|
class ViewReactionsEpoxyController @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val emojiCompatWrapper: EmojiCompatWrapper )
|
||||||
: TypedEpoxyController<DisplayReactionsViewState>() {
|
: TypedEpoxyController<DisplayReactionsViewState>() {
|
||||||
|
|
||||||
override fun buildModels(state: DisplayReactionsViewState) {
|
override fun buildModels(state: DisplayReactionsViewState) {
|
||||||
@ -43,16 +46,15 @@ class ViewReactionsEpoxyController(private val context: Context, private val emo
|
|||||||
is Fail -> {
|
is Fail -> {
|
||||||
genericFooterItem {
|
genericFooterItem {
|
||||||
id("failure")
|
id("failure")
|
||||||
text(context.getString(R.string.unknown_error))
|
text(stringProvider.getString(R.string.unknown_error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Success -> {
|
is Success -> {
|
||||||
state.mapReactionKeyToMemberList()?.forEach {
|
state.mapReactionKeyToMemberList()?.forEach {
|
||||||
reactionInfoSimpleItem {
|
reactionInfoSimpleItem {
|
||||||
id(it.eventId)
|
id(it.eventId)
|
||||||
emojiTypeFace(emojiCompatTypeface)
|
|
||||||
timeStamp(it.timestamp)
|
timeStamp(it.timestamp)
|
||||||
reactionKey(it.reactionKey)
|
reactionKey(emojiCompatWrapper.safeEmojiSpanify(it.reactionKey))
|
||||||
authorDisplayName(it.authorName ?: it.authorId)
|
authorDisplayName(it.authorName ?: it.authorId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,35 @@
|
|||||||
package im.vector.riotx.features.home.room.detail.timeline.factory
|
package im.vector.riotx.features.home.room.detail.timeline.factory
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
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.item.DefaultItem
|
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.DefaultItem_
|
||||||
|
import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DefaultItemFactory @Inject constructor(){
|
class DefaultItemFactory @Inject constructor(private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val informationDataFactory: MessageInformationDataFactory) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent, highlight: Boolean, exception: Exception? = null): DefaultItem? {
|
fun create(event: TimelineEvent,
|
||||||
|
highlight: Boolean,
|
||||||
|
callback: TimelineEventController.Callback?,
|
||||||
|
exception: Exception? = null): DefaultItem? {
|
||||||
val text = if (exception == null) {
|
val text = if (exception == null) {
|
||||||
"${event.root.getClearType()} events are not yet handled"
|
"${event.root.getClearType()} events are not yet handled"
|
||||||
} else {
|
} else {
|
||||||
"an exception occurred when rendering the event ${event.root.eventId}"
|
"an exception occurred when rendering the event ${event.root.eventId}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val informationData = informationDataFactory.create(event, null)
|
||||||
|
|
||||||
return DefaultItem_()
|
return DefaultItem_()
|
||||||
.text(text)
|
.text(text)
|
||||||
|
.avatarRenderer(avatarRenderer)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
|
.informationData(informationData)
|
||||||
|
.baseCallback(callback)
|
||||||
|
.readReceiptsCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -28,15 +28,7 @@ import im.vector.matrix.android.api.permalinks.MatrixLinkify
|
|||||||
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
|
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.RelationType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
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.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||||
@ -47,26 +39,12 @@ import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
|||||||
import im.vector.riotx.core.linkify.VectorLinkify
|
import im.vector.riotx.core.linkify.VectorLinkify
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
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.core.utils.DebouncedClickListener
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
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.TimelineEventController
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
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.helper.TimelineMediaSizeProvider
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.senderAvatar
|
import im.vector.riotx.features.home.room.detail.timeline.item.*
|
||||||
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.home.room.detail.timeline.util.MessageInformationDataFactory
|
||||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||||
import im.vector.riotx.features.media.ImageContentRenderer
|
import im.vector.riotx.features.media.ImageContentRenderer
|
||||||
@ -104,31 +82,27 @@ class MessageItemFactory @Inject constructor(
|
|||||||
val messageContent: MessageContent =
|
val messageContent: MessageContent =
|
||||||
event.getLastMessageContent()
|
event.getLastMessageContent()
|
||||||
?: //Malformed content, we should echo something on screen
|
?: //Malformed content, we should echo something on screen
|
||||||
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
|
return buildNotHandledMessageItem(stringProvider.getString(R.string.malformed_message),
|
||||||
|
informationData, highlight, callback)
|
||||||
|
|
||||||
if (messageContent.relatesTo?.type == RelationType.REPLACE
|
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 it when debugging as a notice event
|
// This is an edit event, we should display it when debugging as a notice event
|
||||||
return noticeItemFactory.create(event, highlight, callback)
|
return noticeItemFactory.create(event, highlight, callback)
|
||||||
}
|
}
|
||||||
// val all = event.root.toContent()
|
// val all = event.root.toContent()
|
||||||
// val ev = all.toModel<Event>()
|
// val ev = all.toModel<Event>()
|
||||||
return when (messageContent) {
|
return when (messageContent) {
|
||||||
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
|
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, highlight, callback)
|
||||||
informationData,
|
is MessageTextContent -> buildTextMessageItem(messageContent, informationData, highlight, callback)
|
||||||
highlight,
|
|
||||||
callback)
|
|
||||||
is MessageTextContent -> buildTextMessageItem(messageContent,
|
|
||||||
informationData,
|
|
||||||
highlight,
|
|
||||||
callback)
|
|
||||||
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, highlight, callback)
|
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, highlight, callback)
|
||||||
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback)
|
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback)
|
||||||
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback)
|
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback)
|
||||||
is MessageFileContent -> buildFileMessageItem(messageContent, informationData, highlight, callback)
|
is MessageFileContent -> buildFileMessageItem(messageContent, informationData, highlight, callback)
|
||||||
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, callback)
|
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, callback)
|
||||||
else -> buildNotHandledMessageItem(messageContent, highlight)
|
else -> buildNotHandledMessageItem("${messageContent.type} message events are not yet handled",
|
||||||
|
informationData, highlight, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,11 +164,17 @@ class MessageItemFactory @Inject constructor(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNotHandledMessageItem(messageContent: MessageContent, highlight: Boolean): DefaultItem? {
|
private fun buildNotHandledMessageItem(text: String,
|
||||||
val text = "${messageContent.type} message events are not yet handled"
|
informationData: MessageInformationData,
|
||||||
|
highlight: Boolean,
|
||||||
|
callback: TimelineEventController.Callback?): DefaultItem? {
|
||||||
return DefaultItem_()
|
return DefaultItem_()
|
||||||
.text(text)
|
.text(text)
|
||||||
|
.avatarRenderer(avatarRenderer)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
|
.informationData(informationData)
|
||||||
|
.baseCallback(callback)
|
||||||
|
.readReceiptsCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildImageMessageItem(messageContent: MessageImageContent,
|
private fun buildImageMessageItem(messageContent: MessageImageContent,
|
||||||
|
@ -20,12 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotx.core.epoxy.EmptyItem_
|
import im.vector.riotx.core.epoxy.EmptyItem_
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
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.TimelineEventController
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.senderAvatar
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -71,7 +66,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||||||
|
|
||||||
// Unhandled event types (yet)
|
// Unhandled event types (yet)
|
||||||
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
|
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
|
||||||
EventType.STICKER -> defaultItemFactory.create(event, highlight)
|
EventType.STICKER -> defaultItemFactory.create(event, highlight, callback)
|
||||||
else -> {
|
else -> {
|
||||||
Timber.v("Type ${event.root.getClearType()} not handled")
|
Timber.v("Type ${event.root.getClearType()} not handled")
|
||||||
null
|
null
|
||||||
@ -79,7 +74,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "failed to create message item")
|
Timber.e(e, "failed to create message item")
|
||||||
defaultItemFactory.create(event, highlight, e)
|
defaultItemFactory.create(event, highlight, callback, e)
|
||||||
}
|
}
|
||||||
return (computedModel ?: EmptyItem_())
|
return (computedModel ?: EmptyItem_())
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.format
|
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.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
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.events.model.toModel
|
||||||
@ -24,12 +23,14 @@ 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.model.call.CallInviteContent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.senderName
|
import im.vector.riotx.features.home.room.detail.timeline.helper.senderName
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NoticeEventFormatter @Inject constructor(private val stringProvider: StringProvider) {
|
class NoticeEventFormatter @Inject constructor(private val sessionHolder: ActiveSessionHolder,
|
||||||
|
private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun format(timelineEvent: TimelineEvent): CharSequence? {
|
fun format(timelineEvent: TimelineEvent): CharSequence? {
|
||||||
return when (val type = timelineEvent.root.getClearType()) {
|
return when (val type = timelineEvent.root.getClearType()) {
|
||||||
@ -74,10 +75,10 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
|
|
||||||
private fun formatRoomNameEvent(event: Event, senderName: String?): CharSequence? {
|
private fun formatRoomNameEvent(event: Event, senderName: String?): CharSequence? {
|
||||||
val content = event.getClearContent().toModel<RoomNameContent>() ?: return null
|
val content = event.getClearContent().toModel<RoomNameContent>() ?: return null
|
||||||
return if (!TextUtils.isEmpty(content.name)) {
|
return if (content.name.isNullOrBlank()) {
|
||||||
stringProvider.getString(R.string.notice_room_name_changed, senderName, content.name)
|
|
||||||
} else {
|
|
||||||
stringProvider.getString(R.string.notice_room_name_removed, senderName)
|
stringProvider.getString(R.string.notice_room_name_removed, senderName)
|
||||||
|
} else {
|
||||||
|
stringProvider.getString(R.string.notice_room_name_changed, senderName, content.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,8 +96,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? {
|
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? {
|
||||||
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility ?: return null
|
||||||
?: return null
|
|
||||||
|
|
||||||
val formattedVisibility = when (historyVisibility) {
|
val formattedVisibility = when (historyVisibility) {
|
||||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||||
@ -138,7 +138,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||||
val displayText = StringBuilder()
|
val displayText = StringBuilder()
|
||||||
// Check display name has been changed
|
// Check display name has been changed
|
||||||
if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) {
|
if (eventContent?.displayName != prevEventContent?.displayName) {
|
||||||
val displayNameText = when {
|
val displayNameText = when {
|
||||||
prevEventContent?.displayName.isNullOrEmpty() ->
|
prevEventContent?.displayName.isNullOrEmpty() ->
|
||||||
stringProvider.getString(R.string.notice_display_name_set, event.senderId, eventContent?.displayName)
|
stringProvider.getString(R.string.notice_display_name_set, event.senderId, eventContent?.displayName)
|
||||||
@ -151,7 +151,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
displayText.append(displayNameText)
|
displayText.append(displayNameText)
|
||||||
}
|
}
|
||||||
// Check whether the avatar has been changed
|
// Check whether the avatar has been changed
|
||||||
if (!TextUtils.equals(eventContent?.avatarUrl, prevEventContent?.avatarUrl)) {
|
if (eventContent?.avatarUrl != prevEventContent?.avatarUrl) {
|
||||||
val displayAvatarText = if (displayText.isNotEmpty()) {
|
val displayAvatarText = if (displayText.isNotEmpty()) {
|
||||||
displayText.append(" ")
|
displayText.append(" ")
|
||||||
stringProvider.getString(R.string.notice_avatar_changed_too)
|
stringProvider.getString(R.string.notice_avatar_changed_too)
|
||||||
@ -168,13 +168,14 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
val targetDisplayName = eventContent?.displayName ?: prevEventContent?.displayName ?: ""
|
val targetDisplayName = eventContent?.displayName ?: prevEventContent?.displayName ?: ""
|
||||||
return when {
|
return when {
|
||||||
Membership.INVITE == eventContent?.membership -> {
|
Membership.INVITE == eventContent?.membership -> {
|
||||||
// TODO get userId
|
val selfUserId = sessionHolder.getSafeActiveSession()?.myUserId
|
||||||
val selfUserId = ""
|
|
||||||
when {
|
when {
|
||||||
eventContent.thirdPartyInvite != null ->
|
eventContent.thirdPartyInvite != null -> {
|
||||||
|
val userWhoHasAccepted = eventContent.thirdPartyInvite?.signed?.mxid ?: event.stateKey
|
||||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
userWhoHasAccepted, eventContent.thirdPartyInvite?.displayName)
|
||||||
TextUtils.equals(event.stateKey, selfUserId) ->
|
}
|
||||||
|
event.stateKey == selfUserId ->
|
||||||
stringProvider.getString(R.string.notice_room_invite_you, senderDisplayName)
|
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)
|
stringProvider.getString(R.string.notice_room_invite_no_invitee, senderDisplayName)
|
||||||
@ -186,7 +187,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
|
|||||||
stringProvider.getString(R.string.notice_room_join, senderDisplayName)
|
stringProvider.getString(R.string.notice_room_join, senderDisplayName)
|
||||||
Membership.LEAVE == eventContent?.membership ->
|
Membership.LEAVE == eventContent?.membership ->
|
||||||
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
|
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
|
||||||
return if (TextUtils.equals(event.senderId, event.stateKey)) {
|
return if (event.senderId == event.stateKey) {
|
||||||
if (prevEventContent?.membership == Membership.INVITE) {
|
if (prevEventContent?.membership == Membership.INVITE) {
|
||||||
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
||||||
} else {
|
} else {
|
||||||
|
@ -56,7 +56,8 @@ fun TimelineEvent.isDisplayable(showHiddenEvent: Boolean): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (root.content.isNullOrEmpty()) {
|
if (root.content.isNullOrEmpty()) {
|
||||||
return false
|
//redacted events have empty content but are displayable
|
||||||
|
return root.unsignedData?.redactedEvent != null
|
||||||
}
|
}
|
||||||
//Edits should be filtered out!
|
//Edits should be filtered out!
|
||||||
if (EventType.MESSAGE == root.type
|
if (EventType.MESSAGE == root.type
|
||||||
|
@ -32,7 +32,6 @@ import com.airbnb.epoxy.EpoxyAttribute
|
|||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.ui.views.ReadReceiptsView
|
|
||||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||||
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
|
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
@ -151,7 +150,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
|||||||
idToRefInFlow.add(reactionButton.id)
|
idToRefInFlow.add(reactionButton.id)
|
||||||
reactionButton.reactionString = reaction.key
|
reactionButton.reactionString = reaction.key
|
||||||
reactionButton.reactionCount = reaction.count
|
reactionButton.reactionCount = reaction.count
|
||||||
reactionButton.emojiTypeFace = emojiTypeFace
|
//reactionButton.emojiTypeFace = emojiTypeFace
|
||||||
reactionButton.setChecked(reaction.addedByMe)
|
reactionButton.setChecked(reaction.addedByMe)
|
||||||
reactionButton.isEnabled = reaction.synced
|
reactionButton.isEnabled = reaction.synced
|
||||||
}
|
}
|
||||||
@ -181,7 +180,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
|||||||
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||||
val timeView by bind<TextView>(R.id.messageTimeView)
|
val timeView by bind<TextView>(R.id.messageTimeView)
|
||||||
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
|
||||||
var reactionWrapper: ViewGroup? = null
|
var reactionWrapper: ViewGroup? = null
|
||||||
var reactionFlowHelper: Flow? = null
|
var reactionFlowHelper: Flow? = null
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotx.core.platform.CheckableView
|
import im.vector.riotx.core.platform.CheckableView
|
||||||
|
import im.vector.riotx.core.ui.views.ReadReceiptsView
|
||||||
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
|
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,6 +50,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
|||||||
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
||||||
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)
|
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)
|
||||||
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
||||||
|
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
||||||
|
|
||||||
override fun bindView(itemView: View) {
|
override fun bindView(itemView: View) {
|
||||||
super.bindView(itemView)
|
super.bindView(itemView)
|
||||||
|
@ -16,19 +16,46 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.item
|
package im.vector.riotx.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||||
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
|
||||||
abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
|
abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var informationData: MessageInformationData
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var baseCallback: TimelineEventController.BaseCallback? = null
|
||||||
|
|
||||||
|
private var longClickListener = View.OnLongClickListener {
|
||||||
|
return@OnLongClickListener baseCallback?.onEventLongClicked(informationData, null, it) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null
|
||||||
|
|
||||||
|
private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
|
||||||
|
readReceiptsCallback?.onReadReceiptsClicked(informationData.readReceipts)
|
||||||
|
})
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var text: CharSequence? = null
|
var text: CharSequence? = null
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.messageView.text = text
|
holder.messageView.text = text
|
||||||
|
|
||||||
|
holder.view.setOnLongClickListener(longClickListener)
|
||||||
|
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getViewType() = STUB_ID
|
override fun getViewType() = STUB_ID
|
||||||
|
@ -21,6 +21,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
|
||||||
@ -75,6 +76,9 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
|
|||||||
holder.separatorView.visibility = View.VISIBLE
|
holder.separatorView.visibility = View.VISIBLE
|
||||||
holder.expandView.setText(R.string.merged_events_collapse)
|
holder.expandView.setText(R.string.merged_events_collapse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No read receipt for this item
|
||||||
|
holder.readReceiptsView.isVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Data(
|
data class Data(
|
||||||
|
@ -22,7 +22,6 @@ import android.widget.TextView
|
|||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.ui.views.ReadReceiptsView
|
|
||||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
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.TimelineEventController
|
||||||
@ -72,7 +71,6 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
|
|||||||
class Holder : BaseHolder(STUB_ID) {
|
class Holder : BaseHolder(STUB_ID) {
|
||||||
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
|
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
|
||||||
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
|
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
|
||||||
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -71,7 +71,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
|||||||
memberName = formattedMemberName,
|
memberName = formattedMemberName,
|
||||||
showInformation = showInformation,
|
showInformation = showInformation,
|
||||||
orderedReactionList = event.annotations?.reactionsSummary
|
orderedReactionList = event.annotations?.reactionsSummary
|
||||||
?.filter { isSingleEmoji(it.key) }
|
//?.filter { isSingleEmoji(it.key) }
|
||||||
?.map {
|
?.map {
|
||||||
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
||||||
},
|
},
|
||||||
|
@ -89,19 +89,45 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
thumbnailView.isVisible = false
|
|
||||||
loadingView.isVisible = false
|
|
||||||
|
|
||||||
val resolvedUrl = contentUrlResolver.resolveFullSize(data.url)
|
val resolvedUrl = contentUrlResolver.resolveFullSize(data.url)
|
||||||
|
|
||||||
if (resolvedUrl == null) {
|
if (resolvedUrl == null) {
|
||||||
|
thumbnailView.isVisible = false
|
||||||
|
loadingView.isVisible = false
|
||||||
errorView.isVisible = true
|
errorView.isVisible = true
|
||||||
errorView.setText(R.string.unknown_error)
|
errorView.setText(R.string.unknown_error)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
//Temporary code, some remote videos are not played by videoview setVideoUri
|
||||||
|
//So for now we download them then play
|
||||||
|
thumbnailView.isVisible = true
|
||||||
|
loadingView.isVisible = true
|
||||||
|
|
||||||
|
activeSessionHolder.getActiveSession()
|
||||||
|
.downloadFile(
|
||||||
|
FileService.DownloadMode.FOR_INTERNAL_USE,
|
||||||
|
data.eventId,
|
||||||
|
data.filename,
|
||||||
|
data.url,
|
||||||
|
null,
|
||||||
|
object : MatrixCallback<File> {
|
||||||
|
override fun onSuccess(data: File) {
|
||||||
|
thumbnailView.isVisible = false
|
||||||
|
loadingView.isVisible = false
|
||||||
videoView.isVisible = true
|
videoView.isVisible = true
|
||||||
videoView.setVideoPath(resolvedUrl)
|
|
||||||
|
videoView.setVideoPath(data.path)
|
||||||
videoView.start()
|
videoView.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
loadingView.isVisible = false
|
||||||
|
errorView.isVisible = true
|
||||||
|
errorView.text = errorFormatter.toHumanReadable(failure)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class PushRuleTriggerListener @Inject constructor(
|
|||||||
//TODO
|
//TODO
|
||||||
} else {
|
} else {
|
||||||
notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||||
Timber.v("New event to notify $notifiableEvent tweaks:$notificationAction")
|
Timber.v("New event to notify")
|
||||||
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,7 +52,8 @@ import javax.inject.Singleton
|
|||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||||
private val versionProvider: VersionProvider) {
|
private val versionProvider: VersionProvider,
|
||||||
|
private val vectorFileLogger : VectorFileLogger) {
|
||||||
var inMultiWindowMode = false
|
var inMultiWindowMode = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -162,7 +163,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||||||
val gzippedFiles = ArrayList<File>()
|
val gzippedFiles = ArrayList<File>()
|
||||||
|
|
||||||
if (withDevicesLogs) {
|
if (withDevicesLogs) {
|
||||||
val files = VectorFileLogger.getLogFiles()
|
val files = vectorFileLogger.getLogFiles()
|
||||||
|
|
||||||
for (f in files) {
|
for (f in files) {
|
||||||
if (!mIsCancelled) {
|
if (!mIsCancelled) {
|
||||||
@ -348,20 +349,20 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||||||
} else if (null == response || null == response.body()) {
|
} else if (null == response || null == response.body()) {
|
||||||
serverError = "Failed with error $responseCode"
|
serverError = "Failed with error $responseCode"
|
||||||
} else {
|
} else {
|
||||||
var `is`: InputStream? = null
|
var inputStream: InputStream? = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
`is` = response.body()!!.byteStream()
|
inputStream = response.body()!!.byteStream()
|
||||||
|
|
||||||
if (null != `is`) {
|
if (null != inputStream) {
|
||||||
var ch = `is`.read()
|
var ch = inputStream.read()
|
||||||
val b = StringBuilder()
|
val b = StringBuilder()
|
||||||
while (ch != -1) {
|
while (ch != -1) {
|
||||||
b.append(ch.toChar())
|
b.append(ch.toChar())
|
||||||
ch = `is`.read()
|
ch = inputStream.read()
|
||||||
}
|
}
|
||||||
serverError = b.toString()
|
serverError = b.toString()
|
||||||
`is`.close()
|
inputStream.close()
|
||||||
|
|
||||||
// check if the error message
|
// check if the error message
|
||||||
try {
|
try {
|
||||||
@ -380,7 +381,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||||||
Timber.e(e, "## sendBugReport() : failed to parse error " + e.message)
|
Timber.e(e, "## sendBugReport() : failed to parse error " + e.message)
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
`is`?.close()
|
inputStream?.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## sendBugReport() : failed to close the error stream " + e.message)
|
Timber.e(e, "## sendBugReport() : failed to close the error stream " + e.message)
|
||||||
}
|
}
|
||||||
|
@ -17,43 +17,74 @@
|
|||||||
package im.vector.riotx.features.rageshake
|
package im.vector.riotx.features.rageshake
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
import android.util.Log
|
||||||
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.logging.*
|
import java.util.logging.*
|
||||||
import java.util.logging.Formatter
|
import java.util.logging.Formatter
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
object VectorFileLogger : Timber.DebugTree() {
|
private const val LOG_SIZE_BYTES = 20 * 1024 * 1024 // 20MB
|
||||||
|
|
||||||
private const val LOG_SIZE_BYTES = 50 * 1024 * 1024 // 50MB
|
private const val LOG_ROTATION_COUNT = 3
|
||||||
|
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class VectorFileLogger @Inject constructor(val context: Context, private val vectorPreferences: VectorPreferences) : Timber.DebugTree() {
|
||||||
|
|
||||||
// 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 val sLogger = Logger.getLogger("im.vector.riotx")
|
||||||
private lateinit var sFileHandler: FileHandler
|
private var sFileHandler: FileHandler? = null
|
||||||
private lateinit var sCacheDirectory: File
|
private var sCacheDirectory: File? = null
|
||||||
private var sFileName = "riotx"
|
private var sFileName = "riotxlogs"
|
||||||
|
|
||||||
fun init(context: Context) {
|
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 {
|
||||||
val logsDirectoryFile = context.cacheDir.absolutePath + "/logs"
|
val logsDirectoryFile = context.cacheDir.absolutePath + "/logs"
|
||||||
|
|
||||||
setLogDirectory(File(logsDirectoryFile))
|
setLogDirectory(File(logsDirectoryFile))
|
||||||
init("RiotXLog")
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||||
|
if (sFileHandler == null) return
|
||||||
|
if (skipLog(priority)) return
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
logToFile(t)
|
logToFile(t)
|
||||||
}
|
}
|
||||||
|
logToFile(prioPrefixes[priority] ?: "$priority ", tag ?: "Tag", message)
|
||||||
|
}
|
||||||
|
|
||||||
logToFile("$priority ", tag ?: "Tag", message)
|
private fun skipLog(priority: Int): Boolean {
|
||||||
|
return if (vectorPreferences.labAllowedExtendedLogging()) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
priority < Log.ERROR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,24 +99,6 @@ object VectorFileLogger : Timber.DebugTree() {
|
|||||||
sCacheDirectory = cacheDir
|
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.
|
* Adds our own log files to the provided list of files.
|
||||||
@ -99,8 +112,8 @@ object VectorFileLogger : Timber.DebugTree() {
|
|||||||
try {
|
try {
|
||||||
// reported by GA
|
// reported by GA
|
||||||
if (null != sFileHandler) {
|
if (null != sFileHandler) {
|
||||||
sFileHandler.flush()
|
sFileHandler!!.flush()
|
||||||
val absPath = sCacheDirectory.absolutePath
|
val absPath = sCacheDirectory?.absolutePath ?: return emptyList()
|
||||||
|
|
||||||
for (i in 0..LOG_ROTATION_COUNT) {
|
for (i in 0..LOG_ROTATION_COUNT) {
|
||||||
val filepath = "$absPath/$sFileName.$i.txt"
|
val filepath = "$absPath/$sFileName.$i.txt"
|
||||||
@ -111,7 +124,7 @@ object VectorFileLogger : Timber.DebugTree() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## addLogFiles() failed : " + e.message)
|
Timber.e(e, "## addLogFiles() failed : %s", e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return files
|
return files
|
||||||
|
@ -21,7 +21,6 @@ import android.animation.AnimatorSet
|
|||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.TypedArray
|
import android.content.res.TypedArray
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -35,8 +34,11 @@ import android.widget.TextView
|
|||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import im.vector.riotx.EmojiCompatWrapper
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.di.HasScreenInjector
|
||||||
import im.vector.riotx.core.utils.TextUtils
|
import im.vector.riotx.core.utils.TextUtils
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An animated reaction button.
|
* An animated reaction button.
|
||||||
@ -46,6 +48,12 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
|||||||
defStyleAttr: Int = 0)
|
defStyleAttr: Int = 0)
|
||||||
: FrameLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
|
: FrameLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (context is HasScreenInjector) {
|
||||||
|
context.injector().inject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator()
|
private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator()
|
||||||
private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator()
|
private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator()
|
||||||
@ -53,17 +61,13 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
|
||||||
|
|
||||||
private var emojiView: TextView? = null
|
private var emojiView: TextView? = null
|
||||||
private var countTextView: TextView? = null
|
private var countTextView: TextView? = null
|
||||||
|
|
||||||
private var reactionSelector: View? = null
|
private var reactionSelector: View? = null
|
||||||
|
|
||||||
var emojiTypeFace: Typeface? = null
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
emojiView?.typeface = value ?: Typeface.DEFAULT
|
|
||||||
}
|
|
||||||
|
|
||||||
private var dotsView: DotsView
|
private var dotsView: DotsView
|
||||||
private var circleView: CircleView
|
private var circleView: CircleView
|
||||||
var reactedListener: ReactedListener? = null
|
var reactedListener: ReactedListener? = null
|
||||||
@ -82,7 +86,9 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
|||||||
var reactionString = "😀"
|
var reactionString = "😀"
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
emojiView?.text = field
|
//maybe cache this for performances?
|
||||||
|
val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value)
|
||||||
|
emojiView?.text = emojiSpanned
|
||||||
}
|
}
|
||||||
|
|
||||||
private var animationScaleFactor: Float = 0.toFloat()
|
private var animationScaleFactor: Float = 0.toFloat()
|
||||||
@ -104,7 +110,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
|||||||
|
|
||||||
countTextView?.text = TextUtils.formatCountToShortDecimal(reactionCount)
|
countTextView?.text = TextUtils.formatCountToShortDecimal(reactionCount)
|
||||||
|
|
||||||
emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT
|
// emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT
|
||||||
|
|
||||||
val array = context.obtainStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr, 0)
|
val array = context.obtainStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr, 0)
|
||||||
|
|
||||||
|
@ -149,6 +149,8 @@ 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_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"
|
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_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"
|
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||||
|
|
||||||
@ -257,6 +259,10 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||||||
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true)
|
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.
|
* Tells if we have already asked the user to disable battery optimisations on android >= M devices.
|
||||||
*
|
*
|
||||||
|
6
vector/src/main/res/drawable/pill_receipt_black.xml
Normal file
6
vector/src/main/res/drawable/pill_receipt_black.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
<solid android:color="@color/riotx_header_panel_border_mobile_black" />
|
||||||
|
</shape>
|
6
vector/src/main/res/drawable/pill_receipt_dark.xml
Normal file
6
vector/src/main/res/drawable/pill_receipt_dark.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
<solid android:color="@color/riotx_header_panel_border_mobile_dark" />
|
||||||
|
</shape>
|
6
vector/src/main/res/drawable/pill_receipt_light.xml
Normal file
6
vector/src/main/res/drawable/pill_receipt_light.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
<solid android:color="@color/riotx_header_panel_border_mobile_light" />
|
||||||
|
</shape>
|
@ -34,7 +34,9 @@
|
|||||||
android:id="@+id/videoMediaViewerThumbnailView"
|
android:id="@+id/videoMediaViewerThumbnailView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
android:scaleType="centerInside"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@ -49,6 +51,7 @@
|
|||||||
android:id="@+id/videoMediaViewerVideoView"
|
android:id="@+id/videoMediaViewerVideoView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -5,38 +5,26 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="40dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
android:paddingEnd="8dp">
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/readReceiptAvatar"
|
android:id="@+id/readReceiptAvatar"
|
||||||
android:layout_width="24dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="32dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/readReceiptName"
|
android:id="@+id/readReceiptName"
|
||||||
android:layout_width="0dp"
|
style="@style/BottomSheetItemTextMain"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:lines="1"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:textColor="?riotx_text_primary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/readReceiptDate"
|
android:id="@+id/readReceiptDate"
|
||||||
android:layout_width="wrap_content"
|
style="@style/BottomSheetItemTime"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lines="1"
|
|
||||||
android:textColor="?riotx_text_secondary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
tools:text="10:44" />
|
tools:text="10:44" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -6,6 +6,7 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
|
android:minHeight="40dp"
|
||||||
android:paddingEnd="8dp">
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -22,25 +23,12 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemSimpleReactionInfoMemberName"
|
android:id="@+id/itemSimpleReactionInfoMemberName"
|
||||||
android:layout_width="0dp"
|
style="@style/BottomSheetItemTextMain"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:lines="1"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemSimpleReactionInfoTime"
|
android:id="@+id/itemSimpleReactionInfoTime"
|
||||||
android:layout_width="wrap_content"
|
style="@style/BottomSheetItemTime"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lines="1"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
tools:text="10:44" />
|
tools:text="10:44" />
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,8 +129,10 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -58,8 +58,10 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -37,20 +37,23 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reactionText"
|
android:id="@+id/reactionText"
|
||||||
android:layout_width="20dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_gravity="center"
|
android:minWidth="20dp"
|
||||||
android:layout_marginStart="6dp"
|
android:layout_marginStart="6dp"
|
||||||
android:layout_marginLeft="6dp"
|
android:layout_marginLeft="6dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="13sp"
|
android:textSize="13sp"
|
||||||
|
android:maxEms="10"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:singleLine="true"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/reactionCount"
|
app:layout_constraintEnd_toStartOf="@id/reactionCount"
|
||||||
tools:text="👍" />
|
tools:text="* Party Parrot Again * 👀" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reactionCount"
|
android:id="@+id/reactionCount"
|
||||||
@ -58,8 +61,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/reactionText"
|
app:layout_constraintBaseline_toBaselineOf="@id/reactionText"
|
||||||
android:layout_marginStart="-4dp"
|
android:layout_marginStart="2dp"
|
||||||
android:layout_marginLeft="-4dp"
|
android:layout_marginLeft="2dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
@ -9,48 +9,55 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/receiptMore"
|
android:id="@+id/receiptMore"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="18dp"
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:background="?vctr_pill_receipt"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
tools:text="999+" />
|
tools:text="999+" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/receiptAvatar5"
|
android:id="@+id/receiptAvatar5"
|
||||||
android:layout_width="16dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/receiptAvatar4"
|
android:id="@+id/receiptAvatar4"
|
||||||
android:layout_width="16dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/receiptAvatar3"
|
android:id="@+id/receiptAvatar3"
|
||||||
android:layout_width="16dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/receiptAvatar2"
|
android:id="@+id/receiptAvatar2"
|
||||||
android:layout_width="16dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/receiptAvatar1"
|
android:id="@+id/receiptAvatar1"
|
||||||
android:layout_width="16dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
@ -326,7 +326,7 @@
|
|||||||
<string name="room_unsent_messages_notification">Съобщенията не са изпратени. %1$s или %2$s?</string>
|
<string name="room_unsent_messages_notification">Съобщенията не са изпратени. %1$s или %2$s?</string>
|
||||||
<string name="room_unknown_devices_messages_notification">Съобщението не е изпратено поради наличието на непознати устройства. %1$s или %2$s?</string>
|
<string name="room_unknown_devices_messages_notification">Съобщението не е изпратено поради наличието на непознати устройства. %1$s или %2$s?</string>
|
||||||
<string name="room_prompt_resend">Изпрати всички отново</string>
|
<string name="room_prompt_resend">Изпрати всички отново</string>
|
||||||
<string name="room_prompt_cancel">откажи всички</string>
|
<string name="room_prompt_cancel">Откажи всички</string>
|
||||||
<string name="room_resend_unsent_messages">Изпрати отново неизпратените съобщения</string>
|
<string name="room_resend_unsent_messages">Изпрати отново неизпратените съобщения</string>
|
||||||
<string name="room_delete_unsent_messages">Изтрий неизпратените съобщения</string>
|
<string name="room_delete_unsent_messages">Изтрий неизпратените съобщения</string>
|
||||||
<string name="room_message_file_not_found">Файлът не е намерен</string>
|
<string name="room_message_file_not_found">Файлът не е намерен</string>
|
||||||
@ -1600,4 +1600,17 @@
|
|||||||
|
|
||||||
<string name="link_copied_to_clipboard">Връзката беше копирана</string>
|
<string name="link_copied_to_clipboard">Връзката беше копирана</string>
|
||||||
|
|
||||||
|
<string name="settings_integration_manager">Мениджър на интеграции</string>
|
||||||
|
|
||||||
|
<string name="integration_manager_not_configured">Не е конфигуриран мениджър на интеграции.</string>
|
||||||
|
<string name="add_by_matrix_id">Добави по Matrix идентификатор</string>
|
||||||
|
<string name="creating_direct_room">Създаване на стая…</string>
|
||||||
|
<string name="direct_room_no_known_users">Не са намерени резултати, използва \"Добави по Matrix идентификатор\" за търсене на сървъра.</string>
|
||||||
|
<string name="direct_room_start_search">Започнете да пишете, за да получите резултати</string>
|
||||||
|
<string name="direct_room_filter_hint">Филтрирай по потребител или ID…</string>
|
||||||
|
|
||||||
|
<string name="joining_room">Присъединяване в стая…</string>
|
||||||
|
|
||||||
|
<string name="message_view_edit_history">Виж историята на редакциите</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<string name="black_them">Tema negre</string>
|
<string name="black_them">Tema negre</string>
|
||||||
|
|
||||||
<!-- permanent notification subtitle -->
|
<!-- permanent notification subtitle -->
|
||||||
<string name="notification_sync_in_progress">Sincronitzant</string>
|
<string name="notification_sync_in_progress">S\'està sincronitzant…</string>
|
||||||
<string name="notification_listening_for_events">Escolta esdeveniments</string>
|
<string name="notification_listening_for_events">Escolta esdeveniments</string>
|
||||||
<string name="notification_noisy_notifications">Notificacions sorolloses</string>
|
<string name="notification_noisy_notifications">Notificacions sorolloses</string>
|
||||||
<string name="notification_silent_notifications">Notificacions silencioses</string>
|
<string name="notification_silent_notifications">Notificacions silencioses</string>
|
||||||
@ -46,9 +46,10 @@
|
|||||||
<string name="rename">Reanomena</string>
|
<string name="rename">Reanomena</string>
|
||||||
<string name="report_content">Informa del contingut</string>
|
<string name="report_content">Informa del contingut</string>
|
||||||
<string name="active_call">Trucada activa</string>
|
<string name="active_call">Trucada activa</string>
|
||||||
<string name="ongoing_conference_call">" Conferència en curs.\nUniu-vos hi per %1$s o %2$s."</string>
|
<string name="ongoing_conference_call">Conferència en curs.
|
||||||
<string name="ongoing_conference_call_voice">veu</string>
|
\nUniu-vos hi per %1$s o %2$s.</string>
|
||||||
<string name="ongoing_conference_call_video">vídeo</string>
|
<string name="ongoing_conference_call_voice">Veu</string>
|
||||||
|
<string name="ongoing_conference_call_video">Vídeo</string>
|
||||||
<string name="cannot_start_call">La trucada no es pot iniciar, prova-ho més tard</string>
|
<string name="cannot_start_call">La trucada no es pot iniciar, prova-ho més tard</string>
|
||||||
<string name="missing_permissions_warning">Pot ser que algunes funcions no apareguin per manca de permisos…</string>
|
<string name="missing_permissions_warning">Pot ser que algunes funcions no apareguin per manca de permisos…</string>
|
||||||
<string name="missing_permissions_to_start_conf_call">Es necessiten permisos per convidar a iniciar una conferència en aquesta sala</string>
|
<string name="missing_permissions_to_start_conf_call">Es necessiten permisos per convidar a iniciar una conferència en aquesta sala</string>
|
||||||
@ -84,11 +85,11 @@
|
|||||||
<string name="bottom_action_rooms">Sales</string>
|
<string name="bottom_action_rooms">Sales</string>
|
||||||
|
|
||||||
<!-- Home screen -->
|
<!-- Home screen -->
|
||||||
<string name="home_filter_placeholder_home">Cerca sales</string>
|
<string name="home_filter_placeholder_home">Filtrar noms de sales</string>
|
||||||
<string name="home_filter_placeholder_favorites">Cerca preferits</string>
|
<string name="home_filter_placeholder_favorites">Filtrar preferits</string>
|
||||||
<string name="home_filter_placeholder_people">Cerca persones</string>
|
<string name="home_filter_placeholder_people">Filtrar persones</string>
|
||||||
<string name="home_filter_placeholder_rooms">Cerca sales</string>
|
<string name="home_filter_placeholder_rooms">Filtrar per noms de sala</string>
|
||||||
<string name="home_filter_placeholder_groups">Cerca comunitats</string>
|
<string name="home_filter_placeholder_groups">Filtrar per nom de comunitats</string>
|
||||||
|
|
||||||
<!-- Home fragment -->
|
<!-- Home fragment -->
|
||||||
<string name="invitations_header">Convida</string>
|
<string name="invitations_header">Convida</string>
|
||||||
@ -137,7 +138,7 @@
|
|||||||
|
|
||||||
<string name="join_room">Uneix-te a la sala</string>
|
<string name="join_room">Uneix-te a la sala</string>
|
||||||
<string name="username">Nom d\'usuari</string>
|
<string name="username">Nom d\'usuari</string>
|
||||||
<string name="create_account">Registra\'m</string>
|
<string name="create_account">Crear un compte</string>
|
||||||
<string name="login">Entra</string>
|
<string name="login">Entra</string>
|
||||||
<string name="logout">Desconnecta</string>
|
<string name="logout">Desconnecta</string>
|
||||||
<string name="hs_url">URL del servidor</string>
|
<string name="hs_url">URL del servidor</string>
|
||||||
@ -235,7 +236,7 @@
|
|||||||
<!-- accounts list Screen -->
|
<!-- accounts list Screen -->
|
||||||
|
|
||||||
<!-- image size selection -->
|
<!-- image size selection -->
|
||||||
<string name="compression_options">"Envia com "</string>
|
<string name="compression_options">Envia com</string>
|
||||||
<string name="compression_opt_list_original">Original</string>
|
<string name="compression_opt_list_original">Original</string>
|
||||||
<string name="compression_opt_list_large">Gran</string>
|
<string name="compression_opt_list_large">Gran</string>
|
||||||
<string name="compression_opt_list_medium">Mitjana</string>
|
<string name="compression_opt_list_medium">Mitjana</string>
|
||||||
@ -283,7 +284,9 @@
|
|||||||
<string name="permissions_rationale_msg_record_audio">Per tal de fer trucades de veu, el Riot necessita permís d\'accés al microfon.</string>
|
<string name="permissions_rationale_msg_record_audio">Per tal de fer trucades de veu, el Riot necessita permís d\'accés al microfon.</string>
|
||||||
<string name="permissions_rationale_msg_record_audio_explanation">\n\nA la següent finestra emergent, doneu permís d\'accés per tal de poder fer la trucada.</string>
|
<string name="permissions_rationale_msg_record_audio_explanation">\n\nA la següent finestra emergent, doneu permís d\'accés per tal de poder fer la trucada.</string>
|
||||||
<string name="permissions_rationale_msg_camera_and_audio">Per tal de fer vídeotrucades, el Riot necessita permís d\'accés a la càmera i al microfon.\n\nA la següent finestra emergent, doneu permís d\'accés per tal de poder fer la trucada.</string>
|
<string name="permissions_rationale_msg_camera_and_audio">Per tal de fer vídeotrucades, el Riot necessita permís d\'accés a la càmera i al microfon.\n\nA la següent finestra emergent, doneu permís d\'accés per tal de poder fer la trucada.</string>
|
||||||
<string name="permissions_rationale_msg_contacts">Per tal de trobar altres usuaris de Matrix a partir dels seus correus electrònics o dels seus números de telefon, el Riot necessita permís d\'accés a l\'agenda de contactes.\n\nA la següent finestra emergent, doneu permís d\'accés per trobar amb quins contactes de la teva agenda pots contactar a partir de Riot.</string>
|
<string name="permissions_rationale_msg_contacts">Riot pot comprovar la vostra agenda de contactes per tal de trobar altres usuaris de Matrix basant-se en les seves adreces de correu i números de telèfon.
|
||||||
|
\n
|
||||||
|
\nSi accepteu compartir la vostra agenda de contactes amb aquesta finalitat, si us plau permeteu l\'accés de la següent finestra emergent.</string>
|
||||||
<string name="permissions_msg_contacts_warning_other_androids">Per tal de trobar altres usuaris de Matrix a partir dels seus correus electrònics o dels seus números de telefon, el Riot necessita permís d\'accés a l\'agenda de contactes.\n\nPermeteu que Riot accedeixi als vostres contactes?</string>
|
<string name="permissions_msg_contacts_warning_other_androids">Per tal de trobar altres usuaris de Matrix a partir dels seus correus electrònics o dels seus números de telefon, el Riot necessita permís d\'accés a l\'agenda de contactes.\n\nPermeteu que Riot accedeixi als vostres contactes?</string>
|
||||||
|
|
||||||
<string name="permissions_action_not_performed_missing_permissions">No s\'ha realitzat l\'acció per falta de permisos</string>
|
<string name="permissions_action_not_performed_missing_permissions">No s\'ha realitzat l\'acció per falta de permisos</string>
|
||||||
@ -370,7 +373,7 @@
|
|||||||
<string name="room_unsent_messages_notification">Els missatges no s\'han enviat. %1$s o %2$s ara?</string>
|
<string name="room_unsent_messages_notification">Els missatges no s\'han enviat. %1$s o %2$s ara?</string>
|
||||||
<string name="room_unknown_devices_messages_notification">Els missatges no s\'han enviat perquè hi ha disposistius desconeguts. %1$s o %2$s ara?</string>
|
<string name="room_unknown_devices_messages_notification">Els missatges no s\'han enviat perquè hi ha disposistius desconeguts. %1$s o %2$s ara?</string>
|
||||||
<string name="room_prompt_resend">Reenvia-ho tot</string>
|
<string name="room_prompt_resend">Reenvia-ho tot</string>
|
||||||
<string name="room_prompt_cancel">cancel·la-ho tot</string>
|
<string name="room_prompt_cancel">Cancel·la-ho tot</string>
|
||||||
<string name="room_resend_unsent_messages">Reenvia els missatges no enviats</string>
|
<string name="room_resend_unsent_messages">Reenvia els missatges no enviats</string>
|
||||||
<string name="room_delete_unsent_messages">Elimina els missatges no enviats</string>
|
<string name="room_delete_unsent_messages">Elimina els missatges no enviats</string>
|
||||||
<string name="room_message_file_not_found">No s\'ha trobat el fitxer</string>
|
<string name="room_message_file_not_found">No s\'ha trobat el fitxer</string>
|
||||||
@ -542,9 +545,9 @@ Tingueu en compte que aquesta acció reiniciarà l\'aplicació i que pot trigar
|
|||||||
<string name="account_phone_number_already_used_error">Aquest número de telèfon ja està en ús.</string>
|
<string name="account_phone_number_already_used_error">Aquest número de telèfon ja està en ús.</string>
|
||||||
|
|
||||||
<string name="settings_change_password">Canvia la contrasenya</string>
|
<string name="settings_change_password">Canvia la contrasenya</string>
|
||||||
<string name="settings_old_password">Contrasenya antiga</string>
|
<string name="settings_old_password">Contrasenya actual</string>
|
||||||
<string name="settings_new_password">Contrasenya nova</string>
|
<string name="settings_new_password">Contrasenya nova</string>
|
||||||
<string name="settings_confirm_password">Confirmeu la contrasenya</string>
|
<string name="settings_confirm_password">Confirmeu la nova contrasenya</string>
|
||||||
<string name="settings_fail_to_update_password">No s\'ha pogut actualitzar la contrasenya</string>
|
<string name="settings_fail_to_update_password">No s\'ha pogut actualitzar la contrasenya</string>
|
||||||
<string name="settings_password_updated">La contrasenya s\'ha actualitzat</string>
|
<string name="settings_password_updated">La contrasenya s\'ha actualitzat</string>
|
||||||
<string name="settings_unignore_user">Mostra tots els missatges des de %s?
|
<string name="settings_unignore_user">Mostra tots els missatges des de %s?
|
||||||
@ -889,7 +892,7 @@ Atenció: es podria eliminar aquest fitxer si es desinstal·la l\'aplicació.</s
|
|||||||
<string name="e2e_re_request_encryption_key_dialog_content">Si us plau, engega Riot a un altre dispositiu que pugui desencriptar el missatge de manera que pugui enviar la clau a aquest dispositiu.</string>
|
<string name="e2e_re_request_encryption_key_dialog_content">Si us plau, engega Riot a un altre dispositiu que pugui desencriptar el missatge de manera que pugui enviar la clau a aquest dispositiu.</string>
|
||||||
|
|
||||||
<string name="settings_notification_privacy_normal">Normal</string>
|
<string name="settings_notification_privacy_normal">Normal</string>
|
||||||
<string name="status_theme">Motiu Status.im</string>
|
<string name="status_theme">Tema Status.im</string>
|
||||||
|
|
||||||
<string name="missing_permissions_error">Manquen permisos per a dur a terme aquesta acció.</string>
|
<string name="missing_permissions_error">Manquen permisos per a dur a terme aquesta acció.</string>
|
||||||
<string name="dialog_title_error">Error</string>
|
<string name="dialog_title_error">Error</string>
|
||||||
@ -924,7 +927,7 @@ En voleu afegir algun?</string>
|
|||||||
<string name="room_participants_action_unignore_prompt">Mostrar tots els missatges d\'aquest usuari?
|
<string name="room_participants_action_unignore_prompt">Mostrar tots els missatges d\'aquest usuari?
|
||||||
|
|
||||||
Tingueu en compte que aquesta acció reiniciarà l\'aplicació i pot trigar una estona.</string>
|
Tingueu en compte que aquesta acció reiniciarà l\'aplicació i pot trigar una estona.</string>
|
||||||
<string name="room_participants_invite_join_names">"%1$s, "</string>
|
<string name="room_participants_invite_join_names">"%1$s,· "</string>
|
||||||
<string name="room_participants_invite_join_names_and">%1$s i %2$s</string>
|
<string name="room_participants_invite_join_names_and">%1$s i %2$s</string>
|
||||||
<string name="room_participants_invite_join_names_combined">%1$s %2$s</string>
|
<string name="room_participants_invite_join_names_combined">%1$s %2$s</string>
|
||||||
|
|
||||||
@ -938,7 +941,7 @@ Tingueu en compte que aquesta acció reiniciarà l\'aplicació i pot trigar una
|
|||||||
<string name="settings_notification_privacy">Notificació de privacitat</string>
|
<string name="settings_notification_privacy">Notificació de privacitat</string>
|
||||||
<string name="settings_notification_privacy_reduced">Privacitat reduïda</string>
|
<string name="settings_notification_privacy_reduced">Privacitat reduïda</string>
|
||||||
<string name="settings_notification_privacy_need_permission">L\'aplicació necessita permisos per funcionar en segon pla</string>
|
<string name="settings_notification_privacy_need_permission">L\'aplicació necessita permisos per funcionar en segon pla</string>
|
||||||
<string name="settings_notification_privacy_fcm">• Les notificacions s\'envien via Google Cloud Messaging</string>
|
<string name="settings_notification_privacy_fcm">• Les notificacions s\'envien via Firebase Cloud Messaging</string>
|
||||||
<string name="settings_notification_privacy_metadata">• Les notificacions contenen només meta dades</string>
|
<string name="settings_notification_privacy_metadata">• Les notificacions contenen només meta dades</string>
|
||||||
<string name="settings_notification_privacy_secure_message_content">• El contingut dels missatges de les notificacions s\'obté <b>de forma segura des del servidor de Matrix</b></string>
|
<string name="settings_notification_privacy_secure_message_content">• El contingut dels missatges de les notificacions s\'obté <b>de forma segura des del servidor de Matrix</b></string>
|
||||||
<string name="settings_notification_privacy_nosecure_message_content">• Les notificacions contenen <b>meta dades i dades de missatges</b></string>
|
<string name="settings_notification_privacy_nosecure_message_content">• Les notificacions contenen <b>meta dades i dades de missatges</b></string>
|
||||||
@ -1246,7 +1249,7 @@ Aquest error és fora del control del Riot. No hi ha cap compte de Google al tel
|
|||||||
<string name="settings_troubleshoot_test_bg_restricted_failed">Les restriccions de rerefons són habilitades per al Riot.
|
<string name="settings_troubleshoot_test_bg_restricted_failed">Les restriccions de rerefons són habilitades per al Riot.
|
||||||
Les tasques que l\'aplicació intenta fer estaran restringides agressivament mentre estigui al rerefons, i això pot afectar les notificacions.
|
Les tasques que l\'aplicació intenta fer estaran restringides agressivament mentre estigui al rerefons, i això pot afectar les notificacions.
|
||||||
%1$s</string>
|
%1$s</string>
|
||||||
<string name="settings_troubleshoot_test_battery_failed">"Si un usuari deixa un dispositiu sense endollar i immòbil durant un període de temps, amb la pantalla apagada, el dispositiu entra en el mode Doze. Això impedeix les aplicacions d\'accedir a la xarxa i ajorna les seves tasques, sincronitzacions i alarmes estàndard. "</string>
|
<string name="settings_troubleshoot_test_battery_failed">Si un usuari deixa un dispositiu sense endollar i immòbil durant un període de temps, amb la pantalla apagada, el dispositiu entra en el mode d\'estalvi d\'energia. Això impedeix les aplicacions d\'accedir a la xarxa i ajorna les seves tasques, sincronitzacions i alarmes estàndard.</string>
|
||||||
<string name="store_full_description">"Una aplicació de xat, sota el vostre control i totalment flexible. El Riot us permet comunicar-vos de la manera que preferiu. Fet per al [matrix] - l’estàndard per a la comunicació oberta i descentralitzada.
|
<string name="store_full_description">"Una aplicació de xat, sota el vostre control i totalment flexible. El Riot us permet comunicar-vos de la manera que preferiu. Fet per al [matrix] - l’estàndard per a la comunicació oberta i descentralitzada.
|
||||||
|
|
||||||
Obteniu un compte gratuït de matrix.org, executeu el vostre servidor propi a https://modular.im, o empreu un altre servidor de Matrix.
|
Obteniu un compte gratuït de matrix.org, executeu el vostre servidor propi a https://modular.im, o empreu un altre servidor de Matrix.
|
||||||
@ -1385,7 +1388,7 @@ Per què triar Riot.im?
|
|||||||
<string name="notification_sync_init">Inicialitzar servei</string>
|
<string name="notification_sync_init">Inicialitzar servei</string>
|
||||||
<string name="ignore">Ignorar</string>
|
<string name="ignore">Ignorar</string>
|
||||||
|
|
||||||
<string name="auth_login_sso">Registrar-se amb Single Sign-on</string>
|
<string name="auth_login_sso">Iniciar sessió amb Single Sign-on</string>
|
||||||
<string name="login_error_unknown_host">Aquesta URL no està disponible , si us plau verifiqueu-la</string>
|
<string name="login_error_unknown_host">Aquesta URL no està disponible , si us plau verifiqueu-la</string>
|
||||||
<string name="login_error_ssl_handshake">El vostre dispositiu està usant una versió obsoleta del protocol de seguretat TLS, vulnerable a atacs. Per a la vostra seguretat no us podreu connectar</string>
|
<string name="login_error_ssl_handshake">El vostre dispositiu està usant una versió obsoleta del protocol de seguretat TLS, vulnerable a atacs. Per a la vostra seguretat no us podreu connectar</string>
|
||||||
<string name="settings_send_message_with_enter">Envieu un missatge amb Enter</string>
|
<string name="settings_send_message_with_enter">Envieu un missatge amb Enter</string>
|
||||||
@ -1403,4 +1406,174 @@ Per què triar Riot.im?
|
|||||||
<string name="autodiscover_invalid_response">Resposta no vàlida en descobrir homeservers</string>
|
<string name="autodiscover_invalid_response">Resposta no vàlida en descobrir homeservers</string>
|
||||||
<string name="autodiscover_well_known_autofill_confirm">Usar Config</string>
|
<string name="autodiscover_well_known_autofill_confirm">Usar Config</string>
|
||||||
|
|
||||||
|
<string name="title_activity_verify_device">Verificar dispositiu</string>
|
||||||
|
|
||||||
|
<string name="action_mark_room_read">Marcar com a llegit</string>
|
||||||
|
<string name="settings_notification_privacy_no_background_sync">Les app <b>no</b> necessita connectar-se al HomeServer en segon pla, hauria de reduir el consum de bateria</string>
|
||||||
|
<string name="settings_integration_manager">Administrador d\'integracions</string>
|
||||||
|
|
||||||
|
<string name="settings_play_shutter_sound">Reproduir el so de disparador</string>
|
||||||
|
|
||||||
|
<string name="encryption_information_unknown_ip">IP desconeguda</string>
|
||||||
|
<plurals name="notification_compat_summary_line_for_room">
|
||||||
|
<item quantity="one">%1$s: 1 missatge</item>
|
||||||
|
<item quantity="other">%1$s: %2$d missatges</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="notification_compat_summary_title">
|
||||||
|
<item quantity="one">%d notificacion</item>
|
||||||
|
<item quantity="other">%d notificacions</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<string name="notification_unknown_new_event">Nou esdeveniment</string>
|
||||||
|
<string name="notification_unknown_room_name">Sala</string>
|
||||||
|
<string name="notification_new_messages">Missatges nous</string>
|
||||||
|
<string name="notification_new_invitation">Nova invitació</string>
|
||||||
|
<string name="notification_sender_me">Jo</string>
|
||||||
|
<string name="notification_inline_reply_failed">** Error en enviar - Si us plau obriu la sala</string>
|
||||||
|
|
||||||
|
<string name="error_jitsi_not_supported_on_old_device">Ho sentim, els dispositius amb SO Android inferior a 5.0 no suporten trucades multi-usuari amb Jitsi</string>
|
||||||
|
|
||||||
|
<string name="integration_manager_not_configured">No heu configurat cap administrador d\'integracions.</string>
|
||||||
|
<string name="you_added_a_new_device_with_info">Un nou dispositiu està sol·licitant claus d\'encriptació.
|
||||||
|
\nNom del dispositiu: %1$s
|
||||||
|
\nVist per última vegada: %s$s
|
||||||
|
\nSi no heu iniciat sessió en un altre dispositiu, ignoreu la sol·licitud.</string>
|
||||||
|
<string name="your_unverified_device_requesting_with_info">Un dispositiu no verificat està sol·licitant claus d\'encriptació.
|
||||||
|
\nNom del dispositiu: %1$s
|
||||||
|
\nVist per última vegada: %s$s
|
||||||
|
\nSi no heu iniciat sessió en un altre dispositiu, ignoreu la sol·licitud.</string>
|
||||||
|
|
||||||
|
<string name="start_verification_short_label">Verificar</string>
|
||||||
|
<string name="share_without_verifying_short_label">Compartir</string>
|
||||||
|
<string name="key_share_request">Sol·licitud de compartició de clau</string>
|
||||||
|
<string name="ignore_request_short_label">Ignorar</string>
|
||||||
|
|
||||||
|
<string name="keys_backup_setup_override_backup_prompt_tile">Ja existeix una còpia de seguretat al vostre HomeServer</string>
|
||||||
|
<string name="keys_backup_setup_override_backup_prompt_description">Sembla que ja heu configurat una còpia de seguretat de claus des d\'un altre dispositiu. Voleu reemplaçar-la amb la que esteu creant\?</string>
|
||||||
|
<string name="keys_backup_setup_override_replace">Reemplaçar</string>
|
||||||
|
<string name="keys_backup_setup_override_stop">Aturar</string>
|
||||||
|
|
||||||
|
<string name="keys_backup_settings_checking_backup_state">Comprovant l\'estat de la còpia de seguretat</string>
|
||||||
|
<string name="autodiscover_well_known_autofill_dialog_title">Opcions d\'autocompleció del servidor</string>
|
||||||
|
<string name="autodiscover_well_known_autofill_dialog_message">Riot ha detectat una configuració de servidor personalitzat pel domini del seu identificador d\'usuari \"%1$s\":
|
||||||
|
\n%2$s</string>
|
||||||
|
<string name="invalid_or_expired_credentials">Us heu desconnectat a causa de credencials incorrectes o caducades.</string>
|
||||||
|
|
||||||
|
<string name="sas_verify_title">Verificar comparant una cadena de text curta.</string>
|
||||||
|
<string name="sas_security_advise">Per la màxima seguretat us recomanem fer això en persona o usar un altre medi de comunicació confiable.</string>
|
||||||
|
<string name="sas_verify_start_button_title">Començar la verificació</string>
|
||||||
|
<string name="sas_incoming_request_title">Sol·licitud de verificació entrant</string>
|
||||||
|
<string name="sas_incoming_request_description">Verificar aquest dispositiu per marcar-lo com a confiable. Confiar en dispositius d\'amistats us dona un alleujament addicional quan useu missatges encriptats end-to-end.</string>
|
||||||
|
<string name="sas_incoming_request_description_2">Verificant aquest dispositiu el marcareu com a confiable, i també marcareu el vostre dispositiu com a confiable pel vostre company.</string>
|
||||||
|
|
||||||
|
<string name="sas_emoji_description">Verificar aquest dispositiu confirmant els següents emojis que apareguin a la pantalla del vostre company</string>
|
||||||
|
<string name="sas_decimal_description">Verificar aquest dispositiu confirmant els següents números que sortiran a la pantalla del vostre company</string>
|
||||||
|
|
||||||
|
<string name="sas_incoming_verification_request_dialog">Heu rebut una sol·licitud de verificació entrant.</string>
|
||||||
|
<string name="sas_view_request_action">Veure sol·licitud</string>
|
||||||
|
<string name="sas_waiting_for_partner">Esperant que el vostre company confirmi…</string>
|
||||||
|
|
||||||
|
<string name="sas_verified">Verificat!</string>
|
||||||
|
<string name="sas_verified_successful">Heu verificat aquest dispositiu amb èxit.</string>
|
||||||
|
<string name="sas_verified_successful_description">Els missatges segurs amb aquest usuari estan encriptats end-to-end i no serà possible llegir-los per tercers.</string>
|
||||||
|
<string name="sas_got_it">Entesos</string>
|
||||||
|
|
||||||
|
<string name="sas_verifying_keys">No surt res\? Encara no tots els clients suporten la verificació interactiva. Useu el mètode de verificació antic.</string>
|
||||||
|
<string name="sas_legacy_verification_button_title">Useu el mètode antic de verificació.</string>
|
||||||
|
|
||||||
|
<string name="sas_verification_request_notification_channel_title">Verificació de clau</string>
|
||||||
|
<string name="sas_cancelled_dialog_title">Sol·licitud cancel·lada</string>
|
||||||
|
<string name="sas_cancelled_by_other">L\'altre part ha cancel·lat la verificació.
|
||||||
|
\n%s</string>
|
||||||
|
<string name="sas_cancelled_by_me">S\'ha cancel·lat la verificació.
|
||||||
|
\nMotiu: %s</string>
|
||||||
|
|
||||||
|
<string name="sas_verification_request_notification_channel">Verificació de dispositiu interactiva</string>
|
||||||
|
<string name="sas_incoming_request_notif_title">Sol·licitud de verificació</string>
|
||||||
|
<string name="sas_incoming_request_notif_content">%s vol verificar el vostre dispositiu</string>
|
||||||
|
|
||||||
|
<string name="sas_error_m_user">L\'usuari ha cancel·lat la verificació</string>
|
||||||
|
<string name="sas_error_m_timeout">El marge de temps pel procés de verificació ha expirat</string>
|
||||||
|
<string name="sas_error_m_unknown_transaction">El dispositiu no coneix la transacció</string>
|
||||||
|
<string name="sas_error_m_unknown_method">El dispositiu no pot acceptar un acord de claus amb mètodes hash, MAC o SAS</string>
|
||||||
|
<string name="sas_error_m_mismatched_commitment">El compromís del hash no ha coincidit</string>
|
||||||
|
<string name="sas_error_m_mismatched_sas">El SAS no ha coincidit</string>
|
||||||
|
<string name="sas_error_m_unexpected_message">El dispositiu ha rebut un missatge inesperat</string>
|
||||||
|
<string name="sas_error_m_invalid_message">S\'ha rebut un missatge invàlid</string>
|
||||||
|
<string name="sas_error_m_key_mismatch">La clau no coincideix</string>
|
||||||
|
<string name="sas_error_m_user_error">L\'usuari no coincideix</string>
|
||||||
|
<string name="sas_error_unknown">Error desconegut</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="edit">Editar</string>
|
||||||
|
<string name="reply">Respondre</string>
|
||||||
|
|
||||||
|
<string name="global_retry">Tornar-ho a provar</string>
|
||||||
|
<string name="room_list_empty">Unir-se a una sala per començar usant l\'app.</string>
|
||||||
|
<string name="send_you_invite">Se t\'ha enviat una invitació</string>
|
||||||
|
<string name="invited_by">Convidat per %s</string>
|
||||||
|
|
||||||
|
<string name="room_list_catchup_empty_title">Esteu al dia!</string>
|
||||||
|
<string name="room_list_catchup_empty_body">No teniu més missatges sense llegir</string>
|
||||||
|
<string name="room_list_catchup_welcome_title">Benvingut a casa!</string>
|
||||||
|
<string name="room_list_catchup_welcome_body">Posar-se al dia dels missatges sense llegir</string>
|
||||||
|
<string name="room_list_people_empty_title">Converses</string>
|
||||||
|
<string name="room_list_people_empty_body">Els vostres missatges directes es mostraran aquí</string>
|
||||||
|
<string name="room_list_rooms_empty_title">Sales</string>
|
||||||
|
<string name="room_list_rooms_empty_body">Les vostres sales es mostraran aquí</string>
|
||||||
|
|
||||||
|
<string name="title_activity_emoji_reaction_picker">Reaccions</string>
|
||||||
|
<string name="reactions_agree">Confirmar</string>
|
||||||
|
<string name="reactions_like">M\'agrada</string>
|
||||||
|
<string name="message_add_reaction">Afegir reacció</string>
|
||||||
|
<string name="message_view_reaction">Veure reaccions</string>
|
||||||
|
<string name="reactions">Reaccions</string>
|
||||||
|
|
||||||
|
<string name="event_redacted_by_user_reason">Esdeveniment eliminat per l\'usuari</string>
|
||||||
|
<string name="event_redacted_by_admin_reason">Esdeveniment moderat per l\'administrador de la sala</string>
|
||||||
|
<string name="last_edited_info_message">Última edició per %s el %s</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="malformed_message">Esdeveniment mal format, no es pot mostrar</string>
|
||||||
|
<string name="create_new_room">Crear sala nova</string>
|
||||||
|
<string name="error_no_network">No hi ha xarxa. Si us plau comproveu la vostra connexió a internet.</string>
|
||||||
|
<string name="action_change">Canviar</string>
|
||||||
|
<string name="change_room_directory_network">Canviar de xarxa</string>
|
||||||
|
<string name="please_wait">Espereu, si us plau…</string>
|
||||||
|
<string name="group_all_communities">Totes les comunitats</string>
|
||||||
|
|
||||||
|
<string name="room_preview_no_preview">Aquesta sala no es pot pre-visualitzar</string>
|
||||||
|
<string name="room_preview_world_readable_room_not_supported_yet">RiotX encara no suporta la pre-visualització de sales llegibles per tothom</string>
|
||||||
|
|
||||||
|
<string name="fab_menu_create_room">Sales</string>
|
||||||
|
<string name="fab_menu_create_chat">Missatges directes</string>
|
||||||
|
|
||||||
|
<string name="create_room_title">Sala nova</string>
|
||||||
|
<string name="create_room_action_create">CREAR</string>
|
||||||
|
<string name="create_room_name_hint">Nom de la sala</string>
|
||||||
|
<string name="create_room_public_title">Públic</string>
|
||||||
|
<string name="create_room_public_description">Qualsevol podrà unir-se a aquesta sala</string>
|
||||||
|
<string name="create_room_directory_title">Directori de sales</string>
|
||||||
|
<string name="create_room_directory_description">Publicar aquesta sala al directori de sales</string>
|
||||||
|
|
||||||
|
<string name="keys_backup_unable_to_get_trust_info">Hi ha hagut un error rebent informació de confança</string>
|
||||||
|
<string name="keys_backup_unable_to_get_keys_backup_data">Hi ha hagut un error rebent dades de la còpia de seguretat de les claus</string>
|
||||||
|
|
||||||
|
<string name="alpha_disclaimer_title">Benvinguts a la beta!</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_1">Mentre RiotX estigui en les primeres etapes de desenvolupament, faltaran algunes funcions i podríeu experimentar alguns errors.</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_gplay">L\'última llista de característiques està sempre a %1$s, i si trobeu errors si us plau reporteu informe des de la part superior esquerra del menú d\'Inici, i el resoldrem tan aviat com puguem.</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_gplay_colored_part">Descripció de la Play Store</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_fdroid">Si trobeu errors si us plau envieu un informe d\'errors des de la part superior esquerra del menú d\'Inici, i el resoldrem tan aviat com puguem.</string>
|
||||||
|
|
||||||
|
<string name="import_e2e_keys_from_file">Importar claus e2e des del fitxer \"%1$s\".</string>
|
||||||
|
|
||||||
|
<string name="settings_sdk_version">Versió de l\'SDK de Matrix</string>
|
||||||
|
<string name="navigate_to_room_when_already_in_the_room">Ja esteu veient aquesta sala!</string>
|
||||||
|
|
||||||
|
<string name="quick_reactions">Reaccions ràpides</string>
|
||||||
|
|
||||||
|
<string name="settings_general_title">General</string>
|
||||||
|
<string name="settings_preferences">Preferències</string>
|
||||||
|
<string name="settings_security_and_privacy">Seguretat i privadesa</string>
|
||||||
|
<string name="settings_expert">Expert</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -210,7 +210,7 @@ Du wurdest auf allen Geräten abgemeldet und wirst keine Push-Benachrichtigungen
|
|||||||
<!-- accounts list Screen -->
|
<!-- accounts list Screen -->
|
||||||
|
|
||||||
<!-- image size selection -->
|
<!-- image size selection -->
|
||||||
<string name="compression_options">"Sende als "</string>
|
<string name="compression_options">Sende als</string>
|
||||||
<string name="compression_opt_list_original">Original</string>
|
<string name="compression_opt_list_original">Original</string>
|
||||||
<string name="compression_opt_list_large">Groß</string>
|
<string name="compression_opt_list_large">Groß</string>
|
||||||
<string name="compression_opt_list_medium">Mittel</string>
|
<string name="compression_opt_list_medium">Mittel</string>
|
||||||
@ -1551,4 +1551,113 @@ Wenn du diese neue Wiederherstellungsmethode nicht eingerichtet hast, kann ein A
|
|||||||
<string name="create_room_directory_title">Raumverzeichnis</string>
|
<string name="create_room_directory_title">Raumverzeichnis</string>
|
||||||
<string name="create_room_directory_description">Veröffentliche diesen Raum ins Raumverzeichnis</string>
|
<string name="create_room_directory_description">Veröffentliche diesen Raum ins Raumverzeichnis</string>
|
||||||
|
|
||||||
|
<string name="settings_integration_manager">Integrationsmanager</string>
|
||||||
|
|
||||||
|
<string name="integration_manager_not_configured">Kein Integrationsmanager konfiguriert.</string>
|
||||||
|
<string name="key_share_request">Schlüsselfreigabe Anfordern</string>
|
||||||
|
<string name="keys_backup_setup_override_backup_prompt_description">Es sieht so aus, als hätten Sie bereits ein Setup-Schlüssel-Backup von einem anderen Gerät. Möchten Sie es durch das ersetzen, das Sie gerade erstellen\?</string>
|
||||||
|
<string name="sas_security_advise">Für maximale Sicherheit empfehlen wir, dies persönlich zu tun oder ein anderes vertrauenswürdiges Kommunikationsmittel zu verwenden.</string>
|
||||||
|
<string name="sas_incoming_request_description">Überprüfen Sie dieses Gerät, um es als vertrauenswürdig zu markieren. Das Vertrauen auf Geräte von Partnern gibt Ihnen zusätzliche Sicherheit, wenn Sie verschlüsselte End-to-End-Nachrichten verwenden.</string>
|
||||||
|
<string name="sas_incoming_request_description_2">Das Verifizieren dieses Benutzers wird seine Geräte als \"vertraut\" markieren und dein Gerät bei ihnen als \"vertraut\" markieren.</string>
|
||||||
|
|
||||||
|
<string name="sas_emoji_description">Verifizieren Sie diesen Benutzer, indem Sie bestätigen, dass folgendes Emoji auf dessen Bildschirm erscheint.</string>
|
||||||
|
<string name="sas_decimal_description">Verifizieren Sie diesen Benutzer, indem Sie bestätigen, dass die folgende Nummer auf dessen Bildschirm erscheint.</string>
|
||||||
|
|
||||||
|
<string name="sas_verifying_keys">Es ist nichts aufgetaucht\? Noch nicht alle Clients unterstützen die interaktive Verifikation. <button>Nutze alte Verifikation</button>.</string>
|
||||||
|
<string name="sas_legacy_verification_button_title">Verwenden Sie die Alte-Überprüfung</string>
|
||||||
|
|
||||||
|
<string name="sas_error_m_unknown_transaction">Das Gerät kennt diese Transaktion nicht</string>
|
||||||
|
<string name="sas_error_m_mismatched_commitment">Die Hash-Verpflichtung stimmte nicht überein</string>
|
||||||
|
<string name="sas_error_m_mismatched_sas">Die SAS stimmte nicht überein</string>
|
||||||
|
<string name="sas_error_m_key_mismatch">Wiederherstellungsschlüssel passt nicht</string>
|
||||||
|
<string name="sas_error_m_user_error">Der Benutzer Wiederherstellungsschlüssel passt nicht</string>
|
||||||
|
<string name="room_list_catchup_welcome_body">Informieren Sie sich hier über ungelesene Nachrichten</string>
|
||||||
|
<string name="room_list_people_empty_body">Ihre direkte Konversation wird hier angezeigt</string>
|
||||||
|
<string name="malformed_message">Fehlerhaftes Ereignis, kann nicht angezeigt werden</string>
|
||||||
|
<string name="keys_backup_unable_to_get_trust_info">Beim Abrufen der Vertrauensinformationen ist ein Fehler aufgetreten</string>
|
||||||
|
<string name="keys_backup_unable_to_get_keys_backup_data">Beim Abrufen der Schlüsselsicherungsdaten ist ein Fehler aufgetreten</string>
|
||||||
|
|
||||||
|
<string name="alpha_disclaimer_title">Willkommen in der Beta!</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_1">Während sich RiotX in der frühen Entwicklung befindet, fehlen möglicherweise einige Funktionen und es können Fehler auftreten.</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_gplay_colored_part">Play Store Beschreibung</string>
|
||||||
|
<string name="settings_sdk_version">Matrix SDK Version</string>
|
||||||
|
<string name="settings_other_third_party_notices">Sonstige Hinweise Dritter</string>
|
||||||
|
<string name="navigate_to_room_when_already_in_the_room">Sie sehen diesen Raum bereits!</string>
|
||||||
|
|
||||||
|
<string name="quick_reactions">Schnelle Reaktionen</string>
|
||||||
|
|
||||||
|
<string name="settings_general_title">Allgemein</string>
|
||||||
|
<string name="settings_preferences">Einstellungen</string>
|
||||||
|
<string name="settings_security_and_privacy">Sicherheit & Privatsphäre</string>
|
||||||
|
<string name="settings_expert">Experte</string>
|
||||||
|
<string name="settings_push_rules">Push-Regeln</string>
|
||||||
|
<string name="settings_push_rules_no_rules">Keine Push-Regeln definiert</string>
|
||||||
|
<string name="settings_push_gateway_no_pushers">Keine registrierten Push-Gateways</string>
|
||||||
|
|
||||||
|
<string name="preference_voice_and_video">Sprache & Video</string>
|
||||||
|
<string name="preference_root_help_about">Hilfe & Über</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="settings_troubleshoot_test_token_registration_quick_fix">Token registrieren</string>
|
||||||
|
|
||||||
|
<string name="send_suggestion">Mache einen Vorschlag</string>
|
||||||
|
<string name="send_suggestion_content">Bitte schreiben Sie Ihren Vorschlag unten.</string>
|
||||||
|
<string name="send_suggestion_report_placeholder">Beschreiben Sie hier Ihren Vorschlag</string>
|
||||||
|
<string name="settings_labs_show_hidden_events_in_timeline">Versteckte Ereignisse in der Zeitleiste anzeigen</string>
|
||||||
|
|
||||||
|
<string name="bottom_action_people_x">Direkte Nachrichten</string>
|
||||||
|
|
||||||
|
<string name="send_file_step_idle">Warten…</string>
|
||||||
|
<string name="send_file_step_encrypting_thumbnail">Miniaturbild wird verschlüsselt…</string>
|
||||||
|
<string name="send_file_step_encrypting_file">Datei wird verschlüsselt…</string>
|
||||||
|
<string name="edited_suffix">(bearbeitet)</string>
|
||||||
|
|
||||||
|
<string name="message_edits">Nachrichtenbearbeitung</string>
|
||||||
|
<string name="no_message_edits_found">Keine Änderungen gefunden</string>
|
||||||
|
|
||||||
|
<string name="room_filtering_filter_hint">Gespräche filtern…</string>
|
||||||
|
<string name="room_filtering_footer_create_new_direct_message">Senden Sie eine neue Direktnachricht</string>
|
||||||
|
<string name="room_filtering_footer_open_room_directory">Das Raumverzeichnis anzeigen</string>
|
||||||
|
|
||||||
|
<string name="link_copied_to_clipboard">Link in die Zwischenablage kopiert</string>
|
||||||
|
|
||||||
|
<string name="add_by_matrix_id">Nach Matrix-ID hinzufügen</string>
|
||||||
|
<string name="creating_direct_room">Raum erstellen…</string>
|
||||||
|
<string name="message_view_edit_history">Bearbeitungsverlauf anzeigen</string>
|
||||||
|
|
||||||
|
<string name="sas_cancelled_by_other">Die andere Partei hat die Überprüfung abgebrochen.
|
||||||
|
\n%s</string>
|
||||||
|
<string name="sas_error_m_unknown_method">Das Gerät kann sich nicht auf eine Schlüsselvereinbarung, eine Hash-, eine MAC- oder eine SAS-Methode einigen</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_gplay">Die neueste Featureliste befindet sich immer in %1$s. Wenn Sie Fehler finden, senden Sie uns bitte einen Bericht im Menü oben links von \"Startseite\". Wir werden den Fehler so schnell wie möglich beheben.</string>
|
||||||
|
<string name="alpha_disclaimer_content_line_2_fdroid">Wenn Sie Fehler finden, senden Sie uns bitte einen Bericht im Menü oben links von \"Startseite\". Wir werden diese so schnell wie möglich beheben.</string>
|
||||||
|
|
||||||
|
<string name="import_e2e_keys_from_file">Importieren Sie e2e-Schlüssel aus der Datei \"%1$s\".</string>
|
||||||
|
|
||||||
|
<string name="send_suggestion_sent">Vielen Dank, der Vorschlag wurde erfolgreich gesendet</string>
|
||||||
|
<string name="send_suggestion_failed">Der Vorschlag konnte nicht gesendet werden (%s)</string>
|
||||||
|
|
||||||
|
<string name="store_riotx_title">RiotX - Matrix Client der nächsten Generation</string>
|
||||||
|
<string name="store_riotx_short_description">Ein schnellerer und leichterer Client für Matrix mit den neuesten Android-Frameworks</string>
|
||||||
|
<string name="send_file_step_sending_thumbnail">Miniaturbild wird gesendet (%1$s / %2$s)</string>
|
||||||
|
<string name="send_file_step_sending_file">Datei wird gesendet (%1$s / %2$s)</string>
|
||||||
|
|
||||||
|
<string name="downloading_file">Datei %1$s wird heruntergeladen …</string>
|
||||||
|
<string name="downloaded_file">Die Datei %1$s wurde heruntergeladen!</string>
|
||||||
|
|
||||||
|
<string name="riotx_no_registration_notice">%1$s , um ein Konto zu erstellen.</string>
|
||||||
|
<string name="riotx_no_registration_notice_colored_part">Benutze die alte App</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="room_filtering_footer_title">Können Sie nicht finden, wonach Sie suchen\?</string>
|
||||||
|
<string name="room_filtering_footer_create_new_room">Erstelle einen neuen Raum</string>
|
||||||
|
<string name="room_directory_search_hint">Name oder ID (#Beispiel: matrix.org)</string>
|
||||||
|
|
||||||
|
<string name="labs_swipe_to_reply_in_timeline">Aktivieren Sie das Streichen, um in der Zeitleiste zu antworten</string>
|
||||||
|
|
||||||
|
<string name="direct_room_no_known_users">Kein Ergebnis gefunden. Verwenden Sie Nach Matrix-ID hinzufügen, um auf dem Server zu suchen.</string>
|
||||||
|
<string name="direct_room_start_search">Beginnen Sie mit der Eingabe, um Ergebnisse zu erhalten</string>
|
||||||
|
<string name="direct_room_filter_hint">Filtern nach Benutzername oder ID…</string>
|
||||||
|
|
||||||
|
<string name="joining_room">Raum betreten…</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user