Merge remote-tracking branch 'upstream/develop' into direct_share

This commit is contained in:
Constantin Wartenburger 2020-10-13 20:26:07 +02:00
commit afe55ae57e
No known key found for this signature in database
GPG key ID: 7439D96D8E1DB894
376 changed files with 2926 additions and 1433 deletions

View file

@ -4,16 +4,7 @@
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
</value>
</option>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />

View file

@ -18,8 +18,10 @@ Improvements 🙌:
- Filter room member (and banned users) by name (#2184)
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
- Direct share (#2029)
- Add FAB to room members list (#2226)
- Add Sygnal API implementation to test is Push are correctly received
- Add PushGateway API implementation to test if Push are correctly received
- Cross signing: shouldn't offer to verify with other session when there is not. (#2227)
Bugfix 🐛:
- Improve support for image/audio/video/file selection with intent changes (#1376)
@ -27,9 +29,10 @@ Bugfix 🐛:
- Invalid popup when pressing back (#1635)
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
- Very long topic cannot be fully visible (#1957)
- Properly detect cross signing keys reset
Translations 🗣:
-
- Move store data to `/fastlane/metadata/android` (#812)
SDK API changes ⚠️:
- Search messages in a room by using Session.searchService() or Room.search()

View file

@ -0,0 +1 @@
Element (o novo Riot.im)

View file

@ -13,7 +13,7 @@ Element здатен забезпечити усе це завдяки тому,
Element надає вам повний контроль, дозволяючи обирати з-поміж надавачів послуг, що обслуговують сервери з вашими бесідами. Ви вільні обрати будь-який спосіб розміщення прямо з застосунку Element:
1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org
1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org, який обслуговують розробники Matrix, чи на одному з тисяч публічних серверів, які обслуговують волонтери
2. Розмістити свій обліковий запис на власному сервері
3. Зареєструватись на індивідуальному сервері, просто підписавшись на послуги платформи Element Matrix Services

View file

@ -17,6 +17,8 @@
package org.matrix.android.sdk.api
import android.content.Context
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration
import androidx.work.WorkManager
@ -48,14 +50,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var olmManager: OlmManager
@Inject internal lateinit var sessionManager: SessionManager
private val uiHandler = Handler(Looper.getMainLooper())
init {
Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) {
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
}
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
}
fun getUserAgent() = userAgentHolder.userAgent

View file

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.crypto
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
@ -34,14 +33,8 @@ internal class CryptoStoreHelper {
.modules(RealmCryptoStoreModule())
.build(),
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
credentials = createCredential())
}
fun createCredential() = Credentials(
userId = "userId_" + Random.nextInt(),
homeServer = "http://matrix.org",
accessToken = "access_token",
refreshToken = null,
deviceId = "deviceId_sample"
)
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.crypto.encryption
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.amshove.kluent.shouldBe
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EncryptionTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test
fun test_EncryptionEvent() {
performTest(roomShouldBeEncrypted = false) { room ->
// Send an encryption Event as an Event (and not as a state event)
room.sendEvent(
eventType = EventType.STATE_ROOM_ENCRYPTION,
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
)
}
}
@Test
fun test_EncryptionStateEvent() {
performTest(roomShouldBeEncrypted = true) { room ->
// Send an encryption Event as a State Event
room.sendStateEvent(
eventType = EventType.STATE_ROOM_ENCRYPTION,
stateKey = null,
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent(),
callback = NoOpMatrixCallback()
)
}
}
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
val aliceSession = cryptoTestData.firstSession
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
room.isEncrypted() shouldBe false
val timeline = room.createTimeline(null, TimelineSettings(10))
val latch = CountDownLatch(1)
val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val newMessages = snapshot
.filter { it.root.sendState == SendState.SYNCED }
.filter { it.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION }
if (newMessages.isNotEmpty()) {
timeline.removeListener(this)
latch.countDown()
}
}
}
timeline.start()
timeline.addListener(timelineListener)
action.invoke(room)
mTestHelper.await(latch)
timeline.dispose()
room.isEncrypted() shouldBe roomShouldBeEncrypted
cryptoTestData.cleanUp(mTestHelper)
}
}

View file

@ -17,15 +17,14 @@
package org.matrix.android.sdk.internal.crypto.verification.qrcode
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.matrix.android.sdk.InstrumentedTest
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeNull
import org.amshove.kluent.shouldEqual
import org.amshove.kluent.shouldEqualTo
import org.amshove.kluent.shouldNotBeNull
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@ -66,32 +65,32 @@ class QrCodeTest : InstrumentedTest {
@Test
fun testEncoding1() {
qrCode1.toEncodedString() shouldEqual value1
qrCode1.toEncodedString() shouldBeEqualTo value1
}
@Test
fun testEncoding2() {
qrCode2.toEncodedString() shouldEqual value2
qrCode2.toEncodedString() shouldBeEqualTo value2
}
@Test
fun testEncoding3() {
qrCode3.toEncodedString() shouldEqual value3
qrCode3.toEncodedString() shouldBeEqualTo value3
}
@Test
fun testSymmetry1() {
qrCode1.toEncodedString().toQrCodeData() shouldEqual qrCode1
qrCode1.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode1
}
@Test
fun testSymmetry2() {
qrCode2.toEncodedString().toQrCodeData() shouldEqual qrCode2
qrCode2.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode2
}
@Test
fun testSymmetry3() {
qrCode3.toEncodedString().toQrCodeData() shouldEqual qrCode3
qrCode3.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode3
}
@Test
@ -102,7 +101,7 @@ class QrCodeTest : InstrumentedTest {
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 0
byteArray[7] shouldBeEqualTo 0
checkSizeAndTransaction(byteArray)
@ -120,7 +119,7 @@ class QrCodeTest : InstrumentedTest {
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 1
byteArray[7] shouldBeEqualTo 1
checkSizeAndTransaction(byteArray)
compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
@ -137,7 +136,7 @@ class QrCodeTest : InstrumentedTest {
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 2
byteArray[7] shouldBeEqualTo 2
checkSizeAndTransaction(byteArray)
compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray)
@ -156,10 +155,10 @@ class QrCodeTest : InstrumentedTest {
val result = qrCode.toEncodedString()
val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId")
result shouldEqual expected
result shouldBeEqualTo expected
// Reverse operation
expected.toQrCodeData() shouldEqual qrCode
expected.toQrCodeData() shouldBeEqualTo qrCode
}
@Test
@ -170,7 +169,7 @@ class QrCodeTest : InstrumentedTest {
val qrCode = qrCode1.copy(transactionId = longTransactionId)
// Symmetric operation
qrCode.toEncodedString().toQrCodeData() shouldEqual qrCode
qrCode.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode
}
}
@ -218,32 +217,32 @@ class QrCodeTest : InstrumentedTest {
}
private fun compareArray(actual: ByteArray, expected: ByteArray) {
actual.size shouldEqual expected.size
actual.size shouldBeEqualTo expected.size
for (i in actual.indices) {
actual[i] shouldEqualTo expected[i]
actual[i] shouldBeEqualTo expected[i]
}
}
private fun checkHeader(byteArray: ByteArray) {
// MATRIX
byteArray[0] shouldEqualTo 'M'.toByte()
byteArray[1] shouldEqualTo 'A'.toByte()
byteArray[2] shouldEqualTo 'T'.toByte()
byteArray[3] shouldEqualTo 'R'.toByte()
byteArray[4] shouldEqualTo 'I'.toByte()
byteArray[5] shouldEqualTo 'X'.toByte()
byteArray[0] shouldBeEqualTo 'M'.toByte()
byteArray[1] shouldBeEqualTo 'A'.toByte()
byteArray[2] shouldBeEqualTo 'T'.toByte()
byteArray[3] shouldBeEqualTo 'R'.toByte()
byteArray[4] shouldBeEqualTo 'I'.toByte()
byteArray[5] shouldBeEqualTo 'X'.toByte()
// Version
byteArray[6] shouldEqualTo 2
byteArray[6] shouldBeEqualTo 2
}
private fun checkSizeAndTransaction(byteArray: ByteArray) {
// Size
byteArray[8] shouldEqualTo 0
byteArray[9] shouldEqualTo 13
byteArray[8] shouldBeEqualTo 0
byteArray[9] shouldBeEqualTo 13
// Transaction
byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldEqual "MaTransaction"
byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldBeEqualTo "MaTransaction"
}
}

View file

@ -18,6 +18,14 @@ package org.matrix.android.sdk.session.room.timeline
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.createObject
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.send.SendState
@ -29,14 +37,6 @@ import org.matrix.android.sdk.internal.database.model.SessionRealmModule
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.createObject
import org.amshove.kluent.shouldBeTrue
import org.amshove.kluent.shouldEqual
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class ChunkEntityTest : InstrumentedTest {
@ -60,10 +60,10 @@ internal class ChunkEntityTest : InstrumentedTest {
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
realm.copyToRealmOrUpdate(it)
realm.copyToRealm(it)
}
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.timelineEvents.size shouldEqual 1
chunk.timelineEvents.size shouldBeEqualTo 1
}
}
@ -72,11 +72,11 @@ internal class ChunkEntityTest : InstrumentedTest {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
realm.copyToRealmOrUpdate(it)
realm.copyToRealm(it)
}
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.timelineEvents.size shouldEqual 1
chunk.timelineEvents.size shouldBeEqualTo 1
}
}
@ -88,7 +88,7 @@ internal class ChunkEntityTest : InstrumentedTest {
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.timelineEvents.size shouldEqual 60
chunk1.timelineEvents.size shouldBeEqualTo 60
}
}
@ -104,7 +104,7 @@ internal class ChunkEntityTest : InstrumentedTest {
chunk1.addAll(ROOM_ID, eventsForChunk1, PaginationDirection.FORWARDS)
chunk2.addAll(ROOM_ID, eventsForChunk2, PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.timelineEvents.size shouldEqual 40
chunk1.timelineEvents.size shouldBeEqualTo 40
chunk1.isLastForward.shouldBeTrue()
}
}
@ -119,7 +119,7 @@ internal class ChunkEntityTest : InstrumentedTest {
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.FORWARDS)
chunk1.prevToken shouldEqual prevToken
chunk1.prevToken shouldBeEqualTo prevToken
}
}
@ -133,7 +133,7 @@ internal class ChunkEntityTest : InstrumentedTest {
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.nextToken shouldEqual nextToken
chunk1.nextToken shouldBeEqualTo nextToken
}
}
@ -142,7 +142,7 @@ internal class ChunkEntityTest : InstrumentedTest {
direction: PaginationDirection) {
events.forEach { event ->
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let {
realm.copyToRealmOrUpdate(it)
realm.copyToRealm(it)
}
addTimelineEvent(roomId, fakeEvent, direction, emptyMap())
}

View file

@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import kotlin.random.Random
@ -41,7 +41,7 @@ object RoomDataHelper {
}
}
fun createFakeEvent(type: String,
private fun createFakeEvent(type: String,
content: Content? = null,
prevContent: Content? = null,
sender: String = FAKE_TEST_SENDER,
@ -62,8 +62,8 @@ object RoomDataHelper {
return createFakeEvent(EventType.MESSAGE, message)
}
fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
private fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
}
}

View file

@ -78,7 +78,7 @@ internal class TimelineTest : InstrumentedTest {
// }
// }
// latch.await()
// timelineEvents.size shouldEqual initialLoad + paginationCount
// timelineEvents.size shouldBeEqualTo initialLoad + paginationCount
// timeline.dispose()
// }
}

View file

@ -2,8 +2,10 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.matrix.android.sdk">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- TODO Is WRITE_EXTERNAL_STORAGE necessary? -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:networkSecurityConfig="@xml/network_security_config">

View file

@ -48,18 +48,25 @@ data class MatrixError(
companion object {
/** Forbidden access, e.g. joining a room without permission, failed login. */
const val M_FORBIDDEN = "M_FORBIDDEN"
/** An unknown error has occurred. */
const val M_UNKNOWN = "M_UNKNOWN"
/** The access token specified was not recognised. */
const val M_UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
/** No access token was specified for the request. */
const val M_MISSING_TOKEN = "M_MISSING_TOKEN"
/** Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys. */
const val M_BAD_JSON = "M_BAD_JSON"
/** Request did not contain valid JSON. */
const val M_NOT_JSON = "M_NOT_JSON"
/** No resource was found for this request. */
const val M_NOT_FOUND = "M_NOT_FOUND"
/** Too many requests have been sent in a short period of time. Wait a while then try again. */
const val M_LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
@ -69,68 +76,97 @@ data class MatrixError(
/** Encountered when trying to register a user ID which has been taken. */
const val M_USER_IN_USE = "M_USER_IN_USE"
/** Sent when the room alias given to the createRoom API is already in use. */
const val M_ROOM_IN_USE = "M_ROOM_IN_USE"
/** (Not documented yet) */
const val M_BAD_PAGINATION = "M_BAD_PAGINATION"
/** The request was not correctly authorized. Usually due to login failures. */
const val M_UNAUTHORIZED = "M_UNAUTHORIZED"
/** (Not documented yet) */
const val M_OLD_VERSION = "M_OLD_VERSION"
/** The server did not understand the request. */
const val M_UNRECOGNIZED = "M_UNRECOGNIZED"
/** (Not documented yet) */
const val M_LOGIN_EMAIL_URL_NOT_YET = "M_LOGIN_EMAIL_URL_NOT_YET"
/** Authentication could not be performed on the third party identifier. */
const val M_THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
/** Sent when a threepid given to an API cannot be used because no record matching the threepid was found. */
const val M_THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
/** Sent when a threepid given to an API cannot be used because the same threepid is already in use. */
const val M_THREEPID_IN_USE = "M_THREEPID_IN_USE"
/** The client's request used a third party server, eg. identity server, that this server does not trust. */
const val M_SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
/** The request or entity was too large. */
const val M_TOO_LARGE = "M_TOO_LARGE"
/** (Not documented yet) */
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
/** The request cannot be completed because the homeserver has reached a resource limit imposed on it. For example,
* a homeserver held in a shared hosting environment may reach a resource limit if it starts using too much memory
* or disk space. The error MUST have an admin_contact field to provide the user receiving the error a place to reach
* out to. Typically, this error will appear on routes which attempt to modify state (eg: sending messages, account
* data, etc) and not routes which only read state (eg: /sync, get account data, etc). */
const val M_RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
/** The user ID associated with the request has been deactivated. Typically for endpoints that prove authentication, such as /login. */
const val M_USER_DEACTIVATED = "M_USER_DEACTIVATED"
/** Encountered when trying to register a user ID which is not valid. */
const val M_INVALID_USERNAME = "M_INVALID_USERNAME"
/** Sent when the initial state given to the createRoom API is invalid. */
const val M_INVALID_ROOM_STATE = "M_INVALID_ROOM_STATE"
/** The server does not permit this third party identifier. This may happen if the server only permits,
* for example, email addresses from a particular domain. */
const val M_THREEPID_DENIED = "M_THREEPID_DENIED"
/** The client's request to create a room used a room version that the server does not support. */
const val M_UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION"
/** The client attempted to join a room that has a version the server does not support.
* Inspect the room_version property of the error response for the room's version. */
const val M_INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION"
/** The state change requested cannot be performed, such as attempting to unban a user who is not banned. */
const val M_BAD_STATE = "M_BAD_STATE"
/** The room or resource does not permit guests to access it. */
const val M_GUEST_ACCESS_FORBIDDEN = "M_GUEST_ACCESS_FORBIDDEN"
/** A Captcha is required to complete the request. */
const val M_CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
/** The Captcha provided did not match what was expected. */
const val M_CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
/** A required parameter was missing from the request. */
const val M_MISSING_PARAM = "M_MISSING_PARAM"
/** A parameter that was specified has the wrong value. For example, the server expected an integer and instead received a string. */
const val M_INVALID_PARAM = "M_INVALID_PARAM"
/** The resource being requested is reserved by an application service, or the application service making the request has not created the resource. */
const val M_EXCLUSIVE = "M_EXCLUSIVE"
/** The user is unable to reject an invite to join the server notices room. See the Server Notices module for more information. */
const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
/** (Not documented yet) */
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
/** (Not documented yet) */
const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"

View file

@ -33,6 +33,7 @@ interface MxCallDetail {
interface MxCall : MxCallDetail {
var state: CallState
/**
* Pick Up the incoming call
* It has no effect on outgoing call

View file

@ -53,23 +53,31 @@ package org.matrix.android.sdk.api.session.crypto.keysbackup
enum class KeysBackupState {
// Need to check the current backup version on the homeserver
Unknown,
// Checking if backup is enabled on home server
CheckingBackUpOnHomeserver,
// Backup has been stopped because a new backup version has been detected on the homeserver
WrongBackUpVersion,
// Backup from this device is not enabled
Disabled,
// There is a backup available on the homeserver but it is not trusted.
// It is not trusted because the signature is invalid or the device that created it is not verified
// Use [KeysBackup.getKeysBackupTrust()] to get trust details.
// Consequently, the backup from this device is not enabled.
NotTrusted,
// Backup is being enabled: the backup version is being created on the homeserver
Enabling,
// Backup is enabled and ready to send backup to the homeserver
ReadyToBackUp,
// e2e keys are going to be sent to the homeserver
WillBackUp,
// e2e keys are being sent to the homeserver
BackingUp
}

View file

@ -22,8 +22,10 @@ package org.matrix.android.sdk.api.session.crypto.verification
enum class VerificationMethod {
// Use it when your application supports the SAS verification method
SAS,
// Use it if your application is able to display QR codes
QR_CODE_SHOW,
// Use it if your application is able to scan QR codes
QR_CODE_SCAN
}

View file

@ -253,6 +253,7 @@ fun Event.isFileMessage(): Boolean {
else -> false
}
}
fun Event.isAttachmentMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {

View file

@ -21,10 +21,13 @@ package org.matrix.android.sdk.api.session.events.model
object RelationType {
/** Lets you define an event which annotates an existing event.*/
const val ANNOTATION = "m.annotation"
/** Lets you define an event which replaces an existing event.*/
const val REPLACE = "m.replace"
/** Lets you define an event which references an existing event.*/
const val REFERENCE = "m.reference"
/** Lets you define an event which adds a response to an existing event.*/
const val RESPONSE = "org.matrix.response"
}

View file

@ -33,10 +33,12 @@ data class IntegrationManagerConfig(
* Defined in UserAccountData
*/
ACCOUNT,
/**
* Defined in Wellknown
*/
HOMESERVER,
/**
* Fallback value, hardcoded by the SDK
*/

View file

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.session.room.members
sealed class ChangeMembershipState() {
sealed class ChangeMembershipState {
object Unknown : ChangeMembershipState()
object Joining : ChangeMembershipState()
data class FailedJoining(val throwable: Throwable) : ChangeMembershipState()

View file

@ -33,6 +33,7 @@ data class RoomGuestAccessContent(
enum class GuestAccess(val value: String) {
@Json(name = "can_join")
CanJoin("can_join"),
@Json(name = "forbidden")
Forbidden("forbidden")
}

View file

@ -29,16 +29,19 @@ enum class RoomHistoryVisibility {
* participating homeserver with anyone, regardless of whether they have ever joined the room.
*/
@Json(name = "world_readable") WORLD_READABLE,
/**
* Previous events are always accessible to newly joined members. All events in the
* room are accessible, even those sent when the member was not a part of the room.
*/
@Json(name = "shared") SHARED,
/**
* Events are accessible to newly joined members from the point they were invited onwards.
* Events stop being accessible when the member's state changes to something other than invite or join.
*/
@Json(name = "invited") INVITED,
/**
* Events are accessible to newly joined members from the point they joined the room onwards.
* Events stop being accessible when the member's state changes to something other than join.

View file

@ -29,6 +29,7 @@ object MessageType {
const val MSGTYPE_RESPONSE = "org.matrix.response"
const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed"
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
// Because sticker isn't a message type but a event type without msgtype field
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"

View file

@ -18,18 +18,25 @@ package org.matrix.android.sdk.api.session.room.send
enum class SendState {
UNKNOWN,
// the event has not been sent
UNSENT,
// the event is encrypting
ENCRYPTING,
// the event is currently sending
SENDING,
// the event has been sent
SENT,
// the event has been received from server
SYNCED,
// The event failed to be sent
UNDELIVERED,
// the event failed to be sent because some unknown devices have been found while encrypting it
FAILED_UNKNOWN_DEVICES;

View file

@ -128,6 +128,7 @@ interface Timeline {
* It represents future events.
*/
FORWARDS,
/**
* It represents past events.
*/

View file

@ -20,6 +20,7 @@ interface FilterService {
enum class FilterPreset {
NoFilter,
/**
* Filter for Riot, will include only known event type
*/

View file

@ -35,6 +35,7 @@ const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-
* Secured Shared Storage algorithm constant
*/
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"

View file

@ -194,18 +194,18 @@ internal class DefaultCryptoService @Inject constructor(
private val lastNewSessionForcedDates = MXUsersDevicesMap<Long>()
fun onStateEvent(roomId: String, event: Event) {
when {
event.getClearType() == EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
event.getClearType() == EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
event.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
}
}
fun onLiveEvent(roomId: String, event: Event) {
when {
event.getClearType() == EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
event.getClearType() == EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
event.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
}
}
@ -615,6 +615,7 @@ internal class DefaultCryptoService @Inject constructor(
val encryptionEvent = monarchy.fetchCopied { realm ->
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
.isNotNull(EventEntityFields.STATE_KEY)
.findFirst()
}
return encryptionEvent != null
@ -915,6 +916,11 @@ internal class DefaultCryptoService @Inject constructor(
* @param event the encryption event.
*/
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
if (!event.isStateEvent()) {
// Ignore
Timber.w("Invalid encryption event")
return
}
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val params = LoadRoomMembersTask.Params(roomId)
try {

View file

@ -359,7 +359,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
cryptoStore.storeUserDevices(userId, workingCopy)
}
// Handle cross signing keys update
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
}

View file

@ -28,6 +28,7 @@ enum class GossipingRequestState {
ACCEPTING,
ACCEPTED,
FAILED_TO_ACCEPTED,
// USER_REJECTED,
UNABLE_TO_PROCESS,
CANCELLED_BY_REQUESTER,

View file

@ -36,6 +36,7 @@ import kotlin.math.min
object MXMegolmExportEncryption {
private const val HEADER_LINE = "-----BEGIN MEGOLM SESSION DATA-----"
private const val TRAILER_LINE = "-----END MEGOLM SESSION DATA-----"
// we split into lines before base64ing, because encodeBase64 doesn't deal
// terribly well with large arrays.
private const val LINE_LENGTH = 72 * 4 / 3

View file

@ -70,7 +70,9 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
delay(1500)
cryptoStore.getOrAddOutgoingSecretShareRequest(secretName, recipients)?.let {
// TODO check if there is already one that is being sent?
if (it.state == OutgoingGossipingRequestState.SENDING /**|| it.state == OutgoingGossipingRequestState.SENT*/) {
if (it.state == OutgoingGossipingRequestState.SENDING
/**|| it.state == OutgoingGossipingRequestState.SENT*/
) {
Timber.v("## CRYPTO - GOSSIP sendSecretShareRequest() : we are already sending for that session: $it")
return@launch
}

View file

@ -80,6 +80,7 @@ class OlmInboundGroupSessionWrapper2 : Serializable {
constructor() {
// empty
}
/**
* Create a new instance from the provided keys map.
*

View file

@ -66,19 +66,23 @@ enum class WithHeldCode(val value: String) {
* the user/device was blacklisted
*/
BLACKLISTED("m.blacklisted"),
/**
* the user/devices is unverified
*/
UNVERIFIED("m.unverified"),
/**
* the user/device is not allowed have the key. For example, this would usually be sent in response
* to a key request if the user was not in the room when the message was sent
*/
UNAUTHORISED("m.unauthorised"),
/**
* Sent in reply to a key request if the device that the key is requested from does not have the requested key
*/
UNAVAILABLE("m.unavailable"),
/**
* An olm session could not be established.
* This may happen, for example, if the sender was unable to obtain a one-time key from the recipient.

View file

@ -23,7 +23,6 @@ import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.LocalEcho
@ -86,7 +85,9 @@ import org.matrix.android.sdk.internal.crypto.store.db.query.getById
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.di.CryptoDatabase
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmException
@ -98,7 +99,9 @@ import kotlin.collections.set
internal class RealmCryptoStore @Inject constructor(
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
private val crossSigningKeysMapper: CrossSigningKeysMapper,
private val credentials: Credentials) : IMXCryptoStore {
@UserId private val userId: String,
@DeviceId private val deviceId: String?
) : IMXCryptoStore {
/* ==========================================================================================
* Memory cache, to correctly release JNI objects
@ -141,8 +144,8 @@ internal class RealmCryptoStore @Inject constructor(
// Check credentials
// The device id may not have been provided in credentials.
// Check it only if provided, else trust the stored one.
if (currentMetadata.userId != credentials.userId
|| (credentials.deviceId != null && credentials.deviceId != currentMetadata.deviceId)) {
if (currentMetadata.userId != userId
|| (deviceId != null && deviceId != currentMetadata.deviceId)) {
Timber.w("## open() : Credentials do not match, close this store and delete data")
deleteAll = true
currentMetadata = null
@ -155,8 +158,8 @@ internal class RealmCryptoStore @Inject constructor(
}
// Metadata not found, or database cleaned, create it
realm.createObject(CryptoMetadataEntity::class.java, credentials.userId).apply {
deviceId = credentials.deviceId
realm.createObject(CryptoMetadataEntity::class.java, userId).apply {
deviceId = this@RealmCryptoStore.deviceId
}
}
}
@ -302,22 +305,42 @@ internal class RealmCryptoStore @Inject constructor(
userEntity.crossSigningInfoEntity?.deleteFromRealm()
userEntity.crossSigningInfoEntity = null
} else {
var shouldResetMyDevicesLocalTrust = false
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
// What should we do if we detect a change of the keys?
val existingMaster = signingInfo.getMasterKey()
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingMaster, masterKey)
} else {
Timber.d("## CrossSigning MSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(masterKey)
signingInfo.setMasterKey(keyEntity)
if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true
// my msk has changed! clear my private key
// Could we have some race here? e.g I am the one that did change the keys
// could i get this update to early and clear the private keys?
// -> initializeCrossSigning is guarding for that by storing all at once
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignMasterPrivateKey = null
}
}
}
val existingSelfSigned = signingInfo.getSelfSignedKey()
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
} else {
Timber.d("## CrossSigning SSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
signingInfo.setSelfSignedKey(keyEntity)
if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true
// my ssk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignSelfSignedPrivateKey = null
}
}
}
// Only for me
@ -326,8 +349,27 @@ internal class RealmCryptoStore @Inject constructor(
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingUSK, userSigningKey)
} else {
Timber.d("## CrossSigning USK change for $userId")
val keyEntity = crossSigningKeysMapper.map(userSigningKey)
signingInfo.setUserSignedKey(keyEntity)
if (userId == this.userId) {
shouldResetMyDevicesLocalTrust = true
// my usk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignUserPrivateKey = null
}
}
}
}
// When my cross signing keys are reset, we consider clearing all existing device trust
if (shouldResetMyDevicesLocalTrust) {
realm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, this.userId)
.findFirst()
?.devices?.forEach {
it?.trustLevelEntity?.crossSignedVerified = false
it?.trustLevelEntity?.locallyVerified = it.deviceId == deviceId
}
}
userEntity.crossSigningInfoEntity = signingInfo
@ -1316,7 +1358,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll()
xInfoEntities?.forEach { info ->
// Need to ignore mine
if (info.userId != credentials.userId) {
if (info.userId != userId) {
info.crossSigningKeys.forEach {
it.trustLevelEntity = null
}
@ -1331,7 +1373,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll()
xInfoEntities?.forEach { xInfoEntity ->
// Need to ignore mine
if (xInfoEntity.userId == credentials.userId) return@forEach
if (xInfoEntity.userId == userId) return@forEach
val mapped = mapCrossSigningInfoEntity(xInfoEntity)
val currentTrust = mapped.isTrusted()
val newTrust = check(mapped.userId)

View file

@ -68,10 +68,13 @@ internal abstract class SASDefaultVerificationTransaction(
// Deprecated maybe removed later, use V2
const val KEY_AGREEMENT_V1 = "curve25519"
const val KEY_AGREEMENT_V2 = "curve25519-hkdf-sha256"
// ordered by preferred order
val KNOWN_AGREEMENT_PROTOCOLS = listOf(KEY_AGREEMENT_V2, KEY_AGREEMENT_V1)
// ordered by preferred order
val KNOWN_HASHES = listOf("sha256")
// ordered by preferred order
val KNOWN_MACS = listOf(SAS_MAC_SHA256, SAS_MAC_SHA256_LONGKDF)
@ -101,6 +104,7 @@ internal abstract class SASDefaultVerificationTransaction(
// Visible for test
var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null
// Visible for test
var accepted: ValidVerificationInfoAccept? = null
protected var otherKey: String? = null

View file

@ -47,6 +47,7 @@ internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId
}
return obj
}
internal fun EventAnnotationsSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
return EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId).apply { this.roomId = roomId }

View file

@ -94,6 +94,7 @@ internal class RoomSummaryUpdater @Inject constructor(
// Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
.isNotNull(EventEntityFields.STATE_KEY)
.findFirst()
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)

View file

@ -58,6 +58,7 @@ internal class TimelineEventDecryptor @Inject constructor(
// Set of eventIds which are currently decrypting
private val existingRequests = mutableSetOf<DecryptionRequest>()
// sessionId -> list of eventIds
private val unknownSessionsFailure = mutableMapOf<String, MutableSet<DecryptionRequest>>()

View file

@ -48,6 +48,7 @@ internal class DefaultTypingService @AssistedInject constructor(
// What the homeserver knows
private var userIsTyping = false
// Last time the user is typing event has been sent
private var lastRequestTimestamp: Long = 0

View file

@ -67,6 +67,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
Timber.d("BRIDGE onWidgetEvent : $jsonEventData")
try {
val dataAsDict = jsonAdapter.fromJson(jsonEventData)
@Suppress("UNCHECKED_CAST")
val eventData = (dataAsDict?.get("event.data") as? JsonDict) ?: return
onWidgetMessage(eventData)

View file

@ -4,47 +4,47 @@
<string name="summary_user_sent_image">Uživatel %1$s poslal obrázek.</string>
<string name="summary_user_sent_sticker">Uživatel %1$s poslal nálepku.</string>
<string name="notice_room_invite_no_invitee">Pozvánka od uživatele %s</string>
<string name="notice_room_invite_no_invitee">Pozvání od uživatele %s</string>
<string name="notice_room_invite">Uživatel %1$s pozval uživatele %2$s</string>
<string name="notice_room_invite_you">Uživatel %1$s vás pozval</string>
<string name="notice_room_join">Uživatel %1$s se připojil</string>
<string name="notice_room_join">%1$s vstoupil do místnosti</string>
<string name="notice_room_leave">Uživatel %1$s odešel</string>
<string name="notice_room_reject">Uživatel %1$s odmítl pozvání</string>
<string name="notice_room_kick">Uživatel %1$s vykopl uživatele %2$s</string>
<string name="notice_room_unban">Uživatel %1$s znovu povolil vstup uživateli %2$s</string>
<string name="notice_room_ban">Uživatel %1$s vykázal uživatele %2$s</string>
<string name="notice_room_withdraw">Uživatel %1$s zrušil pozvání pro uživatele %2$s</string>
<string name="notice_avatar_url_changed">Uživatel %1$s změnil svůj profilový obrázek</string>
<string name="notice_display_name_set">Uživatel %1$s nastavil své zobrazované jméno na %2$s</string>
<string name="notice_display_name_changed_from">Uživatel %1$s změnil své zobrazované jméno z %2$s na %3$s</string>
<string name="notice_display_name_removed">Uživatel %1$s odstranil své zobrazované jméno (%2$s)</string>
<string name="notice_room_topic_changed">Uživatel %1$s změnil téma na: %2$s</string>
<string name="notice_room_name_changed">Uživatel %1$s změnil název místnosti na: %2$s</string>
<string name="notice_placed_video_call">Uživatel %s uskutečnil videohovor.</string>
<string name="notice_placed_voice_call">Uživatel %s uskutečnil hlasový hovor.</string>
<string name="notice_answered_call">Uživatel %s přijal hovor.</string>
<string name="notice_ended_call">Uživatel %s ukončil hovor.</string>
<string name="notice_made_future_room_visibility">Uživatel %1$s nastavit viditelnost budoucích zpráv v místnosti pro %2$s</string>
<string name="notice_room_reject">%1$s odmítli pozvání</string>
<string name="notice_room_kick">%1$s vykopli %2$s</string>
<string name="notice_room_unban">%1$s zrušil vykázání %2$s</string>
<string name="notice_room_ban">%1$s vykázali %2$s</string>
<string name="notice_room_withdraw">%1$s zrušili pozvání pro %2$s</string>
<string name="notice_avatar_url_changed">%1$s změnili svůj profilový obrázek</string>
<string name="notice_display_name_set">%1$s nastavili své veřejné jméno na %2$s</string>
<string name="notice_display_name_changed_from">%1$s změnili své veřejné jméno z %2$s na %3$s</string>
<string name="notice_display_name_removed">%1$s odstranili své veřejné jméno (%2$s)</string>
<string name="notice_room_topic_changed">%1$s změnili téma na: %2$s</string>
<string name="notice_room_name_changed">%1$s změnili název místnosti na: %2$s</string>
<string name="notice_placed_video_call">%s uskutečnili videohovor.</string>
<string name="notice_placed_voice_call">%s uskutečnili hlasový hovor.</string>
<string name="notice_answered_call">%s přijali hovor.</string>
<string name="notice_ended_call">%s ukončili hovor.</string>
<string name="notice_made_future_room_visibility">%1$s nastavili viditelnost budoucí historie místnosti pro %2$s</string>
<string name="notice_room_visibility_invited">všechny členy místnosti od chvíle, kdy budou pozváni.</string>
<string name="notice_room_visibility_joined">všechny členy místnosti od chvíle, kdy se připojí.</string>
<string name="notice_room_visibility_shared">všechny členy místnosti.</string>
<string name="notice_room_visibility_world_readable">kohokoliv.</string>
<string name="notice_room_visibility_unknown">neznámým (%s).</string>
<string name="notice_end_to_end">Uživatel %1$s zapnul end-to-end šifrování (%2$s)</string>
<string name="notice_end_to_end">%1$s zapnuli end-to-end šifrování (%2$s)</string>
<string name="notice_requested_voip_conference">Uživatel %1$s požádal o VoIP konferenci</string>
<string name="notice_requested_voip_conference">%1$s požádali o VoIP konferenci</string>
<string name="notice_voip_started">Začala VoIP konference</string>
<string name="notice_voip_finished">VoIP konference skončila</string>
<string name="notice_avatar_changed_too">(profilový obrázek byl také změněn)</string>
<string name="notice_room_name_removed">Uživatel %1$s odstranil název místnosti</string>
<string name="notice_room_topic_removed">Uživatel %1$s odstranil téma místnosti</string>
<string name="notice_profile_change_redacted">Uživatel %1$s aktualizoval svůj profil %2$s</string>
<string name="notice_room_third_party_invite">Uživatel %1$s do této místnosti pozval uživatele %2$s</string>
<string name="notice_room_third_party_registered_invite">Uživatel %1$s přijal pozvání pro %2$s</string>
<string name="notice_room_name_removed">%1$s odstranili název místnosti</string>
<string name="notice_room_topic_removed">%1$s odstranili téma místnosti</string>
<string name="notice_profile_change_redacted">%1$s aktualizovali svůj profil %2$s</string>
<string name="notice_room_third_party_invite">%1$s do této místnosti pozvali %2$s</string>
<string name="notice_room_third_party_registered_invite">%1$s přijali pozvání pro %2$s</string>
<string name="notice_crypto_unable_to_decrypt">** Nelze dešifrovat: %s **</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">Odesílatelovo zařízení neposlalo klíče pro tuto zprávu.</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">Odesílatelovo zařízení nám neposlalo klíče pro tuto zprávu.</string>
<string name="could_not_redact">Nelze vymazat</string>
<string name="unable_to_send_message">Zprávu nelze odeslat</string>
@ -54,7 +54,7 @@
<string name="network_error">Chyba sítě</string>
<string name="matrix_error">Chyba v Matrixu</string>
<string name="room_error_join_failed_empty_room">V současnosti není možné se znovu připojit do prázdné místnosti.</string>
<string name="room_error_join_failed_empty_room">V současnosti není možné znovu vstoupit do prázdné místnosti.</string>
<string name="encrypted_message">Šifrovaná zpráva</string>
@ -74,35 +74,161 @@
<string name="room_displayname_empty_room">Prázdná místnost</string>
<string name="notice_room_update">Uživatel %s upgradoval tuto místnost.</string>
<string name="notice_room_update">%s povýšili tuto místnost.</string>
<string name="notice_event_redacted_with_reason">Zpráva byla smazána [důvod: %1$s]</string>
<string name="notice_event_redacted_by_with_reason">Zpráva smazána uživatelem %1$s [důvod: %2$s]</string>
<string name="notice_room_third_party_revoked_invite">Uživatel %1$s obnovil pozvánku do místnosti pro uživatele %2$s</string>
<string name="notice_room_third_party_revoked_invite">%1$s zrušili pozvánku do místnosti pro %2$s</string>
<string name="initial_sync_start_importing_account">Úvodní synchronizace:
\nImport účtu</string>
\nImportuji účet…</string>
<string name="initial_sync_start_importing_account_crypto">Úvodní synchronizace:
\nImport klíčů</string>
\nImportuji klíče</string>
<string name="initial_sync_start_importing_account_rooms">Úvodní synchronizace:
\nImport místností</string>
\nImportuji místnosti</string>
<string name="initial_sync_start_importing_account_joined_rooms">Úvodní synchronizace:
\nImport místností, kterými jste členy</string>
\nImportuji místností, jichž jste členy</string>
<string name="initial_sync_start_importing_account_left_rooms">Úvodní synchronizace:
\nImport opuštěných místností</string>
\nImportuji místnost, jež jste opustili</string>
<string name="initial_sync_start_importing_account_groups">Úvodní synchronizace:
\nImport skupin</string>
\nImportuji komunity</string>
<string name="initial_sync_start_importing_account_data">Úvodní synchronizace:
\nImport dat účtu</string>
\nImportuji data účtu</string>
<string name="event_status_sending_message">Odesílání zprávy</string>
<string name="event_status_sending_message">Odesílám zprávu</string>
<string name="initial_sync_start_importing_account_invited_rooms">Úvodní synchronizace:
\nImport pozvánek</string>
\nImportuji pozvání</string>
<string name="clear_timeline_send_queue">Vymazat frontu neodeslaných zpráv</string>
<string name="notice_room_invite_with_reason">Uživatel %1$s pozval uživatele %2$s. Důvod: %3$s</string>
<string name="notice_room_invite_you_with_reason">Uživatel %1$s váš pozval. Důvod: %2$s</string>
<string name="notice_room_leave_with_reason">Uživatel %1$s odešel. Důvod: %2$s</string>
<string name="notice_room_invite_with_reason">%1$s pozvali %2$s. Důvod: %3$s</string>
<string name="notice_room_invite_you_with_reason">%1$s vás pozvali. Důvod: %2$s</string>
<string name="notice_room_leave_with_reason">%1$s opustil místnost. Důvod: %2$s</string>
<string name="notice_event_redacted">Zpráva odstraněna</string>
<string name="notice_event_redacted_by">Zprávu odstranil/a %1$s</string>
<string name="summary_you_sent_image">Poslali jste obrázek.</string>
<string name="summary_you_sent_sticker">Poslali jste nálepku.</string>
<string name="notice_room_invite_no_invitee_by_you">Vaše pozvání</string>
<string name="notice_room_created">%1$s založil místnost</string>
<string name="notice_room_created_by_you">Vy jste založili místnost</string>
<string name="notice_room_invite_by_you">Pozvali jste %1$s</string>
<string name="notice_room_join_by_you">Vstoupili jste do místnosti</string>
<string name="notice_room_leave_by_you">Opustili jste místnost</string>
<string name="notice_room_reject_by_you">Odmítli jste pozvání</string>
<string name="notice_room_kick_by_you">Vykopli jste %1$s</string>
<string name="notice_room_unban_by_you">Zrušili jste vykázání pro %1$s</string>
<string name="notice_room_ban_by_you">Vykázali jste %1$s</string>
<string name="notice_room_withdraw_by_you">Stáhli jste pozvánku od %1$s zpět</string>
<string name="notice_avatar_url_changed_by_you">Změnili jste svůj profilový obrázek</string>
<string name="notice_display_name_set_by_you">Změnili jste své veřejné jméno na %1$s</string>
<string name="notice_display_name_changed_from_by_you">Změnili jste své veřejné jméno z %1$s na %2$s</string>
<string name="notice_display_name_removed_by_you">Odstranili jste své veřejné jméno (%1$s)</string>
<string name="notice_room_topic_changed_by_you">Změnili jste téma na: %1$s</string>
<string name="notice_room_avatar_changed">%1$s změnili obrázek místnosti</string>
<string name="notice_room_avatar_changed_by_you">Změnili jste obrázek místnosti</string>
<string name="notice_room_name_changed_by_you">Změnili jste jméno místnosti na: %1$s</string>
<string name="notice_placed_video_call_by_you">Zahájili jste video hovor.</string>
<string name="notice_placed_voice_call_by_you">Zahájili jste hlasový hovor.</string>
<string name="notice_call_candidates">%s poslali data, aby mohli zahájit hovor.</string>
<string name="notice_call_candidates_by_you">Poslali jste data, abyste mohli zahájit hovor.</string>
<string name="notice_answered_call_by_you">Přijali jste hovor.</string>
<string name="notice_ended_call_by_you">Ukončili jste hovor.</string>
<string name="notice_made_future_room_visibility_by_you">Učinili jste budoucí historii místnosti viditelnou pro %1$s</string>
<string name="notice_end_to_end_by_you">Zapnuli jste end-to-end šifrování (%1$s)</string>
<string name="notice_room_update_by_you">Povýšili jste tuto místnost.</string>
<string name="notice_requested_voip_conference_by_you">Požádali jste o VoIP konferenci</string>
<string name="notice_room_name_removed_by_you">Odstranili jste jméno místnosti</string>
<string name="notice_room_topic_removed_by_you">Odstranili jste téma místnosti</string>
<string name="notice_room_avatar_removed">%1$s odstranili obrázek místnosti</string>
<string name="notice_room_avatar_removed_by_you">Odstranili jste obrázek místnosti</string>
<string name="notice_profile_change_redacted_by_you">Aktualizovali jste svů profil %1$s</string>
<string name="notice_room_third_party_invite_by_you">Poslali jste %1$s pozvání ke vstupu do místnosti</string>
<string name="notice_room_third_party_revoked_invite_by_you">Zrušili jste pozvánku ke vstupu do místnosti pro %1$s</string>
<string name="notice_room_third_party_registered_invite_by_you">Přijali jste pozvání pro %1$s</string>
<string name="notice_widget_added">%1$s přidali widget %2$s</string>
<string name="notice_widget_added_by_you">Přidali jste widget %1$s</string>
<string name="notice_widget_removed">%1$s odstranili widget %2$s</string>
<string name="notice_widget_removed_by_you">Odstranili jste widget %1$s</string>
<string name="notice_widget_modified">%1$s změnil widget %2$s</string>
<string name="notice_widget_modified_by_you">Změnili jste widget %1$s</string>
<string name="power_level_admin">Správce</string>
<string name="power_level_moderator">Moderátor</string>
<string name="power_level_default">Výchozí</string>
<string name="power_level_custom">Vlastní (%1$d)</string>
<string name="power_level_custom_no_value">Vlastní</string>
<string name="notice_power_level_changed_by_you">Změnili jste %1$s stupeň oprávnění.</string>
<string name="notice_power_level_changed">%1$s změnili %2$s stupeň oprávnění.</string>
<string name="notice_power_level_diff">%1$s z %2$s na %3$s</string>
<string name="notice_room_invite_no_invitee_with_reason">Pozvání od %1$s. Důvod: %2$s</string>
<string name="notice_room_invite_no_invitee_with_reason_by_you">Vaše pozvání. Důvod: %1$s</string>
<string name="notice_room_invite_with_reason_by_you">Pozvali jste %1$s. Důvod: %2$s</string>
<string name="notice_room_join_with_reason">%1$s vstoupili do místnosti. Důvod: %2$s</string>
<string name="notice_room_join_with_reason_by_you">Vstoupili jste do místnosti. Důvod: %1$s</string>
<string name="notice_room_leave_with_reason_by_you">Opustili jste místnost. Důvod: %1$s</string>
<string name="notice_room_reject_with_reason">%1$s pozvání odmítli. Důvod: %2$s</string>
<string name="notice_room_reject_with_reason_by_you">Odmítli jste pozvání. Důvod: %1$s</string>
<string name="notice_room_kick_with_reason">%1$s vykopnuli %2$s. Důvod: %3$s</string>
<string name="notice_room_kick_with_reason_by_you">Vykopnuli jste %1$s. Důvod: %2$s</string>
<string name="notice_room_unban_with_reason">%1$s zrušili %2$s vykázání. Důvod: %3$s</string>
<string name="notice_room_unban_with_reason_by_you">Zrušili jste %1$s vykázání. Důvod: %2$s</string>
<string name="notice_room_ban_with_reason">%1$s vykázali %2$s. Důvod: %3$s</string>
<string name="notice_room_ban_with_reason_by_you">Vykázali jste %1$s. Důvod: %2$s</string>
<string name="notice_room_third_party_invite_with_reason">%1$s poslali %2$s pozvání, aby vstoupili do místnosti. Důvod: %3$s</string>
<string name="notice_room_third_party_invite_with_reason_by_you">"Poslali jste %1$s pozvání, aby vstoupili do místnosti. Důvod: %2$s"</string>
<string name="notice_room_third_party_revoked_invite_with_reason">%1$s zrušili pozvání do místnosti pro %2$s. Důvod: %3$s</string>
<string name="notice_room_third_party_revoked_invite_with_reason_by_you">Zrušili jste pozvání do místnosti pro %1$s. Důvod: %2$s</string>
<string name="notice_room_third_party_registered_invite_with_reason">%1$s přijali pozvání pro %2$s. Důvod: %3$s</string>
<string name="notice_room_third_party_registered_invite_with_reason_by_you">Přijali jste pozvání pro %1$s. Důvod: %2$s</string>
<string name="notice_room_withdraw_with_reason">%1$s zrušili pozvání pro %2$s. Důvod: %3$s</string>
<string name="notice_room_withdraw_with_reason_by_you">Zrušili jste pozvání od %1$s. Důvod: %2$s</string>
<plurals name="notice_room_aliases_added">
<item quantity="one">%1$s přidali %2$s jako adresu pro tuto místnost.</item>
<item quantity="few">%1$s přidali %2$s jako adresy pro tuto místnost.</item>
<item quantity="other">%1$s přidali %2$s jako adresy pro tuto místnost.</item>
</plurals>
<plurals name="notice_room_aliases_added_by_you">
<item quantity="one">Přidali jste %1$s jako adresu pro tuto místnost.</item>
<item quantity="few">Přidali jste %1$s jako adresy pro tuto místnost.</item>
<item quantity="other">Přidali jste %1$s jako adresy pro tuto místnost.</item>
</plurals>
<plurals name="notice_room_aliases_removed">
<item quantity="one">%1$s odstranili %2$s jako adresu pro tuto místnost.</item>
<item quantity="few">%1$s odstranili %2$s jako adresy pro tuto místnost.</item>
<item quantity="other">%1$s odstranili %2$s jako adresy pro tuto místnost.</item>
</plurals>
<plurals name="notice_room_aliases_removed_by_you">
<item quantity="one">Odstranili jste %2$s jako adresu pro tuto místnost.</item>
<item quantity="few">Odstranili jste %2$s jako adresuy pro tuto místnost.</item>
<item quantity="other">Odstranili jste %2$s jako adresy pro tuto místnost.</item>
</plurals>
<string name="notice_room_aliases_added_and_removed">%1$s přidali %2$ a odstranili %3$s jako adresy pro tuto místnost.</string>
<string name="notice_room_aliases_added_and_removed_by_you">Přidali jste %1$s a odstranili %2$s jako adresy pro tuto místnost.</string>
<string name="notice_room_canonical_alias_set">%1$s nastavili hlavní adresu této místnosti na %2$s.</string>
<string name="notice_room_canonical_alias_set_by_you">Nastavili jste %1$s na hlavní adresu této místnosti.</string>
<string name="notice_room_canonical_alias_unset">%1$s odstranili hlavní adresu této místnosti.</string>
<string name="notice_room_canonical_alias_unset_by_you">Odstranili jste hlavní adresu této místnosti.</string>
<string name="notice_room_guest_access_can_join">"%1$s povolili hostům vstoupit do místnosti."</string>
<string name="notice_room_guest_access_can_join_by_you">Povolili jste hostům vstoupit do místnosti.</string>
<string name="notice_room_guest_access_forbidden">%1$s zamezili hostům vstoupit do místnosti.</string>
<string name="notice_room_guest_access_forbidden_by_you">Zamezili jste hostům vstoupit do místnosti.</string>
<string name="notice_end_to_end_ok">%1$s zapnuli end-to-end šifrování.</string>
<string name="notice_end_to_end_ok_by_you">Zapnuli jste end-to-end šifrování.</string>
<string name="notice_end_to_end_unknown_algorithm">%1$s zapnuli end-to-end šifrování (neznámý algoritmus %2$s).</string>
<string name="notice_end_to_end_unknown_algorithm_by_you">Zapnuli jste end-to-end šifrování (neznámý algoritmus %2$s).</string>
<string name="key_verification_request_fallback_message">%s žádá ověření Vašeho klíče, ale Váš klient nepodporuje ověření klíče v chatu. Budete muset k ověření klíčů použít zastaralý způsob ověření.</string>
</resources>

View file

@ -40,10 +40,10 @@
<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_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_room_third_party_invite">%1$s دعوتی برای پیوستن %2$s به اتاق فرستاد</string>
<string name="notice_room_third_party_revoked_invite">%1$s دعوت پیوستن به اتاق %2$s را باطل کرد</string>
<string name="notice_room_third_party_registered_invite">%1$s دعوت برای %2$s را پذیرفت</string>
@ -56,7 +56,7 @@
<string name="message_failed_to_upload">شکست در بارگذاری تصویر</string>
<string name="network_error">خطای شبکه</string>
<string name="matrix_error">خطای ماتریس</string>
<string name="matrix_error">خطای ماتریکس</string>
<string name="room_error_join_failed_empty_room">در حال حاضر امکان بازپیوست به اتاقی خالی وجود ندارد‌‌.</string>
@ -135,6 +135,98 @@
<string name="key_verification_request_fallback_message">%s درخواست تأیید کلیدتان را دارد، ولی کارخواهتان تأیید کلید درون گپ را پشتیبانی نمی‌کند. برای تأیید کلیدها لازم است از تأییدیهٔ کلید قدیمی استفاده کنید.</string>
<string name="notice_room_created">%1$s اتاق را ایجاد کرد</string>
<string name="notice_profile_change_redacted">%1$s نمایه خود را به‌روز کرد %2$s</string>
<string name="notice_profile_change_redacted">%1$s نمایه‌اش را به‌روز کرد %2$s</string>
<string name="could_not_redact">نمی‌توان ویرایش کرد</string>
<string name="summary_you_sent_image">تصویری فرستادید.</string>
<string name="summary_you_sent_sticker">برچسبی فرستادید.</string>
<string name="notice_room_invite_no_invitee_by_you">دعوتتان</string>
<string name="notice_room_created_by_you">اتاق را ایجاد کردید</string>
<string name="notice_room_invite_by_you">از %1$s دعوت کردید</string>
<string name="notice_room_join_by_you">به اتاق پیوستید</string>
<string name="notice_room_leave_by_you">اتاق را ترک کردید</string>
<string name="notice_room_reject_by_you">دعوت را رد کردید</string>
<string name="notice_room_kick_by_you">%1$s را اخراج کردید</string>
<string name="notice_room_unban_by_you">تحریم %1$s را برداشتید</string>
<string name="notice_room_ban_by_you">%1$s را تحریم کردید</string>
<string name="notice_room_withdraw_by_you">دعوت %1$s را پس‌گرفتید</string>
<string name="notice_avatar_url_changed_by_you">آواتارتان را عوض کردید</string>
<string name="notice_display_name_set_by_you">نام نمایشیتان را به %1$s تغییر دادید</string>
<string name="notice_display_name_changed_from_by_you">نام نمایشیتان را از %1$s به %2$s تغییر دادید</string>
<string name="notice_display_name_removed_by_you">نام نمایشیتان را برداشتید (%1$s بود)</string>
<string name="notice_room_topic_changed_by_you">موضوع را به %1$s تغییر دادید</string>
<string name="notice_room_avatar_changed">%1$s آواتار اتاق را تغییر داد</string>
<string name="notice_room_avatar_changed_by_you">آواتار اتاق را تغییر دادید</string>
<string name="notice_room_name_changed_by_you">نام اتاق را به %1$s تغییر دادید</string>
<string name="notice_placed_video_call_by_you">تماس تصویری گرفتید.</string>
<string name="notice_placed_voice_call_by_you">تماس صوتی گرفتید.</string>
<string name="notice_call_candidates">%s برای برپایی تماس، داده فرستاد.</string>
<string name="notice_call_candidates_by_you">برای برپایی تماس، داده فرستادید.</string>
<string name="notice_answered_call_by_you">تماس را پاسخ دادید.</string>
<string name="notice_ended_call_by_you">به تماس پایان دادید.</string>
<string name="notice_made_future_room_visibility_by_you">تاریخچهٔ آتی اتاق را برای %1$s نمایان کردید</string>
<string name="notice_end_to_end_by_you">رمزنگاری سرتاسری را روشن کردید (%1$s)</string>
<string name="notice_room_update_by_you">این اتاق را ارتقا دادید.</string>
<string name="notice_requested_voip_conference_by_you">دارخواست کنفرانس ویپ دادید</string>
<string name="notice_room_name_removed_by_you">نام اتاق را برداشتید</string>
<string name="notice_room_topic_removed_by_you">موضوع اتاق را برداشتید</string>
<string name="notice_room_avatar_removed">%1$s آواتار اتاق را برداشت</string>
<string name="notice_room_avatar_removed_by_you">آواتار اتاق را برداشتید</string>
<string name="notice_profile_change_redacted_by_you">نمایه‌تان را به‌روز کردید %1$s</string>
<string name="notice_room_third_party_invite_by_you">برای %1$s دعوت پیوستن به اتاق فرستادید</string>
<string name="notice_room_third_party_revoked_invite_by_you">دعوت پیوستن %1$s به اتاق را پس گرفتید</string>
<string name="notice_room_third_party_registered_invite_by_you">دعوت برای %1$s را پذیرفتید</string>
<string name="notice_widget_added">%1$s ابزارک %2$s را افزود</string>
<string name="notice_widget_added_by_you">ابزارک %1$s را افزودید</string>
<string name="notice_widget_removed">%1$s ابزارک %2$s را برداشت</string>
<string name="notice_widget_removed_by_you">ابزارک %1$s را برداشتید</string>
<string name="notice_widget_modified">%1$s ابزارک %2$s را دستکاری کرد</string>
<string name="notice_widget_modified_by_you">ابزارک %1$s را دستکاری کردید</string>
<string name="power_level_admin">مدیر</string>
<string name="power_level_moderator">ناظم</string>
<string name="power_level_default">پیش‌گزیده</string>
<string name="power_level_custom">سفارشی (%1$d)</string>
<string name="power_level_custom_no_value">سفارشی</string>
<string name="notice_power_level_changed_by_you">سطح قدرت %1$s را تغییر دادید.</string>
<string name="notice_power_level_changed">%1$s سطح قدرت %2$s را تغییر داد.</string>
<string name="notice_power_level_diff">%1$s از %2$s به %3$s</string>
<string name="notice_room_invite_no_invitee_with_reason_by_you">دعوتتان. دلیل: %1$s</string>
<string name="notice_room_invite_with_reason_by_you">%1$s را دعوت کردید. دلیل: %2$s</string>
<string name="notice_room_join_with_reason_by_you">به اتاق پیوستید. دلیل: %1$s</string>
<string name="notice_room_leave_with_reason_by_you">اتاق را ترک کردید. دلیل: %1$s</string>
<string name="notice_room_reject_with_reason_by_you">دعوت را رد کردید. دلیل: %1$s</string>
<string name="notice_room_kick_with_reason_by_you">%1$s را اخراج کردید. دلیل: %2$s</string>
<string name="notice_room_unban_with_reason_by_you">تحریم %1$s را برداشتید. دلیل: %2$s</string>
<string name="notice_room_ban_with_reason_by_you">%1$s را تحریم کردید. دلیل: %2$s</string>
<string name="notice_room_third_party_invite_with_reason_by_you">دعوتی به %1$s برای پیوستن به اتاق فرستادید. دلیل: %2$s</string>
<string name="notice_room_third_party_revoked_invite_with_reason_by_you">دعوت %1$s برای پیوستن به اتاق را پس گرفتید. دلیل: %2$s</string>
<string name="notice_room_third_party_registered_invite_with_reason_by_you">دعوت برای %1$s را پذیرفتید. دلیل: %2$s</string>
<string name="notice_room_withdraw_with_reason_by_you">دعوت %1$s را رد کردید. دلیل: %2$s</string>
<plurals name="notice_room_aliases_added_by_you">
<item quantity="one">نشانی %1$s را به این اتاق افزودید.</item>
<item quantity="other">نشانی‌های %1$s را به این اتاق افزودید.</item>
</plurals>
<plurals name="notice_room_aliases_removed_by_you">
<item quantity="one">نشانی %1$s ار از این اتاق برداشتید.</item>
<item quantity="other">نشانی‌های %1$s ار از این اتاق برداشتید.</item>
</plurals>
<string name="notice_room_aliases_added_and_removed_by_you">نشانی %1$s ار افزوده و %2$s را از این اتاق برداشتید.</string>
<string name="notice_room_canonical_alias_set_by_you">نشانی اصلی این اتاق را به %1$s تنظیم کردید.</string>
<string name="notice_room_canonical_alias_unset_by_you">نشانی اصلی این اتاق را برداشتید.</string>
<string name="notice_room_guest_access_can_join_by_you">به میهمانان اجازهٔ پیوستن به گروه دادید.</string>
<string name="notice_room_guest_access_forbidden_by_you">میمهانان را از پیوستن به گروه بازداشتید.</string>
<string name="notice_end_to_end_ok_by_you">رمزنگاری سرتاسری را روشن کردید.</string>
<string name="notice_end_to_end_unknown_algorithm_by_you">رمزنگاری سرتاسری را روشن کردید (الگوریتم ناشناخته %1$s).</string>
</resources>

View file

@ -201,7 +201,7 @@
<item quantity="other">Tekkseḍ %2$s am tansiwin i texxamt-a.</item>
</plurals>
<string name="notice_room_aliases_added_and_removed">%1$s yerna %2$s terniḍ tekkseḍ %3s am tansiwin i texxamt-a.</string>
<string name="notice_room_aliases_added_and_removed">%1$s yerna %2$s terniḍ tekkseḍ %3$s am tansiwin i texxamt-a.</string>
<string name="notice_room_aliases_added_and_removed_by_you">Terniḍ %1$s terniḍ tekkseḍ %2$s am tansiwin i texxamt-a.</string>
<string name="notice_room_canonical_alias_set">%1$s isbadu %2$s am tansa tagejdant i texxamt-a.</string>

View file

@ -138,4 +138,96 @@
<string name="key_verification_request_fallback_message">%s po kërkon të verifikojë kyçin tuaj, por klienti juaj nuk mbulon verifikim kyçesh brenda fjalosjeje. Që të verifikoni kyça, do tju duhet të përdorni verifikim të dikurshëm kyçesh.</string>
<string name="notice_room_created">%1$s krijo dhomën</string>
<string name="summary_you_sent_image">Dërguat një figurë.</string>
<string name="summary_you_sent_sticker">Dërguat një ngjitës.</string>
<string name="notice_room_invite_no_invitee_by_you">Ftesa juaj</string>
<string name="notice_room_created_by_you">Krijuat dhomën</string>
<string name="notice_room_invite_by_you">Ftuat %1$s</string>
<string name="notice_room_join_by_you">Hytë në dhomë</string>
<string name="notice_room_leave_by_you">Dolët nga dhoma</string>
<string name="notice_room_reject_by_you">Hodhët poshtë ftesën</string>
<string name="notice_room_kick_by_you">Përzutë %1$s</string>
<string name="notice_room_unban_by_you">Hoqët dëbimin për %1$s</string>
<string name="notice_room_ban_by_you">Dëbuat %1$s</string>
<string name="notice_room_withdraw_by_you">Tërhoqët mbrapsht ftesën për %1$s</string>
<string name="notice_avatar_url_changed_by_you">Ndryshuat avatarin tuaj</string>
<string name="notice_display_name_set_by_you">Caktuat si emrin tuaj në ekran %1$s</string>
<string name="notice_display_name_changed_from_by_you">E ndryshuat emrin tuaj në ekran nga %1$s në %2$s</string>
<string name="notice_display_name_removed_by_you">Hoqët emrin tuaj në ekran (qe %1$s)</string>
<string name="notice_room_topic_changed_by_you">E ndryshuat temën në: %1$s</string>
<string name="notice_room_avatar_changed">%1$s ndryshoi avatarin e dhomës</string>
<string name="notice_room_avatar_changed_by_you">Ndryshuat avatarin e dhomës</string>
<string name="notice_room_name_changed_by_you">Ndryshuat emrin e dhomës në: %1$s</string>
<string name="notice_placed_video_call_by_you">Filluat një thirrje video.</string>
<string name="notice_placed_voice_call_by_you">Filluat një thirrje zanore.</string>
<string name="notice_call_candidates">%s dërgoi të dhëna për ujdisjen e thirrjes.</string>
<string name="notice_call_candidates_by_you">Dërguat të dhëna për ujdisjen e thirrjes.</string>
<string name="notice_answered_call_by_you">Iu përgjigjët thirrjes.</string>
<string name="notice_ended_call_by_you">E përfunduat thirrjen.</string>
<string name="notice_made_future_room_visibility_by_you">E bëtë historikun e ardhshëm të dhomë të dukshëm për %1$s</string>
<string name="notice_end_to_end_by_you">Aktivizuat fshehtëzim skaj-më-skaj (%1$s)</string>
<string name="notice_room_update_by_you">Përmirësuat këtë dhomë.</string>
<string name="notice_requested_voip_conference_by_you">Kërkuat një konferencë VoIP</string>
<string name="notice_room_name_removed_by_you">Hoqët emrin e dhomës</string>
<string name="notice_room_topic_removed_by_you">Hoqët temën e dhomës</string>
<string name="notice_room_avatar_removed">%1$s hoqi avatarin e dhomës</string>
<string name="notice_room_avatar_removed_by_you">Hoqët avatarin e dhomës</string>
<string name="notice_profile_change_redacted_by_you">Përditësuat profilin tuaj %1$s</string>
<string name="notice_room_third_party_invite_by_you">Dërguat një ftesë te %1$s për të ardhur te dhoma</string>
<string name="notice_room_third_party_revoked_invite_by_you">Shfuqizuat ftesën për ardhjen në dhomë të %1$s</string>
<string name="notice_room_third_party_registered_invite_by_you">Pranuat ftesën për %1$s</string>
<string name="notice_widget_added">%1$s shtoi widget-in %2$s</string>
<string name="notice_widget_added_by_you">Shtuat widget-in %1$s</string>
<string name="notice_widget_removed">%1$s hoqi widget-in %2$s</string>
<string name="notice_widget_removed_by_you">Hoqët widget-in %1$s</string>
<string name="notice_widget_modified">%1$s ndryshoi widget-in %2$s</string>
<string name="notice_widget_modified_by_you">Ndryshuat widget-in %1$s</string>
<string name="power_level_admin">Përgjegjës</string>
<string name="power_level_moderator">Moderator</string>
<string name="power_level_default">Parazgjedhje</string>
<string name="power_level_custom">Vetjake (%1$d)</string>
<string name="power_level_custom_no_value">Vetjake</string>
<string name="notice_power_level_changed_by_you">Ndryshuat shkallën e pushtetit për %1$s.</string>
<string name="notice_power_level_changed">%1$s ndryshoi shkallën e pushtetit për %2$s.</string>
<string name="notice_power_level_diff">%1$s nga %2$s në %3$s</string>
<string name="notice_room_invite_no_invitee_with_reason_by_you">Ftesa juaj. Arsye: %1$s</string>
<string name="notice_room_invite_with_reason_by_you">Ftuat %1$s. Arsye: %2$s</string>
<string name="notice_room_join_with_reason_by_you">Erdhët në dhomë, Arsye: %1$s</string>
<string name="notice_room_leave_with_reason_by_you">Ikët nga dhoma. Arsye: %1$s</string>
<string name="notice_room_reject_with_reason_by_you">Hodhët poshtë ftesën. Arsye: %1$s</string>
<string name="notice_room_kick_with_reason_by_you">Përzutë %1$s. Arsye: %2$s</string>
<string name="notice_room_unban_with_reason_by_you">Hoqët dëbimin për %1$s. Arsye: %2$s</string>
<string name="notice_room_ban_with_reason_by_you">Dëbuat %1$s. Arsye: %2$s</string>
<string name="notice_room_third_party_invite_with_reason_by_you">Dërguat një ftesë për %1$s të vijë në dhomë. Arsye: %2$s</string>
<string name="notice_room_third_party_revoked_invite_with_reason_by_you">Shfuqizuat ftesën për ardhjen në dhomë të %1$s. Arsye: %2$s</string>
<string name="notice_room_third_party_registered_invite_with_reason_by_you">Pranuat ftesën për %1$s. Arsye: %2$s</string>
<string name="notice_room_withdraw_with_reason_by_you">Tërhoqët mbrapsht ftesën për %1$s. Arsye: %2$s</string>
<plurals name="notice_room_aliases_added_by_you">
<item quantity="one">Shtuat %1$s si një adresë për këtë dhomë.</item>
<item quantity="other">Shtuat %1$s si adresa për këtë dhomë.</item>
</plurals>
<plurals name="notice_room_aliases_removed_by_you">
<item quantity="one">Hoqët %1$s si një adresë për këtë dhomë.</item>
<item quantity="other">Hoqët %1$s si adresa për këtë dhomë.</item>
</plurals>
<string name="notice_room_aliases_added_and_removed_by_you">Shtuat %1$s dhe hoqët %2$s si adresa për këtë dhomë.</string>
<string name="notice_room_canonical_alias_set_by_you">Caktuat si adresë kryesore për këtë dhomë %1$s.</string>
<string name="notice_room_canonical_alias_unset_by_you">Hoqët adresën kryesore për këtë dhomë.</string>
<string name="notice_room_guest_access_can_join_by_you">Keni lejuar të vijnë mysafirë në dhomë.</string>
<string name="notice_room_guest_access_forbidden_by_you">U keni penguar mysafirëve të vijnë në dhomë.</string>
<string name="notice_end_to_end_ok_by_you">Aktivizuat fshehtëzimin skaj-më-skaj.</string>
<string name="notice_end_to_end_unknown_algorithm_by_you">Aktivizuat fshehtëzimin skaj-më-skaj (algoritëm %1$s i panjohur).</string>
</resources>

View file

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.crypto.verification.qrcode
import org.matrix.android.sdk.MatrixTest
import org.amshove.kluent.shouldEqualTo
import org.amshove.kluent.shouldBeEqualTo
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runners.MethodSorters
@ -37,17 +37,17 @@ class BinaryStringTest: MatrixTest {
val str = byteArray.toString(Charsets.ISO_8859_1)
str.length shouldEqualTo 256
str.length shouldBeEqualTo 256
// Ok convert back to bytearray
val result = str.toByteArray(Charsets.ISO_8859_1)
result.size shouldEqualTo 256
result.size shouldBeEqualTo 256
for (i in 0..255) {
result[i] shouldEqualTo i.toByte()
result[i] shouldEqualTo byteArray[i]
result[i] shouldBeEqualTo i.toByte()
result[i] shouldBeEqualTo byteArray[i]
}
}
}

View file

@ -79,9 +79,5 @@ layout_constraintLeft_
### Use im.vector.app.core.preference.VectorPreference to support multiline of the title
<Preference\n
### Will crash on API < 21. Use ?colorAccent instead
\?android:colorAccent
\?android:attr/colorAccent
### Use androidx.recyclerview.widget.RecyclerView because EpoxyRecyclerViews add behavior we do not want to
<com\.airbnb\.epoxy\.EpoxyRecyclerView

View file

@ -96,7 +96,6 @@
<data android:host="element" />
</intent-filter>
</activity>
<activity android:name=".features.media.ImageMediaViewerActivity" />
<!-- Add tools:ignore="Instantiatable" for the error reported only by Buildkite :/ -->
<activity
@ -112,7 +111,6 @@
android:name=".features.settings.VectorSettingsActivity"
android:label="@string/title_activity_settings"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".features.media.VideoMediaViewerActivity" />
<activity
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
android:label="@string/title_activity_keys_backup_setup" />

View file

@ -18,8 +18,8 @@
package im.vector.app
import arrow.core.Option
import org.matrix.android.sdk.api.session.Session
import im.vector.app.core.utils.BehaviorDataSource
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -20,9 +20,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import arrow.core.Option
import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID
import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.home.HomeRoomListDataSource
@ -32,6 +29,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.addTo
import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.rx.rx
import java.util.concurrent.TimeUnit
import javax.inject.Inject

View file

@ -51,9 +51,7 @@ import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.link.LinkHandlerActivity
import im.vector.app.features.login.LoginActivity
import im.vector.app.features.media.BigImageViewerActivity
import im.vector.app.features.media.ImageMediaViewerActivity
import im.vector.app.features.media.VectorAttachmentViewerActivity
import im.vector.app.features.media.VideoMediaViewerActivity
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.permalink.PermalinkHandlerActivity
import im.vector.app.features.pin.PinLocker
@ -125,10 +123,8 @@ interface ScreenComponent {
fun inject(activity: MainActivity)
fun inject(activity: RoomDirectoryActivity)
fun inject(activity: BugReportActivity)
fun inject(activity: ImageMediaViewerActivity)
fun inject(activity: FilteredRoomsActivity)
fun inject(activity: CreateRoomActivity)
fun inject(activity: VideoMediaViewerActivity)
fun inject(activity: CreateDirectRoomActivity)
fun inject(activity: IncomingShareActivity)
fun inject(activity: SoftLogoutActivity)

View file

@ -42,18 +42,25 @@ abstract class BottomSheetActionItem : VectorEpoxyModel<BottomSheetActionItem.Ho
@EpoxyAttribute
@DrawableRes
var iconRes: Int = 0
@EpoxyAttribute
var textRes: Int = 0
@EpoxyAttribute
var showExpand = false
@EpoxyAttribute
var expanded = false
@EpoxyAttribute
var selected = false
@EpoxyAttribute
var subMenuItem = false
@EpoxyAttribute
var destructive = false
@EpoxyAttribute
lateinit var listener: View.OnClickListener

View file

@ -37,14 +37,19 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
@EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute
lateinit var matrixItem: MatrixItem
@EpoxyAttribute
lateinit var body: CharSequence
@EpoxyAttribute
var time: CharSequence? = null
@EpoxyAttribute
var movementMethod: MovementMethod? = null
@EpoxyAttribute
var userClicked: (() -> Unit)? = null

View file

@ -33,10 +33,13 @@ abstract class BottomSheetQuickReactionsItem : VectorEpoxyModel<BottomSheetQuick
@EpoxyAttribute
lateinit var fontProvider: EmojiCompatFontProvider
@EpoxyAttribute
lateinit var texts: List<String>
@EpoxyAttribute
lateinit var selecteds: List<Boolean>
@EpoxyAttribute
var listener: Listener? = null

View file

@ -34,8 +34,10 @@ abstract class BottomSheetSendStateItem : VectorEpoxyModel<BottomSheetSendStateI
@EpoxyAttribute
var showProgress: Boolean = false
@EpoxyAttribute
lateinit var text: CharSequence
@EpoxyAttribute
@DrawableRes
var drawableStart: Int = 0

View file

@ -30,6 +30,7 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxy
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var editable: Boolean = true
@EpoxyAttribute
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null

View file

@ -38,6 +38,7 @@ abstract class ProfileActionItem : VectorEpoxyModel<ProfileActionItem.Holder>()
@EpoxyAttribute
lateinit var title: String
@EpoxyAttribute
var subtitle: String? = null

View file

@ -20,8 +20,8 @@ import android.content.Context
import androidx.annotation.StringRes
import androidx.core.text.HtmlCompat
import im.vector.app.R
import org.matrix.android.sdk.api.failure.MatrixError
import me.gujun.android.span.span
import org.matrix.android.sdk.api.failure.MatrixError
class ResourceLimitErrorFormatter(private val context: Context) {

View file

@ -33,8 +33,6 @@ import timber.log.Timber
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.lang.Exception
import java.lang.IllegalArgumentException
class VectorGlideModelLoaderFactory(private val activeSessionHolder: ActiveSessionHolder)
: ModelLoaderFactory<ImageContentRenderer.Data, InputStream> {

View file

@ -26,11 +26,11 @@ class CheckableFrameLayout : FrameLayout, Checkable {
private var mChecked = false
constructor(context: Context) : super(context) {}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun isChecked(): Boolean {
return mChecked

View file

@ -25,8 +25,8 @@ import butterknife.BindView
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.hideKeyboard
import org.matrix.android.sdk.api.session.Session
import kotlinx.android.synthetic.main.activity.*
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
/**

View file

@ -177,7 +177,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
}
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("onCreate Activity ${this.javaClass.simpleName}")
Timber.i("onCreate Activity ${javaClass.simpleName}")
val vectorComponent = getVectorComponent()
screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this)
val timeForInjection = measureTimeMillis {
@ -305,7 +305,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onDestroy() {
super.onDestroy()
Timber.i("onDestroy Activity ${this.javaClass.simpleName}")
Timber.i("onDestroy Activity ${javaClass.simpleName}")
unBinder?.unbind()
unBinder = null
@ -333,7 +333,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onResume() {
super.onResume()
Timber.i("onResume Activity ${this.javaClass.simpleName}")
Timber.i("onResume Activity ${javaClass.simpleName}")
configurationViewModel.onActivityResumed()
@ -373,7 +373,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onPause() {
super.onPause()
Timber.i("onPause Activity ${this.javaClass.simpleName}")
Timber.i("onPause Activity ${javaClass.simpleName}")
rageShake.stop()

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