Merge branch 'develop' into feature/integration_manager

This commit is contained in:
ganfra 2020-05-11 15:48:27 +02:00
commit ce884ac577
44 changed files with 771 additions and 178 deletions

View file

@ -1,4 +1,28 @@
Changes in RiotX 0.19.0 (2020-XX-XX)
Changes in RiotX 0.20.0 (2020-XX-XX)
===================================================
Features ✨:
-
Improvements 🙌:
-
Bugfix 🐛:
- Sometimes the same device appears twice in the list of devices of a user (#1329)
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
-
Other changes:
-
Changes in RiotX 0.19.0 (2020-05-04)
===================================================
Features ✨:
@ -51,7 +75,7 @@ Bugfix 🐛:
- Fix bad Shield Logic for DM (#963)
Translations 🗣:
-
- Weblate now create PR directly to RiotX GitHub project
SDK API changes ⚠️:
- Increase targetSdkVersion to 29

View file

@ -19,13 +19,13 @@ package im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestData
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
/**
* Data class to store result of [KeysBackupTestHelper.createKeysBackupScenarioWithPassword]
*/
data class KeysBackupScenarioData(val cryptoTestData: CryptoTestData,
val aliceKeys: List<OlmInboundGroupSessionWrapper>,
val aliceKeys: List<OlmInboundGroupSessionWrapper2>,
val prepareKeysBackupDataResult: PrepareKeysBackupDataResult,
val aliceSession2: Session) {
fun cleanUp(testHelper: CommonTestHelper) {

View file

@ -446,7 +446,7 @@ internal class DefaultCryptoService @Inject constructor(
}
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
return cryptoStore.getUserDevices(userId)?.map { it.value }?.sortedBy { it.deviceId } ?: emptyList()
return cryptoStore.getUserDeviceList(userId) ?: emptyList()
}
override fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>> {

View file

@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.di.MoshiProvider
@ -488,7 +488,7 @@ internal class MXOlmDevice @Inject constructor(
forwardingCurve25519KeyChain: List<String>,
keysClaimed: Map<String, String>,
exportFormat: Boolean): Boolean {
val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat)
val session = OlmInboundGroupSessionWrapper2(sessionKey, exportFormat)
runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }
.fold(
{
@ -543,18 +543,18 @@ internal class MXOlmDevice @Inject constructor(
* @param megolmSessionsData the megolm sessions data
* @return the successfully imported sessions.
*/
fun importInboundGroupSessions(megolmSessionsData: List<MegolmSessionData>): List<OlmInboundGroupSessionWrapper> {
val sessions = ArrayList<OlmInboundGroupSessionWrapper>(megolmSessionsData.size)
fun importInboundGroupSessions(megolmSessionsData: List<MegolmSessionData>): List<OlmInboundGroupSessionWrapper2> {
val sessions = ArrayList<OlmInboundGroupSessionWrapper2>(megolmSessionsData.size)
for (megolmSessionData in megolmSessionsData) {
val sessionId = megolmSessionData.sessionId
val senderKey = megolmSessionData.senderKey
val roomId = megolmSessionData.roomId
var session: OlmInboundGroupSessionWrapper? = null
var session: OlmInboundGroupSessionWrapper2? = null
try {
session = OlmInboundGroupSessionWrapper(megolmSessionData)
session = OlmInboundGroupSessionWrapper2(megolmSessionData)
} catch (e: Exception) {
Timber.e(e, "## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
}
@ -741,7 +741,7 @@ internal class MXOlmDevice @Inject constructor(
* @param senderKey the base64-encoded curve25519 key of the sender.
* @return the inbound group session.
*/
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper {
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper2 {
if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) {
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)
}

View file

@ -48,7 +48,7 @@ internal class SetDeviceVerificationAction @Inject constructor(
if (device.trustLevel != trustLevel) {
device.trustLevel = trustLevel
cryptoStore.storeUserDevice(userId, device)
cryptoStore.setDeviceTrust(userId, deviceId, trustLevel.crossSigningVerified, trustLevel.locallyVerified)
}
}
}

View file

@ -66,7 +66,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.tasks.UpdateKeysBacku
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
@ -1318,7 +1318,7 @@ internal class DefaultKeysBackupService @Inject constructor(
@VisibleForTesting
@WorkerThread
fun encryptGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper): KeyBackupData {
fun encryptGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2): KeyBackupData {
// Gather information for each key
val device = cryptoStore.deviceWithIdentityKey(olmInboundGroupSessionWrapper.senderKey!!)

View file

@ -103,11 +103,10 @@ class OlmInboundGroupSessionWrapper : Serializable {
/**
* Export the inbound group session keys
* @param index the index to export. If null, the first known index will be used
*
* @return the inbound group session as MegolmSessionData if the operation succeeds
*/
fun exportKeys(index: Long? = null): MegolmSessionData? {
fun exportKeys(): MegolmSessionData? {
return try {
if (null == forwardingCurve25519KeyChain) {
forwardingCurve25519KeyChain = ArrayList()
@ -117,8 +116,6 @@ class OlmInboundGroupSessionWrapper : Serializable {
return null
}
val wantedIndex = index ?: olmInboundGroupSession!!.firstKnownIndex
MegolmSessionData(
senderClaimedEd25519Key = keysClaimed?.get("ed25519"),
forwardingCurve25519KeyChain = ArrayList(forwardingCurve25519KeyChain!!),
@ -126,7 +123,7 @@ class OlmInboundGroupSessionWrapper : Serializable {
senderClaimedKeys = keysClaimed,
roomId = roomId,
sessionId = olmInboundGroupSession!!.sessionIdentifier(),
sessionKey = olmInboundGroupSession!!.export(wantedIndex),
sessionKey = olmInboundGroupSession!!.export(olmInboundGroupSession!!.firstKnownIndex),
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
)
} catch (e: Exception) {

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.model
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.MegolmSessionData
import org.matrix.olm.OlmInboundGroupSession
import timber.log.Timber
import java.io.Serializable
/**
* This class adds more context to a OlmInboundGroupSession object.
* This allows additional checks. The class implements Serializable so that the context can be stored.
*/
class OlmInboundGroupSessionWrapper2 : Serializable {
// The associated olm inbound group session.
var olmInboundGroupSession: OlmInboundGroupSession? = null
// The room in which this session is used.
var roomId: String? = null
// The base64-encoded curve25519 key of the sender.
var senderKey: String? = null
// Other keys the sender claims.
var keysClaimed: Map<String, String>? = null
// Devices which forwarded this session to us (normally empty).
var forwardingCurve25519KeyChain: List<String>? = ArrayList()
/**
* @return the first known message index
*/
val firstKnownIndex: Long?
get() {
if (null != olmInboundGroupSession) {
try {
return olmInboundGroupSession!!.firstKnownIndex
} catch (e: Exception) {
Timber.e(e, "## getFirstKnownIndex() : getFirstKnownIndex failed")
}
}
return null
}
/**
* Constructor
*
* @param sessionKey the session key
* @param isImported true if it is an imported session key
*/
constructor(sessionKey: String, isImported: Boolean) {
try {
if (!isImported) {
olmInboundGroupSession = OlmInboundGroupSession(sessionKey)
} else {
olmInboundGroupSession = OlmInboundGroupSession.importSession(sessionKey)
}
} catch (e: Exception) {
Timber.e(e, "Cannot create")
}
}
constructor() {
// empty
}
/**
* Create a new instance from the provided keys map.
*
* @param megolmSessionData the megolm session data
* @throws Exception if the data are invalid
*/
@Throws(Exception::class)
constructor(megolmSessionData: MegolmSessionData) {
try {
olmInboundGroupSession = OlmInboundGroupSession.importSession(megolmSessionData.sessionKey!!)
if (olmInboundGroupSession!!.sessionIdentifier() != megolmSessionData.sessionId) {
throw Exception("Mismatched group session Id")
}
senderKey = megolmSessionData.senderKey
keysClaimed = megolmSessionData.senderClaimedKeys
roomId = megolmSessionData.roomId
} catch (e: Exception) {
throw Exception(e.message)
}
}
/**
* Export the inbound group session keys
* @param index the index to export. If null, the first known index will be used
*
* @return the inbound group session as MegolmSessionData if the operation succeeds
*/
fun exportKeys(index: Long? = null): MegolmSessionData? {
return try {
if (null == forwardingCurve25519KeyChain) {
forwardingCurve25519KeyChain = ArrayList()
}
if (keysClaimed == null) {
return null
}
val wantedIndex = index ?: olmInboundGroupSession!!.firstKnownIndex
MegolmSessionData(
senderClaimedEd25519Key = keysClaimed?.get("ed25519"),
forwardingCurve25519KeyChain = ArrayList(forwardingCurve25519KeyChain!!),
senderKey = senderKey,
senderClaimedKeys = keysClaimed,
roomId = roomId,
sessionId = olmInboundGroupSession!!.sessionIdentifier(),
sessionKey = olmInboundGroupSession!!.export(wantedIndex),
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
)
} catch (e: Exception) {
Timber.e(e, "## export() : senderKey $senderKey failed")
null
}
}
/**
* Export the session for a message index.
*
* @param messageIndex the message index
* @return the exported data
*/
fun exportSession(messageIndex: Long): String? {
if (null != olmInboundGroupSession) {
try {
return olmInboundGroupSession!!.export(messageIndex)
} catch (e: Exception) {
Timber.e(e, "## exportSession() : export failed")
}
}
return null
}
}

View file

@ -30,7 +30,7 @@ import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
@ -59,7 +59,7 @@ internal interface IMXCryptoStore {
*
* @return the list of all known group sessions, to export them.
*/
fun getInboundGroupSessions(): List<OlmInboundGroupSessionWrapper>
fun getInboundGroupSessions(): List<OlmInboundGroupSessionWrapper2>
/**
* @return true to unilaterally blacklist all unverified devices.
@ -164,14 +164,6 @@ internal interface IMXCryptoStore {
*/
fun saveOlmAccount()
/**
* Store a device for a user.
*
* @param userId the user's id.
* @param device the device to store.
*/
fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?)
/**
* Retrieve a device for a user.
*
@ -282,7 +274,7 @@ internal interface IMXCryptoStore {
*
* @param sessions the inbound group sessions to store.
*/
fun storeInboundGroupSessions(sessions: List<OlmInboundGroupSessionWrapper>)
fun storeInboundGroupSessions(sessions: List<OlmInboundGroupSessionWrapper2>)
/**
* Retrieve an inbound group session.
@ -291,7 +283,7 @@ internal interface IMXCryptoStore {
* @param senderKey the base64-encoded curve25519 key of the sender.
* @return an inbound group session.
*/
fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper?
fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2?
/**
* Remove an inbound group session
@ -315,7 +307,7 @@ internal interface IMXCryptoStore {
*
* @param sessions the sessions
*/
fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List<OlmInboundGroupSessionWrapper>)
fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List<OlmInboundGroupSessionWrapper2>)
/**
* Retrieve inbound group sessions that are not yet backed up.
@ -323,7 +315,7 @@ internal interface IMXCryptoStore {
* @param limit the maximum number of sessions to return.
* @return an array of non backed up inbound group sessions.
*/
fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper>
fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper2>
/**
* Number of stored inbound group sessions.
@ -415,7 +407,7 @@ internal interface IMXCryptoStore {
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean)
fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean?)
fun clearOtherUserTrust()

View file

@ -38,7 +38,7 @@ import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
@ -58,7 +58,6 @@ import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityF
import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEntity
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntity
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity
import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
@ -81,7 +80,6 @@ import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.SessionScope
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmList
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.olm.OlmAccount
@ -110,7 +108,7 @@ internal class RealmCryptoStore @Inject constructor(
private val olmSessionsToRelease = HashMap<String, OlmSessionWrapper>()
// Cache for InboundGroupSession, to release them properly
private val inboundGroupSessionToRelease = HashMap<String, OlmInboundGroupSessionWrapper>()
private val inboundGroupSessionToRelease = HashMap<String, OlmInboundGroupSessionWrapper2>()
private val newSessionListeners = ArrayList<NewSessionListener>()
@ -235,29 +233,6 @@ internal class RealmCryptoStore @Inject constructor(
return olmAccount!!
}
override fun storeUserDevice(userId: String?, deviceInfo: CryptoDeviceInfo?) {
if (userId == null || deviceInfo == null) {
return
}
doRealmTransaction(realmConfiguration) { realm ->
val user = UserEntity.getOrCreate(realm, userId)
// Create device info
val deviceInfoEntity = CryptoMapper.mapToEntity(deviceInfo)
realm.insertOrUpdate(deviceInfoEntity)
// val deviceInfoEntity = DeviceInfoEntity.getOrCreate(it, userId, deviceInfo.deviceId).apply {
// deviceId = deviceInfo.deviceId
// identityKey = deviceInfo.identityKey()
// putDeviceInfo(deviceInfo)
// }
if (!user.devices.contains(deviceInfoEntity)) {
user.devices.add(deviceInfoEntity)
}
}
}
override fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo? {
return doWithRealm(realmConfiguration) {
it.where<DeviceInfoEntity>()
@ -656,7 +631,7 @@ internal class RealmCryptoStore @Inject constructor(
.toMutableSet()
}
override fun storeInboundGroupSessions(sessions: List<OlmInboundGroupSessionWrapper>) {
override fun storeInboundGroupSessions(sessions: List<OlmInboundGroupSessionWrapper2>) {
if (sessions.isEmpty()) {
return
}
@ -694,7 +669,7 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper? {
override fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2? {
val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey)
// If not in cache (or not found), try to read it from realm
@ -714,10 +689,10 @@ internal class RealmCryptoStore @Inject constructor(
}
/**
* Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper,
* Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2,
* so there is no need to use or update `inboundGroupSessionToRelease` for native memory management
*/
override fun getInboundGroupSessions(): MutableList<OlmInboundGroupSessionWrapper> {
override fun getInboundGroupSessions(): MutableList<OlmInboundGroupSessionWrapper2> {
return doWithRealm(realmConfiguration) {
it.where<OlmInboundGroupSessionEntity>()
.findAll()
@ -789,7 +764,7 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List<OlmInboundGroupSessionWrapper>) {
override fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List<OlmInboundGroupSessionWrapper2>) {
if (olmInboundGroupSessionWrappers.isEmpty()) {
return
}
@ -812,7 +787,7 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper> {
override fun inboundGroupSessionsToBackup(limit: Int): List<OlmInboundGroupSessionWrapper2> {
return doWithRealm(realmConfiguration) {
it.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, false)
@ -1278,7 +1253,7 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean) {
override fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean?) {
doRealmTransaction(realmConfiguration) { realm ->
realm.where(DeviceInfoEntity::class.java)
.equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId))
@ -1291,7 +1266,7 @@ internal class RealmCryptoStore @Inject constructor(
deviceInfoEntity.trustLevelEntity = it
}
} else {
trustEntity.locallyVerified = locallyVerified
locallyVerified?.let { trustEntity.locallyVerified = it }
trustEntity.crossSignedVerified = crossSignedVerified
}
}
@ -1423,22 +1398,21 @@ internal class RealmCryptoStore @Inject constructor(
}
private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? {
var existing = CrossSigningInfoEntity.get(realm, userId)
if (info == null) {
// Delete known if needed
existing?.deleteFromRealm()
CrossSigningInfoEntity.get(realm, userId)?.deleteFromRealm()
return null
// TODO notify, we might need to untrust things?
} else {
// Just override existing, caller should check and untrust id needed
existing = CrossSigningInfoEntity.getOrCreate(realm, userId)
// existing.crossSigningKeys.forEach { it.deleteFromRealm() }
val xkeys = RealmList<KeyInfoEntity>()
info.crossSigningKeys.forEach { cryptoCrossSigningKey ->
val keyEntity = crossSigningKeysMapper.map(cryptoCrossSigningKey)
xkeys.add(keyEntity)
}
existing.crossSigningKeys = xkeys
val existing = CrossSigningInfoEntity.getOrCreate(realm, userId)
existing.crossSigningKeys.forEach { it.deleteFromRealm() }
existing.crossSigningKeys.addAll(
info.crossSigningKeys.map {
crossSigningKeysMapper.map(it)
}
)
return existing
}
return existing
}
}

View file

@ -21,6 +21,8 @@ import com.squareup.moshi.Types
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntityFields
@ -29,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEnt
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
@ -42,7 +45,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
// Version 1L added Cross Signing info persistence
companion object {
const val CRYPTO_STORE_SCHEMA_VERSION = 5L
const val CRYPTO_STORE_SCHEMA_VERSION = 6L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@ -53,6 +56,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
if (oldVersion <= 2) migrateTo3(realm)
if (oldVersion <= 3) migrateTo4(realm)
if (oldVersion <= 4) migrateTo5(realm)
if (oldVersion <= 5) migrateTo6(realm)
}
private fun migrateTo1(realm: DynamicRealm) {
@ -214,6 +218,23 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
}
} catch (failure: Throwable) {
}
// Migrate frozen classes
val inboundGroupSessions = realm.where("OlmInboundGroupSessionEntity").findAll()
inboundGroupSessions.forEach { dynamicObject ->
dynamicObject.getString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA)?.let { serializedObject ->
try {
deserializeFromRealm<OlmInboundGroupSessionWrapper?>(serializedObject)?.let { oldFormat ->
val newFormat = oldFormat.exportKeys()?.let {
OlmInboundGroupSessionWrapper2(it)
}
dynamicObject.setString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA, serializeForRealm(newFormat))
}
} catch (failure: Throwable) {
Timber.e(failure, "## OlmInboundGroupSessionEntity migration failed")
}
}
}
}
private fun migrateTo5(realm: DynamicRealm) {
@ -235,4 +256,22 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
}
}
}
// Fixes duplicate devices in UserEntity#devices
private fun migrateTo6(realm: DynamicRealm) {
val userEntities = realm.where("UserEntity").findAll()
userEntities.forEach {
try {
val deviceList = it.getList(UserEntityFields.DEVICES.`$`)
?: return@forEach
val distinct = deviceList.distinctBy { it.getString(DeviceInfoEntityFields.DEVICE_ID) }
if (distinct.size != deviceList.size) {
deviceList.clear()
deviceList.addAll(distinct)
}
} catch (failure: Throwable) {
Timber.w(failure, "Crypto Data base migration error for migrateTo6")
}
}
}
}

View file

@ -16,12 +16,12 @@
package im.vector.matrix.android.internal.crypto.store.db.model
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import timber.log.Timber
internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey"
@ -36,11 +36,16 @@ internal open class OlmInboundGroupSessionEntity(
var backedUp: Boolean = false)
: RealmObject() {
fun getInboundGroupSession(): OlmInboundGroupSessionWrapper? {
return tryThis { deserializeFromRealm<OlmInboundGroupSessionWrapper?>(olmInboundGroupSessionData) }
fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? {
return try {
deserializeFromRealm<OlmInboundGroupSessionWrapper2?>(olmInboundGroupSessionData)
} catch (failure: Throwable) {
Timber.e(failure, "## Deserialization failure")
return null
}
}
fun putInboundGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper?) {
fun putInboundGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2?) {
olmInboundGroupSessionData = serializeForRealm(olmInboundGroupSessionWrapper)
}

View file

@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.session.filter.FilterModule
import im.vector.matrix.android.internal.session.group.GetGroupDataWorker
import im.vector.matrix.android.internal.session.group.GroupModule
import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule
import im.vector.matrix.android.internal.session.openid.OpenIdModule
import im.vector.matrix.android.internal.session.profile.ProfileModule
import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker
import im.vector.matrix.android.internal.session.pushers.PushersModule
@ -70,6 +71,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
CacheModule::class,
CryptoModule::class,
PushersModule::class,
OpenIdModule::class,
AccountDataModule::class,
ProfileModule::class,
SessionAssistedInjectModule::class,

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.openid
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject
internal interface GetOpenIdTokenTask : Task<Unit, RequestOpenIdTokenResponse>
internal class DefaultGetOpenIdTokenTask @Inject constructor(
@UserId private val userId: String,
private val openIdAPI: OpenIdAPI,
private val eventBus: EventBus) : GetOpenIdTokenTask {
override suspend fun execute(params: Unit): RequestOpenIdTokenResponse {
return executeRequest(eventBus) {
apiCall = openIdAPI.openIdToken(userId)
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.openid
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
import retrofit2.http.Path
internal interface OpenIdAPI {
/**
* Gets a bearer token from the homeserver that the user can
* present to a third party in order to prove their ownership
* of the Matrix account they are logged into.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-user-userid-openid-request-token
*
* @param userId the user id
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/openid/request_token")
fun openIdToken(@Path("userId") userId: String, @Body body: JsonDict = emptyMap()): Call<RequestOpenIdTokenResponse>
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.openid
import dagger.Binds
import dagger.Module
import dagger.Provides
import retrofit2.Retrofit
@Module
internal abstract class OpenIdModule {
@Module
companion object {
@JvmStatic
@Provides
fun providesOpenIdAPI(retrofit: Retrofit): OpenIdAPI {
return retrofit.create(OpenIdAPI::class.java)
}
}
@Binds
abstract fun bindGetOpenIdTokenTask(task: DefaultGetOpenIdTokenTask): GetOpenIdTokenTask
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.openid
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class RequestOpenIdTokenResponse(
/**
* Required. An access token the consumer may use to verify the identity of the person who generated the token.
* This is given to the federation API GET /openid/userinfo to verify the user's identity.
*/
@Json(name = "access_token")
val openIdToken: String,
/**
* Required. The string "Bearer".
*/
@Json(name = "token_type")
val tokenType: String,
/**
* Required. The homeserver domain the consumer should use when attempting to verify the user's identity.
*/
@Json(name = "matrix_server_name")
val matrixServerName: String,
/**
* Required. The number of seconds before this token expires and a new one must be generated.
*/
@Json(name = "expires_in")
val expiresIn: Int
)

View file

@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.user.accountdata
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
@ -34,15 +33,4 @@ interface AccountDataAPI {
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/account_data/{type}")
fun setAccountData(@Path("userId") userId: String, @Path("type") type: String, @Body params: Any): Call<Unit>
/**
* Gets a bearer token from the homeserver that the user can
* present to a third party in order to prove their ownership
* of the Matrix account they are logged into.
*
* @param userId the user id
* @param body the body content
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/openid/request_token")
fun openIdToken(@Path("userId") userId: String, @Body body: Map<Any, Any>): Call<Map<Any, Any>>
}

View file

@ -202,4 +202,12 @@
<string name="notice_room_canonical_alias_set">%1$s настрой %2$s като основен адрес за тази стая.</string>
<string name="notice_room_canonical_alias_unset">%1$s премахна основния адрес за тази стая.</string>
<string name="notice_room_guest_access_can_join">%1$s разреши на гости да се присъединяват в стаята.</string>
<string name="notice_room_guest_access_forbidden">%1$s предотврати присъединяването на гости в стаята.</string>
<string name="notice_end_to_end_ok">%1$s включи шифроване от-край-до-край.</string>
<string name="notice_end_to_end_unknown_algorithm">%1$s включи шифроване от-край-до-край (неразпознат алгоритъм %2$s).</string>
<string name="key_verification_request_fallback_message">%s изпрати запитване за потвърждение на ключа ви, но клиентът ви не поддържа верифициране посредством чат. Ще трябва да използвате стария метод за верифициране на ключове.</string>
</resources>

View file

@ -0,0 +1,24 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="summary_user_sent_image">%1$s একটি ফটো পাঠিয়েছে।</string>
<string name="summary_user_sent_sticker">%1$s একটি স্তিকার পাঠিয়েছে।</string>
<string name="notice_room_invite_no_invitee">%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>
</resources>

View file

@ -5,8 +5,8 @@
<string name="notice_room_invite_no_invitee">Käyttäjän %s kutsu</string>
<string name="notice_room_invite">%1$s kutsui käyttäjän %2$s</string>
<string name="notice_room_invite_you">%1$s kutsui sinut</string>
<string name="notice_room_join">%1$s liittyi</string>
<string name="notice_room_leave">%1$s poistui</string>
<string name="notice_room_join">%1$s liittyi huoneeseen</string>
<string name="notice_room_leave">%1$s poistui huoneesta</string>
<string name="notice_room_reject">%1$s hylkäsi kutsun</string>
<string name="notice_room_kick">%1$s poisti käyttäjän %2$s</string>
<string name="notice_room_unban">%1$s poisti porttikiellon käyttäjältä %2$s</string>
@ -177,8 +177,8 @@
<string name="notice_room_invite_no_invitee_with_reason">Henkilön %1$s kutsu. Syy: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s kutsui henkilön %2$s. Syy: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s kutsui sinut. Syy: %2$s</string>
<string name="notice_room_join_with_reason">%1$s liittyi. Syy: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s poistui. Syy: %2$s</string>
<string name="notice_room_join_with_reason">%1$s liittyi huoneeseen. Syy: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s poistui huoneesta. Syy: %2$s</string>
<string name="notice_room_reject_with_reason">%1$s hylkäsi kutsun. Syy: %2$s</string>
<string name="notice_room_kick_with_reason">%1$s poisti käyttäjän %2$s huoneesta. Syy: %3$s</string>
<string name="notice_room_unban_with_reason">%1$s poisti porttikiellon käyttäjältä %2$s. Syy: %3$s</string>
@ -206,4 +206,7 @@
<string name="notice_room_guest_access_can_join">%1$s salli vieraiden liittyä huoneeseen.</string>
<string name="notice_room_guest_access_forbidden">%1$s esti vieraita liittymästä huoneeseen.</string>
<string name="notice_end_to_end_ok">%1$s laittoi päälle osapuolten välisen salauksen.</string>
<string name="notice_end_to_end_unknown_algorithm">%1$s laittoi päälle osapuolisten välisen salauksen (tuntematon algoritmi %2$s).</string>
</resources>

View file

@ -7,8 +7,8 @@
<string name="notice_room_invite_no_invitee">invitation de %s</string>
<string name="notice_room_invite">%1$s a invité %2$s</string>
<string name="notice_room_invite_you">%1$s vous a invité</string>
<string name="notice_room_join">%1$s a rejoint la discussion</string>
<string name="notice_room_leave">%1$s est parti</string>
<string name="notice_room_join">%1$s a rejoint le salon</string>
<string name="notice_room_leave">%1$s est parti du salon</string>
<string name="notice_room_reject">%1$s a rejeté linvitation</string>
<string name="notice_room_kick">%1$s a expulsé %2$s</string>
<string name="notice_room_unban">%1$s a révoqué le bannissement de %2$s</string>
@ -177,7 +177,7 @@
<string name="notice_room_invite_with_reason">%1$s a invité %2$s. Raison : %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s vous a invité. Raison : %2$s</string>
<string name="notice_room_join_with_reason">%1$s a rejoint le salon. Raison : %2$s</string>
<string name="notice_room_leave_with_reason">%1$s est parti. Raison : %2$s</string>
<string name="notice_room_leave_with_reason">%1$s est parti du salon. Raison : %2$s</string>
<string name="notice_room_reject_with_reason">%1$s a refusé linvitation. Raison : %2$s</string>
<string name="notice_room_kick_with_reason">%1$s a expulsé %2$s. Raison : %3$s</string>
<string name="notice_room_unban_with_reason">%1$s a révoqué le bannissement de %2$s. Raison : %3$s</string>

View file

@ -6,8 +6,8 @@
<string name="notice_room_invite_no_invitee">%s meghívója</string>
<string name="notice_room_invite">%1$s meghívta: %2$s</string>
<string name="notice_room_invite_you">%1$s meghívott</string>
<string name="notice_room_join">%1$s csatlakozott</string>
<string name="notice_room_leave">%1$s kilépett</string>
<string name="notice_room_join">%1$s belépett a szobába</string>
<string name="notice_room_leave">%1$s kilépett a szobából</string>
<string name="notice_room_reject">%1$s elutasította a meghívást</string>
<string name="notice_room_kick">%1$s kidobta: %2$s</string>
<string name="notice_room_unban">%1$s feloldotta %2$s tiltását</string>
@ -175,8 +175,8 @@
<string name="notice_room_invite_no_invitee_with_reason">%1$s meghívója. Ok: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s meghívta őt: %2$s. Ok: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s meghívott. Ok: %2$s</string>
<string name="notice_room_join_with_reason">%1$s csatlakozott. Ok: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s kilépett. Ok: %2$s</string>
<string name="notice_room_join_with_reason">%1$s belépett a szobába. Mert: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s kilépett a szobából. Ok: %2$s</string>
<string name="notice_room_reject_with_reason">%1$s visszautasította a meghívót. Ok: %2$s</string>
<string name="notice_room_kick_with_reason">%1$s kirúgta őt: %2$s. Ok: %3$s</string>
<string name="notice_room_unban_with_reason">%1$s visszaengedte őt: %2$s. Ok: %3$s</string>

View file

@ -6,8 +6,8 @@
<string name="notice_room_invite_no_invitee">Invito di %s</string>
<string name="notice_room_invite">%1$s ha invitato %2$s</string>
<string name="notice_room_invite_you">%1$s ti ha invitato</string>
<string name="notice_room_join">%1$s è entrato</string>
<string name="notice_room_leave">%1$s è uscito</string>
<string name="notice_room_join">%1$s è entrato nella stanza</string>
<string name="notice_room_leave">%1$s è uscito dalla stanza</string>
<string name="notice_room_reject">%1$s ha rifiutato l\'invito</string>
<string name="notice_room_kick">%1$s ha buttato fuori %2$s</string>
<string name="notice_room_unban">%1$s ha tolto il bando a %2$s</string>
@ -176,8 +176,8 @@
<string name="notice_room_invite_no_invitee_with_reason">Invito di %1$s. Motivo: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s ha invitato %2$s. Motivo: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s ti ha invitato. Motivo: %2$s</string>
<string name="notice_room_join_with_reason">%1$s è entrato. Motivo: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s è uscito. Motivo: %2$s</string>
<string name="notice_room_join_with_reason">%1$s è entrato nella stanza. Motivo: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s è uscito dalla stanza. Motivo: %2$s</string>
<string name="notice_room_reject_with_reason">%1$s ha rifiutato l\'invito. Motivo: %2$s</string>
<string name="notice_room_kick_with_reason">%1$s ha buttato fuori %2$s. Motivo: %3$s</string>
<string name="notice_room_unban_with_reason">%1$s ha riammesso %2$s. Motivo: %3$s</string>

View file

@ -174,4 +174,18 @@
<string name="clear_timeline_send_queue">Vymazať správy na odoslanie</string>
<string name="notice_room_third_party_revoked_invite">%1$s zamietol pozvanie používateľa %2$s vstúpiť do miestnosti</string>
<string name="notice_room_invite_no_invitee_with_reason">Pozvanie od používateľa %1$s. Dôvod: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s pozval používateľa %2$s. Dôvod: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s vás pozval. Dôvod: %2$s</string>
<string name="notice_room_join_with_reason">%1$s sa pridal. Dôvod: %2$s</string>
<string name="notice_room_leave_with_reason">Používateľ %1$s odišiel. Dôvod: %2$s</string>
<string name="notice_room_reject_with_reason">%1$s odmietol pozvanie. Dôvod: %2$s</string>
<string name="notice_room_kick_with_reason">%1$s vyhodil používateľa %2$s. Dôvod: %3$s</string>
<string name="notice_room_unban_with_reason">%1$s znovu pridaný používateľom %2$s. Dôvod: %3$s</string>
<string name="notice_room_ban_with_reason">%1$s vyhodil %2$s. Dôvod: %3$s</string>
<string name="notice_room_third_party_invite_with_reason">%1$s poslal pozvánku používateľovi %2$s, aby sa pripojil na miestnosť. Dôvod: %3$s</string>
<string name="notice_room_third_party_revoked_invite_with_reason">%1$s odvolal pozvánku pre používateľa %2$s, aby sa pripojil na miestnosť. Dôvod: %3$s</string>
<string name="notice_room_third_party_registered_invite_with_reason">%1$s prijal pozvanie od používateľa %2$s. Dôvod: %3$s</string>
<string name="notice_room_withdraw_with_reason">%1$s odoprel pozvánku používateľa %2$s. Dôvod: %3$s</string>
</resources>

View file

@ -6,8 +6,8 @@
<string name="notice_room_invite_no_invitee">%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_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>
@ -174,8 +174,8 @@
<string name="notice_room_invite_no_invitee_with_reason">%1$s 的邀請。理由:%2$s</string>
<string name="notice_room_invite_with_reason">%1$s 邀請了 %2$s。理由%3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s 邀請了您。理由:%2$s</string>
<string name="notice_room_join_with_reason">%1$s 已加入。理由:%2$s</string>
<string name="notice_room_leave_with_reason">%1$s 已離開。理由:%2$s</string>
<string name="notice_room_join_with_reason">%1$s 已加入聊天室。理由:%2$s</string>
<string name="notice_room_leave_with_reason">%1$s 已離開聊天室。理由:%2$s</string>
<string name="notice_room_reject_with_reason">%1$s 已回絕邀請。理由:%2$s</string>
<string name="notice_room_kick_with_reason">%1$s 踢走了 %2$s。理由%3$s</string>
<string name="notice_room_unban_with_reason">%1$s 取消封鎖了 %2$s。理由%3$s</string>

View file

@ -7,8 +7,8 @@
<string name="notice_room_invite_no_invitee">%s\'s invitation</string>
<string name="notice_room_invite">%1$s invited %2$s</string>
<string name="notice_room_invite_you">%1$s invited you</string>
<string name="notice_room_join">%1$s joined</string>
<string name="notice_room_leave">%1$s left</string>
<string name="notice_room_join">%1$s joined the room</string>
<string name="notice_room_leave">%1$s left the room</string>
<string name="notice_room_reject">%1$s rejected the invitation</string>
<string name="notice_room_kick">%1$s kicked %2$s</string>
<string name="notice_room_unban">%1$s unbanned %2$s</string>
@ -246,8 +246,8 @@
<string name="notice_room_invite_no_invitee_with_reason">%1$s\'s invitation. Reason: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s invited %2$s. Reason: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s invited you. Reason: %2$s</string>
<string name="notice_room_join_with_reason">%1$s joined. Reason: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s left. Reason: %2$s</string>
<string name="notice_room_join_with_reason">%1$s joined the room. Reason: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s left the room. Reason: %2$s</string>
<string name="notice_room_reject_with_reason">%1$s rejected the invitation. Reason: %2$s</string>
<string name="notice_room_kick_with_reason">%1$s kicked %2$s. Reason: %3$s</string>
<string name="notice_room_unban_with_reason">%1$s unbanned %2$s. Reason: %3$s</string>

View file

@ -23,7 +23,7 @@ PARAM_KS_PASS=$3
PARAM_KEY_PASS=$4
# Other params
BUILD_TOOLS_VERSION="28.0.3"
BUILD_TOOLS_VERSION="29.0.3"
MIN_SDK_VERSION=19
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."

View file

@ -15,7 +15,7 @@ androidExtensions {
}
ext.versionMajor = 0
ext.versionMinor = 19
ext.versionMinor = 20
ext.versionPatch = 0
static def getGitTimestamp() {

View file

@ -15,7 +15,6 @@
*/
package im.vector.riotx.features.crypto.keysbackup.restore
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.SpannableString
@ -70,7 +69,7 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
mPassphraseInputLayout.error = newValue
})
helperTextWithLink.text = spannableStringForHelperText(context!!)
helperTextWithLink.text = spannableStringForHelperText()
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
val shouldBeVisible = it ?: false
@ -87,9 +86,9 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
}
}
private fun spannableStringForHelperText(context: Context): SpannableString {
val clickableText = context.getString(R.string.keys_backup_restore_use_recovery_key)
val helperText = context.getString(R.string.keys_backup_restore_with_passphrase_helper_with_link, clickableText)
private fun spannableStringForHelperText(): SpannableString {
val clickableText = getString(R.string.keys_backup_restore_use_recovery_key)
val helperText = getString(R.string.keys_backup_restore_with_passphrase_helper_with_link, clickableText)
val spanString = SpannableString(helperText)
@ -117,7 +116,7 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
fun onRestoreBackup() {
val value = viewModel.passphrase.value
if (value.isNullOrBlank()) {
viewModel.passphraseErrorText.value = context?.getString(R.string.passphrase_empty_error_message)
viewModel.passphraseErrorText.value = getString(R.string.passphrase_empty_error_message)
} else {
viewModel.recoverKeys(sharedViewModel)
}

View file

@ -51,7 +51,7 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor(
try {
sharedViewModel.recoverUsingBackupPass(passphrase.value!!)
} catch (failure: Throwable) {
passphraseErrorText.value = stringProvider.getString(R.string.keys_backup_passphrase_error_decrypt)
passphraseErrorText.postValue(stringProvider.getString(R.string.keys_backup_passphrase_error_decrypt))
}
}
}

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.factory
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
@ -43,6 +44,9 @@ class MergedHeaderItemFactory @Inject constructor(private val sessionHolder: Act
private val collapsedEventIds = linkedSetOf<Long>()
private val mergeItemCollapseStates = HashMap<Long, Boolean>()
/**
* Note: nextEvent is an older event than event
*/
fun create(event: TimelineEvent,
nextEvent: TimelineEvent?,
items: List<TimelineEvent>,
@ -52,7 +56,8 @@ class MergedHeaderItemFactory @Inject constructor(private val sessionHolder: Act
callback: TimelineEventController.Callback?,
requestModelBuild: () -> Unit)
: BasedMergedItem<*>? {
return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE && event.isRoomConfiguration()) {
return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE
&& event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent>()?.creator)) {
// It's the first item before room.create
// Collapse all room configuration events
buildRoomCreationMergedSummary(currentPosition, items, event, eventIdToHighlight, requestModelBuild, callback)
@ -127,7 +132,7 @@ class MergedHeaderItemFactory @Inject constructor(private val sessionHolder: Act
val mergedEvents = ArrayList<TimelineEvent>().also { it.add(event) }
var hasEncryption = false
var encryptionAlgorithm: String? = null
while (prevEvent != null && prevEvent.isRoomConfiguration()) {
while (prevEvent != null && prevEvent.isRoomConfiguration(null)) {
if (prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) {
hasEncryption = true
encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel<EncryptionEventContent>()?.algorithm

View file

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.ReferencesAggregatedContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
import im.vector.matrix.android.api.session.room.send.SendState
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.hasBeenEdited
@ -123,7 +124,9 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
}
private fun getE2EDecoration(room: Room?, event: TimelineEvent): E2EDecoration {
return if (room?.isEncrypted() == true
return if (
event.root.sendState == SendState.SYNCED
&& room?.isEncrypted() == true
// is user verified
&& session.cryptoService().crossSigningService().getUserCrossSigningKeys(event.root.senderId ?: "")?.isTrusted() == true) {
val ts = room.roomSummary()?.encryptionEventTs ?: 0

View file

@ -50,14 +50,23 @@ fun TimelineEvent.canBeMerged(): Boolean {
return root.getClearType() == EventType.STATE_ROOM_MEMBER
}
fun TimelineEvent.isRoomConfiguration(): Boolean {
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
return when (root.getClearType()) {
EventType.STATE_ROOM_GUEST_ACCESS,
EventType.STATE_ROOM_HISTORY_VISIBILITY,
EventType.STATE_ROOM_JOIN_RULES,
EventType.STATE_ROOM_MEMBER,
EventType.STATE_ROOM_NAME,
EventType.STATE_ROOM_TOPIC,
EventType.STATE_ROOM_AVATAR,
EventType.STATE_ROOM_ALIASES,
EventType.STATE_ROOM_CANONICAL_ALIAS,
EventType.STATE_ROOM_POWER_LEVELS,
EventType.STATE_ROOM_ENCRYPTION -> true
EventType.STATE_ROOM_MEMBER -> {
// Keep only room member events regarding the room creator (when he joined the room),
// but exclude events where the room creator invite others, or where others join
roomCreatorUserId != null && root.stateKey == roomCreatorUserId
}
else -> false
}
}

View file

@ -43,7 +43,8 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
super.bind(holder)
if (attributes.isCollapsed) {
val data = distinctMergeData.firstOrNull()
// Take the oldest data
val data = distinctMergeData.lastOrNull()
val summary = holder.expandView.resources.getString(R.string.room_created_summary_item,
data?.memberName ?: data?.userId ?: "")

View file

@ -2210,7 +2210,7 @@ Vaši e-mailovou adresu můžete přidat k profilu v nastavení.</string>
<string name="refresh">Obnovit</string>
<string name="new_session">Neověřené přihlášení. Byli jste to Vy\?</string>
<string name="new_session">Nové přihlášení. Byli jste to Vy\?</string>
<string name="new_session_review">Klepněte pro přehled &amp; ověření</string>
<string name="verify_new_session_notice">Použijte tuto relaci k ověření relace nové, a tím ji udělíte přístup k zašifrovaným zprávám.</string>
<string name="verify_new_session_was_not_me">To jsem nebyl(a) já</string>

View file

@ -1783,10 +1783,10 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine
<string name="settings_text_message_sent">Eine Textnachricht wurde an %s gesendet. Bitte gebe den Verifizierungscode ein, den sie enthält.</string>
<string name="labs_allow_extended_logging">Aktiviere ausführliche Logs.</string>
<string name="labs_allow_extended_logging_summary">Gesprächige Logs wird den Entwicklern helfen indem sie mehr Informationen enthalten, wenn du einen Fehlerbericht sendest. Auch wenn dies aktiviert ist, werden keine Nachrichteninhalte oder andere privaten Daten aufgezeichnet.</string>
<string name="labs_allow_extended_logging_summary">Ausführliche Logs werden den Entwicklern helfen, indem sie mehr Informationen enthalten, wenn du einen Fehlerbericht sendest. Auch wenn dies aktiviert ist, werden keine Nachrichteninhalte oder andere privaten Daten aufgezeichnet.</string>
<string name="error_terms_not_accepted">Bitte erneut versuchen, nachdem du die Nutzungsbedingungendeines Home-Servers akzeptiert hast.</string>
<string name="error_terms_not_accepted">Bitte erneut versuchen, nachdem du die Nutzungsbedingungen deines Home-Servers akzeptiert hast.</string>
<string name="room_widget_permission_webview_shared_info_title">Bei Benutzung könnten Cookies gesetzt werden und es könnten Daten mit %s geteilt werden:</string>
<string name="room_widget_permission_shared_info_title">Bei Benutzung könnten Daten mit %s geteilt werden:</string>
@ -2280,7 +2280,7 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine
<string name="refresh">Neu laden</string>
<string name="new_session">Neue Sitzung</string>
<string name="new_session">Neue Anmeldung. Warst du das\?</string>
<string name="new_session_review">Tippe für eine Überprüfung &amp; Verifikation</string>
<string name="verify_new_session_notice">Benutze diese Sitzung um deine neue zu verfizieren, damit sie auf verschlüsselte Nachrichten zugreifen kann.</string>
<string name="verify_new_session_was_not_me">Das war ich nicht</string>
@ -2346,4 +2346,12 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine
<string name="auth_invalid_login_param_space_in_password">Inkorrekter Benutzername und/oder Passwort. Das eingegebene Passwort beginnt oder endet mit Leerzeichen, bitte kontrolliere es.</string>
<string name="message_key">Nachrichtenschlüssel</string>
<string name="account_password">Kontopasswort</string>
<string name="recovery_passphrase">Wiederherstellungs-Passphrase</string>
<string name="bootstrap_crosssigning_print_it">Druck es aus und speichere es an einem sicheren Ort</string>
<string name="bootstrap_crosssigning_save_cloud">Kopier es in deinen persönlichen Cloud-Speicher</string>
<string name="encryption_not_enabled">Verschlüsselung ist nicht aktiviert</string>
</resources>

View file

@ -2052,4 +2052,27 @@ Jotta et menetä mitään, automaattiset päivitykset kannattaa pitää käytös
<string name="room_profile_leaving_room">Poistutaan huoneesta…</string>
<string name="login_signup_username_hint">Käyttäjätunnus</string>
<string name="notification_initial_sync">Synkronoidaan tietoja ensimmäistä kertaa…</string>
<string name="settings_developer_mode">Kehittäjämoodi</string>
<string name="settings_developer_mode_summary">Kehittäjämoodi aktivoi piilotettuja ominaisuuksia, mutta voi tehdä sovelluksesta epävakaan. Vain kehittäjille!</string>
<string name="settings_rageshake">Raivoravistus</string>
<string name="settings_rageshake_detection_threshold">Tunnistusraja</string>
<string name="settings_rageshake_detection_threshold_summary">Ravista puhelintasi testataksesi tunnistusrajan</string>
<string name="rageshake_detected">Ravistus tunnistettu!</string>
<string name="autocomplete_limited_results">Näytetään vain ensimmäiset tulokset, kirjoita lisää kirjaimia…</string>
<string name="settings_developer_mode_fail_fast_summary">RiotX voi kaatuilla tavallista useammin odottamattomien virheiden vuoksi</string>
<string name="command_description_shrug">Lisää ¯\\_(ツ)_/¯ tavallisen viestin alkuun</string>
<string name="login_error_threepid_denied">Käyttämäsi sähköpostipalvelun ei ole sallittu rekisteröityä tälle palvelimelle</string>
<string name="verification_sas_match">Täsmäävät</string>
<string name="verification_sas_do_not_match">Eivät täsmää</string>
<string name="verify_user_sas_emoji_help_text">Vahvista käyttäjä tarkastamalla että seuraavat emojit vastaavat täysin heidän ruudullaan näkyviä.</string>
<string name="verify_user_sas_emoji_security_tip">Jos haluat kovaa tietoturvaa, käytä jotain toista luotettavaa viestintävälinettä tai tee toisen henkilön ollessa paikalla.</string>
<string name="verification_conclusion_not_secure">Ei turvallinen</string>
<string name="sent_a_video">Video.</string>
<string name="sent_an_image">Kuva.</string>
</resources>

View file

@ -145,7 +145,7 @@
<string name="login_error_not_json">Ne contient pas de JSON valide</string>
<string name="read_receipts_list">Liste des accusés de lecture</string>
<string name="compression_options">"Envoyer en "</string>
<string name="compression_options">Envoyer en</string>
<string name="compression_opt_list_original">Original</string>
<string name="compression_opt_list_large">Grand</string>
<string name="compression_opt_list_medium">Moyen</string>
@ -407,7 +407,7 @@
<string name="encryption_import_room_keys">Importer les clés des salons</string>
<string name="encryption_import_import">Importer</string>
<string name="encryption_never_send_to_unverified_devices_title">Chiffrer uniquement vers les sessions vérifiées</string>
<string name="encryption_information_not_verified">NON vérifié</string>
<string name="encryption_information_not_verified">Non vérifié</string>
<string name="encryption_information_verified">Vérifié</string>
<string name="encryption_information_blocked">Sur liste noire</string>
@ -473,7 +473,7 @@
<string name="attachment_cancel_upload">Annuler lenvoi ?</string>
<string name="attachment_remaining_time_seconds">%d s</string>
<string name="attachment_remaining_time_minutes">%1$dm %2$ds</string>
<string name="attachment_remaining_time_minutes">%1$d min %2$d s</string>
<string name="leave">Quitter</string>
<string name="quote">Citer</string>
@ -484,13 +484,13 @@
Veuillez autoriser laccès dans la prochaine fenêtre contextuelle pour pouvoir envoyer des fichiers depuis votre téléphone.</string>
<string name="permissions_rationale_msg_camera">Riot a besoin daccéder à votre appareil photo pour prendre des photos et passer des appels vidéo.</string>
<string name="permissions_rationale_msg_camera_explanation">
Veuillez autoriser laccès dans la prochaine fenêtre contextuelle pour pouvoir effectuer lappel.</string>
<string name="permissions_rationale_msg_camera_explanation">"
\n
\nVeuillez autoriser laccès dans la prochaine fenêtre contextuelle pour pouvoir effectuer lappel."</string>
<string name="permissions_rationale_msg_record_audio">Riot a besoin daccéder à votre microphone pour passer des appels audio.</string>
<string name="permissions_rationale_msg_record_audio_explanation">
Veuillez autoriser laccès dans la prochaine fenêtre contextuelle pour pouvoir effectuer lappel.</string>
<string name="permissions_rationale_msg_record_audio_explanation">"
\n
\nVeuillez autoriser laccès dans la prochaine fenêtre contextuelle pour pouvoir effectuer lappel."</string>
<string name="permissions_rationale_msg_camera_and_audio">Riot a besoin daccéder à votre appareil photo et à votre microphone pour passer des appels vidéo.
Veuillez autoriser laccès dans les prochaines fenêtres contextuelles pour pouvoir effectuer lappel.</string>
@ -602,8 +602,8 @@ Attention : ce fichier peut être supprimé si lapplication est désinstallé
<string name="encryption_import_room_keys_summary">Importer les clés à partir dun fichier local</string>
<string name="encryption_never_send_to_unverified_devices_summary">Ne jamais envoyer de messages chiffrés aux sessions non vérifiées depuis cette session.</string>
<string name="encryption_information_verify_device_warning">Pour vérifier que cette session est fiable, contactez son propriétaire en utilisant un autre moyen (par ex. en personne ou par téléphone) et demandez-lui si la clé quil voit dans ses paramètres utilisateur pour cette session correspond à la clé ci-dessous :</string>
<string name="encryption_information_verify_device_warning2">Si elle correspond, appuyez sur le bouton « Vérifier » ci-dessous. Si ce nest pas le cas, quelquun dautre intercepte cette session et vous devriez probablement le bloquer. À lavenir, ce processus de vérification sera plus évolué.</string>
<string name="encryption_information_verify_device_warning">Confirmez en comparant les informations suivantes avec les paramètres utilisateur dans votre autre session :</string>
<string name="encryption_information_verify_device_warning2">Si elles ne correspondent pas, la sécurité de votre communication est peut-être compromise.</string>
<string name="encryption_information_verify_key_match">Je confirme que les clés correspondent</string>
<string name="e2e_enabling_on_app_update">Riot prend désormais en charge le chiffrement de bout en bout, mais vous devez vous reconnecter pour lactiver.
@ -2136,7 +2136,7 @@ Si vous navez pas configuré de nouvelle méthode de récupération, un attaq
<string name="settings_active_sessions_list">Sessions actives</string>
<string name="settings_active_sessions_show_all">Afficher toutes les sessions</string>
<string name="settings_active_sessions_manage">Gérer les sessions</string>
<string name="settings_active_sessions_signout_device">Déconnecter cette session</string>
<string name="settings_active_sessions_signout_device">Se déconnecter de cette session</string>
<string name="settings_failed_to_get_crypto_device_info">Aucune information cryptographique nest disponible</string>
@ -2192,7 +2192,7 @@ Si vous navez pas configuré de nouvelle méthode de récupération, un attaq
</plurals>
<string name="poll_item_selected_aria">Option sélectionnée</string>
<string name="command_description_poll">Crée un sondage simple</string>
<string name="verification_cannot_access_other_session">Utiliser une méthode de récupération</string>
<string name="verification_cannot_access_other_session">Utiliser une phrase secrète ou une clé de récupération</string>
<string name="verification_use_passphrase">Si vous navez pas accès à une session existante</string>
<string name="new_signin">Nouvelle connexion</string>
@ -2227,7 +2227,7 @@ Si vous navez pas configuré de nouvelle méthode de récupération, un attaq
<string name="refresh">Actualiser</string>
<string name="new_session">Connexion non vérifiée. Était-ce vous \?</string>
<string name="new_session">Nouvelle connexion. Était-ce vous \?</string>
<string name="new_session_review">Appuyer pour examiner et vérifier</string>
<string name="verify_new_session_notice">Utilisez cette session pour vérifiez la nouvelle, ce qui lui permettra davoir accès aux messages chiffrés.</string>
<string name="verify_new_session_was_not_me">Ce nétait pas moi</string>
@ -2358,4 +2358,39 @@ Si vous navez pas configuré de nouvelle méthode de récupération, un attaq
<string name="error_adding_media_file_to_gallery">Impossible dajouter le fichier multimédia à la galerie</string>
<string name="change_password_summary">Définir un nouveau mot de passe de compte…</string>
<string name="use_other_session_content_description">Utilisez la dernière version de Riot sur vos autres appareils : Riot Web, Riot pour Bureau, Riot iOS, RiotX pour Android, ou un autre client Matrix qui prend en charge la signature croisée</string>
<string name="riot_desktop_web">Riot Web
\nRiot pour Bureau</string>
<string name="riot_ios_android">Riot iOS
\nRiot X pour Android</string>
<string name="or_other_mx_capabale_client">ou un autre client Matrix qui prend en charge la signature croisée</string>
<string name="use_latest_riot">Utilisez la dernière version de Riot sur vos autre appareils :</string>
<string name="command_description_discard_session">Force la session de groupe sortante actuelle dans un salon chiffré à être abandonnée</string>
<string name="command_description_discard_session_not_handled">Seulement pris en charge dans les salons chiffrés</string>
<string name="enter_secret_storage_passphrase_or_key">Utilisez votre %1$s ou votre %2$s pour continuer.</string>
<string name="use_recovery_key">Utiliser la clé de récupération</string>
<string name="enter_secret_storage_input_key">Sélectionnez votre clé de récupération ou saisissez-la manuellement avec le clavier ou en la copiant depuis le presse-papiers</string>
<string name="keys_backup_recovery_key_error_decrypt">La sauvegarde na pas pu être déchiffrée avec cette clé de récupération : veuillez vérifier que vous avez saisi la bonne clé de récupération.</string>
<string name="failed_to_access_secure_storage">Échec daccès au coffre secret</string>
<string name="cross_signing_verify_by_text">Vérifier manuellement avec un texte</string>
<string name="crosssigning_verify_session">Vérifier la connexion</string>
<string name="cross_signing_verify_by_emoji">Vérifier de façon interactive avec des émojis</string>
<string name="confirm_your_identity">Confirmez votre identité en vérifiant cette connexion depuis une de vos autres sessions, ce qui lui permettra davoir accès à vos messages chiffrés.</string>
<string name="mark_as_verified">Marquer comme fiable</string>
<string name="error_empty_field_choose_user_name">Veuillez choisir un nom dutilisateur.</string>
<string name="error_empty_field_choose_password">Veuillez choisir un mot de passe.</string>
<string name="external_link_confirmation_title">Vérifiez ce lien</string>
<string name="external_link_confirmation_message">Le lien %1$s vous emmène à un autre site : %2$s.
\n
\nVoulez-vous vraiment continuer \?</string>
<string name="create_room_dm_failure">Nous navons pas pu créer votre conversation directe. Vérifiez les utilisateurs que vous souhaitez inviter et réessayez.</string>
<string name="unencrypted">Non chiffré</string>
<string name="encrypted_unverified">Chiffré par un appareil non vérifié</string>
<string name="review_logins">Vérifiez où vous vous êtes connecté</string>
<string name="verify_other_sessions">Vérifiez toutes les sessions pour vous assurer que votre compte et vos messages sont en sécurité</string>
<string name="verify_this_session">Vérifiez la nouvelle connexion accédant à votre compte : %1$s</string>
</resources>

View file

@ -2222,7 +2222,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<string name="refresh">Frissítés</string>
<string name="new_session">Ismeretlen bejelentkezés. Ez te vagy\?</string>
<string name="new_session">Új bejelentkezés. Ez te vagy\?</string>
<string name="new_session_review">A megtekintéshez és ellenőrzéshez koppints</string>
<string name="verify_new_session_notice">Az új munkamenet ellenőrzéséhez használd ezt, amivel hozzáférést adsz a titkosított üzenetekhez.</string>
<string name="verify_new_session_was_not_me">Nem én voltam</string>
@ -2353,4 +2353,33 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<string name="error_adding_media_file_to_gallery">A média fájlt nem sikerült hozzáadni a Galériához</string>
<string name="change_password_summary">Új fiók jelszó beállítása…</string>
<string name="riot_desktop_web">Riot Web
\nRiot Desktop</string>
<string name="riot_ios_android">Riot iOS
\nRiot X for Android</string>
<string name="or_other_mx_capabale_client">vagy másik eszközök közötti hitelesítésre alkalmas Matrix kliensre</string>
<string name="use_latest_riot">A Riot legújabb kliensét használd a többi eszközödön:</string>
<string name="command_description_discard_session">A jelenlegi csoport munkamenet törlését kikényszeríti a titkosított szobában</string>
<string name="command_description_discard_session_not_handled">Csak a titkosított szobákban támogatott</string>
<string name="enter_secret_storage_passphrase_or_key">Használd ezt: %1$s vagy ezt: %2$s a továbblépéshez.</string>
<string name="use_recovery_key">Használd a Visszaállítási Kulcsot</string>
<string name="enter_secret_storage_input_key">Válaszd ki a Visszaállítási Kulcsot, add meg kézzel vagy másold be a vágólapról</string>
<string name="keys_backup_recovery_key_error_decrypt">Ezzel a Visszaállítási Kulccsal a mentést nem lehet visszafejteni: kérlek ellenőrizd, hogy a visszaállítási kulcsot jól adtad-e meg.</string>
<string name="failed_to_access_secure_storage">A biztonsági tárolóhoz nem sikerült hozzáférni</string>
<string name="unencrypted">Titkosítatlan</string>
<string name="encrypted_unverified">Ellenőrizetlen eszközzel titkosította</string>
<string name="review_logins">Tekintsd át hol vagy bejelentkezve</string>
<string name="verify_other_sessions">Ellenőrizd minden munkamenetedet, hogy a fiókod és az üzeneteid biztonságban legyenek</string>
<string name="verify_this_session">Ellenőrizd ezt az új bejelentkezést ami hozzáfér a fiókodhoz: %1$s</string>
<string name="cross_signing_verify_by_text">Manuális szöveges ellenőrzés</string>
<string name="crosssigning_verify_session">Belépés ellenőrzése</string>
<string name="cross_signing_verify_by_emoji">Közös ellenőrzés Emodzsival</string>
<string name="mark_as_verified">Megbízhatónak jelölés</string>
<string name="error_empty_field_choose_user_name">Kérlek válasz felhasználói nevet.</string>
<string name="error_empty_field_choose_password">Kérlek válassz jelszót.</string>
<string name="external_link_confirmation_title">Ezt a hivatkozást ellenőrizd le még egyszer</string>
<string name="create_room_dm_failure">A közvetlen üzenetedet nem sikerült elkészíteni. Ellenőrizd azokat a felhasználókat akiket meg szeretnél hívni és próbáld újra.</string>
</resources>

View file

@ -668,8 +668,8 @@
<string name="encryption_information_unblock">Togli dalla lista nera</string>
<string name="encryption_information_verify_device">Verifica la sessione</string>
<string name="encryption_information_verify_device_warning">Per verificare se questa sessione sia affidabile, contatta il suo proprietario utilizzando altre vie di comunicazione (es. di persona o per telefono) e chiedigli se la chiave che vede nelle Impostazioni Utente per questa sessione corrisponde a quella riportata sotto:</string>
<string name="encryption_information_verify_device_warning2">Se corrisponde, premi il pulsante di verifica qui sotto. In caso contrario, qualcun altro sta intercettando questa sessione e probabilmente dovresti metterlo in lista nera. In futuro il processo di verifica sarà più sofisticato.</string>
<string name="encryption_information_verify_device_warning">Conferma confrontando la seguente con le impostazioni utente della tua altra sessione:</string>
<string name="encryption_information_verify_device_warning2">Se non corrispondono, la sicurezza delle tue comunicazioni potrebbe essere compromessa.</string>
<string name="encryption_information_verify_key_match">Ho verificato che le chiavi corrispondono</string>
<string name="e2e_enabling_on_app_update">Riot supporta ora la crittografia da-utente-a-utente, ma per abilitarla devi riconnetterti.
@ -2193,7 +2193,7 @@
<item quantity="other">%d sessioni attive</item>
</plurals>
<string name="crosssigning_verify_this_session">Verifica questa sessione</string>
<string name="crosssigning_verify_this_session">Verifica questo accesso</string>
<string name="crosssigning_other_user_not_trust">Gli altri utenti potrebbero non fidarsi</string>
<string name="complete_security">Completa la sicurezza</string>
@ -2237,7 +2237,7 @@
</plurals>
<string name="poll_item_selected_aria">Opzione selezionata</string>
<string name="command_description_poll">Crea un semplice sondaggio</string>
<string name="verification_cannot_access_other_session">Usa un metodo di recupero</string>
<string name="verification_cannot_access_other_session">Usa una password o chiave di recupero</string>
<string name="verification_use_passphrase">Se non puoi accedere a una sessione esistente</string>
<string name="new_signin">Nuovo accesso</string>
@ -2272,7 +2272,7 @@
<string name="refresh">Ricarica</string>
<string name="new_session">Accesso non verificato. Eri tu\?</string>
<string name="new_session">Nuovo accesso. Eri tu\?</string>
<string name="new_session_review">Tocca per controllare e verificare</string>
<string name="verify_new_session_notice">Usa questa sessione per verificare quella nuova, dandole l\'accesso ai messaggi cifrati.</string>
<string name="verify_new_session_was_not_me">Non ero io</string>
@ -2403,4 +2403,39 @@
<string name="error_adding_media_file_to_gallery">Impossibile aggiungere il file multimediale alla galleria</string>
<string name="change_password_summary">Imposta una nuova password dell\'account…</string>
<string name="use_other_session_content_description">Usa l\'ultima versione di Riot sui tuoi altri dispositivi, Riot Web, Riot Desktop, Riot iOS, RiotX per Android o un altro client Matrix che supporti la firma incrociata</string>
<string name="riot_desktop_web">Riot Web
\nRiot Desktop</string>
<string name="riot_ios_android">Riot iOS
\nRiot X per Android</string>
<string name="or_other_mx_capabale_client">o un altro client Matrix che supporti la firma incrociata</string>
<string name="use_latest_riot">Usa l\'ultimo Riot sui tuoi altri dispositivi:</string>
<string name="command_description_discard_session">Forza l\'attuale sessione di gruppo in uscita in una stanza cifrata ad essere scartata</string>
<string name="command_description_discard_session_not_handled">Supportato solo nelle stanze cifrate</string>
<string name="enter_secret_storage_passphrase_or_key">Usa la tua %1$s o la %2$s per continuare.</string>
<string name="use_recovery_key">Usa la chiave di recupero</string>
<string name="enter_secret_storage_input_key">Seleziona la tua chiave di recupero, oppure inseriscila a mano digitandola o incollando dagli appunti</string>
<string name="keys_backup_recovery_key_error_decrypt">Impossibile decifrare il backup con questa chiave di recupero: verifica di avere inserito la chiave giusta.</string>
<string name="failed_to_access_secure_storage">Accesso all\'archivio sicuro fallito</string>
<string name="unencrypted">Non cifrato</string>
<string name="encrypted_unverified">Cifrato da un dispositivo non verificato</string>
<string name="review_logins">Controlla dove hai fatto l\'accesso</string>
<string name="verify_other_sessions">Verifica tutte le tue sessioni per assicurarti che il tuo account e i messaggi siano protetti</string>
<string name="verify_this_session">Verifica il nuovo accesso entrando nel tuo account: %1$s</string>
<string name="cross_signing_verify_by_text">Verifica manualmente con testo</string>
<string name="crosssigning_verify_session">Verifica accesso</string>
<string name="cross_signing_verify_by_emoji">Verifica interattivamente con emoji</string>
<string name="confirm_your_identity">Conferma la tua identità verificando questo accesso da una delle tua altre sessioni, dandole l\'accesso ai messaggi cifrati.</string>
<string name="mark_as_verified">Segna come fidato</string>
<string name="error_empty_field_choose_user_name">Scegli un nome utente.</string>
<string name="error_empty_field_choose_password">Scegli una password.</string>
<string name="external_link_confirmation_title">Verifica questo collegamento</string>
<string name="external_link_confirmation_message">Il collegamento %1$s ti sta portando ad un altro sito: %2$s.
\n
\nSei sicuro di volere continuare\?</string>
<string name="create_room_dm_failure">Impossibile creare il messaggio diretto. Controlla gli utenti che vuoi invitare e riprova.</string>
</resources>

View file

@ -632,7 +632,7 @@
<string name="encryption_never_send_to_unverified_devices_title">Шифровать только для проверенных сессий</string>
<string name="encryption_never_send_to_unverified_devices_summary">Не отправлять зашифрованные сообщения непроверенным сессиям с этой сессии.</string>
<string name="encryption_information_not_verified">НЕ проверено</string>
<string name="encryption_information_not_verified">Не проверено</string>
<string name="encryption_information_verified">Проверено</string>
<string name="encryption_information_blocked">В черном списке</string>
@ -1450,12 +1450,12 @@
<plurals name="notification_compat_summary_line_for_room">
<item quantity="one">%1$s: 1 сообщение</item>
<item quantity="few">%1$s: %2$d сообщения</item>
<item quantity="many">%1$s: %2$d сообщения</item>
<item quantity="many">%1$s: %2$d сообщений</item>
</plurals>
<plurals name="notification_compat_summary_title">
<item quantity="one">%d оповещение</item>
<item quantity="few">%d оповещения</item>
<item quantity="many">%d оповещения</item>
<item quantity="many">%d оповещений</item>
</plurals>
<string name="notification_unknown_new_event">Новое событие</string>

View file

@ -7,7 +7,7 @@
<string name="dark_theme">Tmavý vzhľad</string>
<string name="black_them">Čierny vzhľad</string>
<string name="notification_sync_in_progress">Synchronizácia…</string>
<string name="notification_sync_in_progress">Prebieha synchronizácia…</string>
<string name="notification_listening_for_events">Spracovanie udalostí</string>
<string name="title_activity_home">Správy</string>
@ -1306,4 +1306,27 @@ Objavte skutočne výkonné možnosti otvorenej spolupráce s Riot.im!</string>
<string name="error_empty_field_enter_user_name">Prosím zadajte meno používateľa.</string>
<string name="title_activity_verify_device">Overiť zariadenie</string>
<string name="resources_script">Latn</string>
<string name="none">Žiadny</string>
<string name="revoke">Zrušiť</string>
<string name="disconnect">Odpojiť sa</string>
<string name="review">Náhlad</string>
<string name="decline">Odmietnuť</string>
<string name="people_no_identity_server">Nebol nakonfigurovaný overovací server.</string>
<string name="call_failed_no_ice_title">Hovor zlyhal kvôli zle nakonfigurovanému serveru</string>
<string name="call_failed_no_ice_description">Prosím, požiadajte administrátora vášho homeserveru (%1$s) o konfiguráciu TURN serveru, aby hovory fungovali spoľahlivo.
\n
\nPrípadně môžete skúsiť použiť verejný server %2$s, ale nebude tak spoľahlivý a bude zdielať vašu IP adresu s týmto serverom. V nastaveniach sa to dá zmeniť.</string>
<string name="call_failed_no_ice_use_alt">Skúste použiť %s</string>
<string name="call_failed_dont_ask_again">Viac sa nepýtať</string>
<string name="auth_add_email_message_2">Nastavte e-mail na obnovenie účtu a voliteľne aby ho neskôr ľudia, ktorí vás poznajú, mohli nájsť.</string>
<string name="auth_add_phone_message_2">Nastavte telefónne číslo, aby neskôr bolo voliteľne k nájdeniu ľuďmi, ktorí Vás poznajú.</string>
<string name="auth_add_email_phone_message_2">Nastavte e-mail na obnovenie účtu. Použite e-mail alebo telefónne číslo voliteľne neskôr, aby ich našli ľudia, ktorí vás poznajú.</string>
<string name="auth_add_email_and_phone_message_2">Nastavte e-mail na obnovenie účtu. Použite e-mail alebo telefónne číslo voliteľne neskôr, aby ich našli ľudia, ktorí vás poznajú.</string>
<string name="login_error_no_homeserver_found">Toto nie je platná adresa Matrix serveru</string>
<string name="login_error_homeserver_not_found">Homeserver nie je dostupný na tomto URL, prosím, preverte</string>
</resources>

View file

@ -218,7 +218,7 @@
<string name="groups_list">群組清單</string>
<string name="compression_options">"傳送為 "</string>
<string name="compression_options">傳送為</string>
<string name="compression_opt_list_original">原始</string>
<string name="compression_opt_list_large"></string>
<string name="compression_opt_list_medium"></string>
@ -258,13 +258,13 @@
請在下個彈跳視窗允許存取,來從手機傳送檔案。</string>
<string name="permissions_rationale_msg_camera">Riot 需要權限存取您的相機,來拍照與視訊通話。</string>
<string name="permissions_rationale_msg_camera_explanation">
為了要通話,請在下個彈跳視窗中允許存取。</string>
<string name="permissions_rationale_msg_camera_explanation">"
\n
\n為了要通話,請在下個彈跳視窗中允許存取。"</string>
<string name="permissions_rationale_msg_record_audio">Riot 需要權限來存取麥克風,來撥打語音通話。</string>
<string name="permissions_rationale_msg_record_audio_explanation">
為了要通話,請在下個彈跳視窗中允許存取。</string>
<string name="permissions_rationale_msg_record_audio_explanation">"
\n
\n為了要通話,請在下個彈跳視窗中允許存取。"</string>
<string name="permissions_rationale_msg_camera_and_audio">Riot 需要權限來存取相機及麥克風來撥打視訊通話。
為了要通話,請在下個彈跳視窗中允許存取。</string>
@ -736,8 +736,8 @@
<string name="encryption_information_unblock">解除黑名單</string>
<string name="encryption_information_verify_device">驗證工作階段</string>
<string name="encryption_information_verify_device_warning">要驗證此工作階段是否可信,請使用其他方式(例如:面對面或是打電話)聯絡它的擁有者並詢問他們在使用者設定中看到此工作階段的金鑰是否與下列的金鑰相符</string>
<string name="encryption_information_verify_device_warning2">如果符合的話,請按下方的驗證按鈕。如果沒有的話,那麼其他地方的某個人可能正在攔截此工作階段,您應該將其列入黑名單。未來,這個驗證程序會變得更加複雜</string>
<string name="encryption_information_verify_device_warning">透過將以下內容與您的其他工作階段中的使用者設定來確認</string>
<string name="encryption_information_verify_device_warning2">如果不符合的話,您的通訊安全可能正受到威脅</string>
<string name="encryption_information_verify_key_match">我驗證金鑰相符</string>
<string name="e2e_enabling_on_app_update">Riot 目前支援端到端加密,但您需要重新登入以啟用。
@ -915,7 +915,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
<string name="room_participants_now">目前 %1$s</string>
<string name="room_participants_ago">%1$s %2$s 前</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 與 %2$s</string>
<string name="room_participants_invite_join_names_combined">%1$s %2$s</string>
@ -2093,7 +2093,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
<item quantity="other">%d 活躍的工作階段</item>
</plurals>
<string name="crosssigning_verify_this_session">驗證此工作階段</string>
<string name="crosssigning_verify_this_session">驗證此登入</string>
<string name="crosssigning_other_user_not_trust">其他使用者可能不會信任它</string>
<string name="complete_security">全面的安全性</string>
@ -2135,7 +2135,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
</plurals>
<string name="poll_item_selected_aria">已選取的選項</string>
<string name="command_description_poll">建立簡易投票</string>
<string name="verification_cannot_access_other_session">使用復原方法</string>
<string name="verification_cannot_access_other_session">使用復原通關密語或金鑰</string>
<string name="verification_use_passphrase">如果您無法存取既有的工作階段的話</string>
<string name="new_signin">新登入</string>
@ -2169,7 +2169,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
<string name="refresh">重新整理</string>
<string name="new_session">未驗證的登入。是您嗎?</string>
<string name="new_session">登入。是您嗎?</string>
<string name="new_session_review">輕觸即可以審閱並驗證</string>
<string name="verify_new_session_notice">使用此工作階段來驗證新的,讓它可以存取已加密的訊息。</string>
<string name="verify_new_session_was_not_me">這不是我</string>
@ -2300,4 +2300,39 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
<string name="error_adding_media_file_to_gallery">無法新增媒體檔案到媒體庫中</string>
<string name="change_password_summary">設定新的帳號密碼……</string>
<string name="use_other_session_content_description">在您的其他裝置上使用最新的 Riot、Riot Web、Riot 桌面版、Riot iOS、RiotX for Android 或其他有交叉簽章功能的 Matrix 客戶端</string>
<string name="riot_desktop_web">Riot Web
\nRiot 桌面版</string>
<string name="riot_ios_android">Riot iOS
\nRiot X for Android</string>
<string name="or_other_mx_capabale_client">或其他有交叉簽章功能的 Matrix 客戶端</string>
<string name="use_latest_riot">在您的其他裝置上使用最新的 Riot</string>
<string name="command_description_discard_session">強制丟棄目前在加密聊天室中的外發群組工作階段</string>
<string name="command_description_discard_session_not_handled">僅在加密聊天室中支援</string>
<string name="enter_secret_storage_passphrase_or_key">使用您的 %1$s 或使用您的 %2$s 以繼續。</string>
<string name="use_recovery_key">使用復原金鑰</string>
<string name="enter_secret_storage_input_key">選取您的復原金鑰,或是透過打字或從您的剪貼簿貼上來手動輸入</string>
<string name="keys_backup_recovery_key_error_decrypt">無法使用此復原金鑰解密備份:請驗證您是否輸入了正確的復原金鑰。</string>
<string name="failed_to_access_secure_storage">存取安全儲存空間失敗</string>
<string name="cross_signing_verify_by_text">透過文字手動驗證</string>
<string name="crosssigning_verify_session">驗證登入</string>
<string name="cross_signing_verify_by_emoji">透過顏文字來進行互動驗證</string>
<string name="confirm_your_identity">從您的其他工作階段驗證此登入以確認您的身份並授予存取加密訊息的權限。</string>
<string name="mark_as_verified">標記為受信任</string>
<string name="error_empty_field_choose_user_name">請選擇使用者名稱。</string>
<string name="error_empty_field_choose_password">請選擇密碼。</string>
<string name="external_link_confirmation_title">仔細檢查此連結</string>
<string name="external_link_confirmation_message">連結 %1$s 正在將您帶往其他網站:%2$s。
\n
\n您確定您想要繼續嗎</string>
<string name="create_room_dm_failure">我們無法建立您的直接訊息。請檢查您想要邀請的使用者,然後再試一次。</string>
<string name="unencrypted">未加密</string>
<string name="encrypted_unverified">由未驗證的裝置加密</string>
<string name="review_logins">審閱您從何處登入</string>
<string name="verify_other_sessions">驗證您所有的工作階段以確保您的帳號與訊息都安全</string>
<string name="verify_this_session">驗證正在存取您帳號的新登入:%1$s</string>
</resources>