diff --git a/README.md b/README.md index 9a6f40db8017097b09e3dd6dd9273160f418ba8a..fc13316d1ce18d107cb4f62a3f9becf27cdc5c58 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ Matrix SDK for Android, extracted from the Element Android application. The SDK is still in beta, and replaces the [legacy Matrix Android SDK](https://github.com/matrix-org/matrix-android-sdk) provided by Matrix.org +## Important notice + +<b>For now, this project is an extract of the Matrix SDK module from Element Android. Please do not open a pull request on this project. If you want to propose a change on the SDK, please open a PR on https://github.com/vector-im/element-android. Thanks!</b> + ## About This repository contains the matrix-android-sdk extracted from the project [Element Android](https://github.com/vector-im/element-android) diff --git a/build.gradle b/build.gradle index 19a244554cc574e28dfec831df83e59110432f8e..f8730fb59ed71055c2b27e3faf8a8f2a1651d04b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.20' - ext.kotlin_coroutines_version = "1.4.1" + // Ref: https://kotlinlang.org/releases.html + ext.kotlin_version = '1.4.31' + ext.kotlin_coroutines_version = "1.4.2" repositories { google() jcenter() @@ -11,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong @@ -31,10 +32,6 @@ allprojects { includeGroupByRegex "com\\.github\\.Zhuinden" } } - // Jitsi repo - maven { - url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-2.9.3" - } google() jcenter() } diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index fd10ea31153f54d0fad27f5b12b2773c601d2d2d..7053e7f1baa7e65eccd1fa323a44cd6ce63407f7 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath "io.realm:realm-gradle-plugin:10.1.2" + classpath "io.realm:realm-gradle-plugin:10.3.1" } } @@ -21,7 +21,7 @@ android { minSdkVersion 21 targetSdkVersion 30 versionCode 1 - versionName "1.0.16" + versionName "1.1.1" // Multidex is useful for tests multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -112,9 +112,9 @@ dependencies { def lifecycle_version = '2.2.0' def arch_version = '2.1.0' def markwon_version = '3.1.0' - def daggerVersion = '2.31' - def work_version = '2.4.0' - def retrofit_version = '2.6.2' + def daggerVersion = '2.33' + def work_version = '2.5.0' + def retrofit_version = '2.9.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" @@ -130,7 +130,7 @@ dependencies { implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" - implementation(platform("com.squareup.okhttp3:okhttp-bom:4.8.1")) + implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1")) implementation 'com.squareup.okhttp3:okhttp' implementation 'com.squareup.okhttp3:logging-interceptor' implementation 'com.squareup.okhttp3:okhttp-urlconnection' @@ -141,11 +141,11 @@ dependencies { implementation "ru.noties.markwon:core:$markwon_version" // Image - implementation 'androidx.exifinterface:exifinterface:1.3.1' + implementation 'androidx.exifinterface:exifinterface:1.3.2' // Database implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' - kapt 'dk.ilios:realmfieldnameshelper:1.1.1' + kapt 'dk.ilios:realmfieldnameshelper:2.0.0' // Work implementation "androidx.work:work-runtime-ktx:$work_version" @@ -155,7 +155,7 @@ dependencies { implementation "io.arrow-kt:arrow-instances-core:$arrow_version" // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm - implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2' + implementation 'org.matrix.gitlab.matrix-org:olm:3.2.2' // DI implementation "com.google.dagger:dagger:$daggerVersion" @@ -166,20 +166,14 @@ dependencies { implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.19' - // Web RTC - // org.webrtc:google-webrtc is for development purposes only. See http://webrtc.github.io/webrtc-org/native-code/android/ - // implementation 'org.webrtc:google-webrtc:1.0.+' - // Use the same WebRTC library than the one used by Jitsi library - implementation('com.facebook.react:react-native-webrtc:1.84.0-jitsi-5112273@aar') - - testImplementation 'junit:junit:4.13' - testImplementation 'org.robolectric:robolectric:4.3' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.5.1' //testImplementation 'org.robolectric:shadows-support-v4:3.0' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 - testImplementation 'io.mockk:mockk:1.9.2.kotlin12' - testImplementation 'org.amshove.kluent:kluent-android:1.61' + testImplementation 'io.mockk:mockk:1.10.6' + testImplementation 'org.amshove.kluent:kluent-android:1.65' testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' @@ -192,7 +186,7 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'org.amshove.kluent:kluent-android:1.61' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 - androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12' + androidTestImplementation 'io.mockk:mockk-android:1.10.6' androidTestImplementation "androidx.arch.core:core-testing:$arch_version" androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt index b0df6fcb44ba66b82ca490721e01584d667a0539..01c4f8ccb3b0e76285b4f9790bfe68dcce072add 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt @@ -26,15 +26,12 @@ import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth -import org.matrix.android.sdk.api.auth.data.LoginFlowResult import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.api.auth.registration.RegistrationResult import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.SessionTestParams import org.matrix.android.sdk.common.TestConstants -import org.matrix.android.sdk.common.TestMatrixCallback import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -46,12 +43,13 @@ class DeactivateAccountTest : InstrumentedTest { @Test fun deactivateAccountTest() { - val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false)) + val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true)) // Deactivate the account commonTestHelper.runBlockingTest { session.deactivateAccount( - object : UserInteractiveAuthInterceptor { + eraseAllData = false, + userInteractiveAuthInterceptor = object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { promise.resume( UserPasswordAuth( @@ -61,7 +59,8 @@ class DeactivateAccountTest : InstrumentedTest { ) ) } - }, false) + } + ) } // Try to login on the previous account, it will fail (M_USER_DEACTIVATED) @@ -75,23 +74,23 @@ class DeactivateAccountTest : InstrumentedTest { // Try to create an account with the deactivate account user id, it will fail (M_USER_IN_USE) val hs = commonTestHelper.createHomeServerConfig() - commonTestHelper.doSync<LoginFlowResult> { - commonTestHelper.matrix.authenticationService.getLoginFlow(hs, it) + commonTestHelper.runBlockingTest { + commonTestHelper.matrix.authenticationService.getLoginFlow(hs) } var accountCreationError: Throwable? = null - commonTestHelper.waitWithLatch { - commonTestHelper.matrix.authenticationService - .getRegistrationWizard() - .createAccount(session.myUserId.substringAfter("@").substringBefore(":"), - TestConstants.PASSWORD, - null, - object : TestMatrixCallback<RegistrationResult>(it, false) { - override fun onFailure(failure: Throwable) { - accountCreationError = failure - super.onFailure(failure) - } - }) + commonTestHelper.runBlockingTest { + try { + commonTestHelper.matrix.authenticationService + .getRegistrationWizard() + .createAccount( + session.myUserId.substringAfter("@").substringBefore(":"), + TestConstants.PASSWORD, + null + ) + } catch (failure: Throwable) { + accountCreationError = failure + } } // Test the error diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index a4dbd70b116bbf97e14ff3ed58e31a7e6445d613..287a4233d990715176a0655c767a60a30cbf3bfc 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -19,11 +19,19 @@ package org.matrix.android.sdk.common import android.content.Context import android.net.Uri import androidx.lifecycle.Observer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.api.auth.data.LoginFlowResult import org.matrix.android.sdk.api.auth.registration.RegistrationResult import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -35,15 +43,6 @@ 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.api.session.sync.SyncState -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue import java.util.ArrayList import java.util.UUID import java.util.concurrent.CountDownLatch @@ -60,7 +59,13 @@ class CommonTestHelper(context: Context) { fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor init { - Matrix.initialize(context, MatrixConfiguration("TestFlavor")) + Matrix.initialize( + context, + MatrixConfiguration( + applicationFlavor = "TestFlavor", + roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() + ) + ) matrix = Matrix.getInstance(context) } @@ -210,22 +215,21 @@ class CommonTestHelper(context: Context) { sessionTestParams: SessionTestParams): Session { val hs = createHomeServerConfig() - doSync<LoginFlowResult> { - matrix.authenticationService - .getLoginFlow(hs, it) + runBlockingTest { + matrix.authenticationService.getLoginFlow(hs) } - doSync<RegistrationResult>(timeout = 60_000) { + runBlockingTest(timeout = 60_000) { matrix.authenticationService .getRegistrationWizard() - .createAccount(userName, password, null, it) + .createAccount(userName, password, null) } // Perform dummy step - val registrationResult = doSync<RegistrationResult>(timeout = 60_000) { + val registrationResult = runBlockingTest(timeout = 60_000) { matrix.authenticationService .getRegistrationWizard() - .dummy(it) + .dummy() } assertTrue(registrationResult is RegistrationResult.Success) @@ -249,15 +253,14 @@ class CommonTestHelper(context: Context) { sessionTestParams: SessionTestParams): Session { val hs = createHomeServerConfig() - doSync<LoginFlowResult> { - matrix.authenticationService - .getLoginFlow(hs, it) + runBlockingTest { + matrix.authenticationService.getLoginFlow(hs) } - val session = doSync<Session> { + val session = runBlockingTest { matrix.authenticationService .getLoginWizard() - .login(userName, password, "myDevice", it) + .login(userName, password, "myDevice") } if (sessionTestParams.withInitialSync) { @@ -277,21 +280,19 @@ class CommonTestHelper(context: Context) { password: String): Throwable { val hs = createHomeServerConfig() - doSync<LoginFlowResult> { - matrix.authenticationService - .getLoginFlow(hs, it) + runBlockingTest { + matrix.authenticationService.getLoginFlow(hs) } var requestFailure: Throwable? = null - waitWithLatch { latch -> - matrix.authenticationService - .getLoginWizard() - .login(userName, password, "myDevice", object : TestMatrixCallback<Session>(latch, onlySuccessful = false) { - override fun onFailure(failure: Throwable) { - requestFailure = failure - super.onFailure(failure) - } - }) + runBlockingTest { + try { + matrix.authenticationService + .getLoginWizard() + .login(userName, password, "myDevice") + } catch (failure: Throwable) { + requestFailure = failure + } } assertNotNull(requestFailure) @@ -390,8 +391,8 @@ fun List<TimelineEvent>.checkSendOrder(baseTextMessage: String, numberOfMessages return drop(startIndex) .take(numberOfMessages) .foldRightIndexed(true) { index, timelineEvent, acc -> - val body = timelineEvent.root.content.toModel<MessageContent>()?.body - val currentMessageSuffix = numberOfMessages - index - acc && (body == null || body.startsWith(baseTextMessage) && body.endsWith("#$currentMessageSuffix")) - } + val body = timelineEvent.root.content.toModel<MessageContent>()?.body + val currentMessageSuffix = numberOfMessages - index + acc && (body == null || body.startsWith(baseTextMessage) && body.endsWith("#$currentMessageSuffix")) + } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestRoomDisplayNameFallbackProvider.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestRoomDisplayNameFallbackProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..7a1d4604f05e4bdfc6006137b93ff3e7af79d5bf --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestRoomDisplayNameFallbackProvider.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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.common + +import org.matrix.android.sdk.api.RoomDisplayNameFallbackProvider + +class TestRoomDisplayNameFallbackProvider() : RoomDisplayNameFallbackProvider { + + override fun getNameForRoomInvite() = + "Room invite" + + override fun getNameForEmptyRoom() = + "Empty room" + + override fun getNameFor2members(name1: String?, name2: String?) = + "$name1 and $name2" + + override fun getNameFor3members(name1: String?, name2: String?, name3: String?) = + "$name1, $name2 and $name3" + + override fun getNameFor4members(name1: String?, name2: String?, name3: String?, name4: String?) = + "$name1, $name2, $name3 and $name4" + + override fun getNameFor4membersAndMore(name1: String?, name2: String?, name3: String?, remainingCount: Int) = + "$name1, $name2, $name3 and $remainingCount others" +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..122584142e57fc5c03a6bca2d5eac3452150d287 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -0,0 +1,103 @@ +/* + * 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 + +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +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.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent +import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class PreShareKeysTest : InstrumentedTest { + + private val mTestHelper = CommonTestHelper(context()) + private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + + @Test + fun ensure_outbound_session_happy_path() { + val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) + val e2eRoomID = testData.roomId + val aliceSession = testData.firstSession + val bobSession = testData.secondSession!! + + // clear any outbound session + aliceSession.cryptoService().discardOutboundSession(e2eRoomID) + + val preShareCount = bobSession.cryptoService().getGossipingEvents().count { + it.senderId == aliceSession.myUserId + && it.getClearType() == EventType.ROOM_KEY + } + + assertEquals(0, preShareCount, "Bob should not have receive any key from alice at this point") + Log.d("#Test", "Room Key Received from alice $preShareCount") + + // Force presharing of new outbound key + mTestHelper.doSync<Unit> { + aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it) + } + + mTestHelper.waitWithLatch { latch -> + mTestHelper.retryPeriodicallyWithLatch(latch) { + val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { + it.senderId == aliceSession.myUserId + && it.getClearType() == EventType.ROOM_KEY + } + newGossipCount > preShareCount + } + } + + val latest = bobSession.cryptoService().getGossipingEvents().lastOrNull { + it.senderId == aliceSession.myUserId + && it.getClearType() == EventType.ROOM_KEY + } + + val content = latest?.getClearContent().toModel<RoomKeyContent>() + assertNotNull(content, "Bob should have received and decrypted a room key event from alice") + assertEquals(e2eRoomID, content.roomId, "Wrong room") + val megolmSessionId = content.sessionId!! + + val sharedIndex = aliceSession.cryptoService().getSharedWithInfo(e2eRoomID, megolmSessionId) + .getObject(bobSession.myUserId, bobSession.sessionParams.deviceId) + + assertEquals(0, sharedIndex, "The session received by bob should match what alice sent") + + // Just send a real message as test + val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() + + assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session") + mTestHelper.waitWithLatch { latch -> + mTestHelper.retryPeriodicallyWithLatch(latch) { + bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE + } + } + + mTestHelper.signOutAndClose(aliceSession) + mTestHelper.signOutAndClose(bobSession) + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index 80cc14fcb69e1c4eb8171128152110daf84a985d..b2516ea2beec915103843d2b62c66b8091888d2a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -51,7 +51,7 @@ class WithHeldTests : InstrumentedTest { // ============================= val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - val bobSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) // Initialize cross signing on both mCryptoTestHelper.initializeCrossSigning(aliceSession) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt index ae300c936d89acf552e9ee3db5aad25b21d3c695..cadb83ca00bc05fbf53ebc0b389e4e8b59eca4af 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -61,7 +61,7 @@ class SearchMessagesTest : InstrumentedTest { 2) run { - var lock = CountDownLatch(1) + val lock = CountDownLatch(1) val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> snapshot.count { it.root.content.toModel<MessageContent>()?.body?.startsWith(MESSAGE).orFalse() } == 2 @@ -70,7 +70,6 @@ class SearchMessagesTest : InstrumentedTest { aliceTimeline.addListener(eventListener) commonTestHelper.await(lock) - lock = CountDownLatch(1) val data = commonTestHelper.runBlockingTest { aliceSession .searchService() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt index 725fd08d3b4ced867f193c5ba3dd67d6aa44f22d..ed809cdb04520d353eb0ab3febbc1a41cc8fc3f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt @@ -35,7 +35,15 @@ data class MatrixConfiguration( * Optional proxy to connect to the matrix servers * You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port) */ - val proxy: Proxy? = null + val proxy: Proxy? = null, + /** + * True to advertise support for call transfers to other parties on Matrix calls. + */ + val supportsCallTransfer: Boolean = false, + /** + * RoomDisplayNameFallbackProvider to provide default room display name. + */ + val roomDisplayNameFallbackProvider: RoomDisplayNameFallbackProvider ) { /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/RoomDisplayNameFallbackProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/RoomDisplayNameFallbackProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..4ac14d5f63afb92d1d552c108db7f25317df7b51 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/RoomDisplayNameFallbackProvider.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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.api + +interface RoomDisplayNameFallbackProvider { + fun getNameForRoomInvite(): String + fun getNameForEmptyRoom(): String + fun getNameFor2members(name1: String?, name2: String?): String + fun getNameFor3members(name1: String?, name2: String?, name3: String?): String + fun getNameFor4members(name1: String?, name2: String?, name3: String?, name4: String?): String + fun getNameFor4membersAndMore(name1: String?, name2: String?, name3: String?, remainingCount: Int): String +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt index bf21941e0c99c5ee5715bcc26b3212420a57ff00..a7f5163774517dbe0ff588741568028f171a65e9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.auth -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.LoginFlowResult @@ -24,7 +23,6 @@ import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.wellknown.WellknownResult import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.util.Cancelable /** * This interface defines methods to authenticate or to create an account to a matrix server. @@ -32,14 +30,14 @@ import org.matrix.android.sdk.api.util.Cancelable interface AuthenticationService { /** * Request the supported login flows for this homeserver. - * This is the first method to call to be able to get a wizard to login or the create an account + * This is the first method to call to be able to get a wizard to login or to create an account */ - fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable + suspend fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult /** * Request the supported login flows for the corresponding sessionId. */ - fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable + suspend fun getLoginFlowOfSession(sessionId: String): LoginFlowResult /** * Get a SSO url @@ -69,12 +67,12 @@ interface AuthenticationService { /** * Cancel pending login or pending registration */ - fun cancelPendingLoginOrRegistration() + suspend fun cancelPendingLoginOrRegistration() /** * Reset all pending settings, including current HomeServerConnectionConfig */ - fun reset() + suspend fun reset() /** * Check if there is an authenticated [Session]. @@ -91,24 +89,21 @@ interface AuthenticationService { /** * Create a session after a SSO successful login */ - fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, - credentials: Credentials, - callback: MatrixCallback<Session>): Cancelable + suspend fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, + credentials: Credentials): Session /** * Perform a wellknown request, using the domain from the matrixId */ - fun getWellKnownData(matrixId: String, - homeServerConnectionConfig: HomeServerConnectionConfig?, - callback: MatrixCallback<WellknownResult>): Cancelable + suspend fun getWellKnownData(matrixId: String, + homeServerConnectionConfig: HomeServerConnectionConfig?): WellknownResult /** * Authenticate with a matrixId and a password * Usually call this after a successful call to getWellKnownData() */ - fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, - matrixId: String, - password: String, - initialDeviceName: String, - callback: MatrixCallback<Session>): Cancelable + suspend fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, + matrixId: String, + password: String, + initialDeviceName: String): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt index 48705ee7b7dbfbb6c602dca5409ea433bfb018df..9c96cba40c7710ef6edf79ec60e7ba2763ed0f41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.auth.login -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.Cancelable @@ -29,26 +28,23 @@ interface LoginWizard { * @param callback the matrix callback on which you'll receive the result of authentication. * @return a [Cancelable] */ - fun login(login: String, - password: String, - deviceName: String, - callback: MatrixCallback<Session>): Cancelable + suspend fun login(login: String, + password: String, + deviceName: String): Session /** * Exchange a login token to an access token */ - fun loginWithToken(loginToken: String, - callback: MatrixCallback<Session>): Cancelable + suspend fun loginWithToken(loginToken: String): Session /** * Reset user password */ - fun resetPassword(email: String, - newPassword: String, - callback: MatrixCallback<Unit>): Cancelable + suspend fun resetPassword(email: String, + newPassword: String) /** - * Confirm the new password, once the user has checked his email + * Confirm the new password, once the user has checked their email */ - fun resetPasswordMailConfirmed(callback: MatrixCallback<Unit>): Cancelable + suspend fun resetPasswordMailConfirmed() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationWizard.kt index ed7b249f1e67c21f6ecb305cf3687ea893912864..d00c9a0c8211c01b1dafbbdbf47d97db8f731681 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationWizard.kt @@ -16,28 +16,25 @@ package org.matrix.android.sdk.api.auth.registration -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - interface RegistrationWizard { - fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun getRegistrationFlow(): RegistrationResult - fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?): RegistrationResult - fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun performReCaptcha(response: String): RegistrationResult - fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun acceptTerms(): RegistrationResult - fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun dummy(): RegistrationResult - fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult - fun sendAgainThreePid(callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun sendAgainThreePid(): RegistrationResult - fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun handleValidateThreePid(code: String): RegistrationResult - fun checkIfEmailHasBeenValidated(delayMillis: Long, callback: MatrixCallback<RegistrationResult>): Cancelable + suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult val currentThreePid: String? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/VerificationState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/VerificationState.kt new file mode 100644 index 0000000000000000000000000000000000000000..54276a6b51e3cab57d89c05dca9b8e28c6094f7d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/VerificationState.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 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.api.crypto + +enum class VerificationState { + REQUEST, + WAITING, + CANCELED_BY_ME, + CANCELED_BY_OTHER, + DONE +} + +fun VerificationState.isCanceled(): Boolean { + return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt index c06cdd9e23a4efdf8e399259419b3101d1052bcd..e0ee9f36ba8f9b9387afa7dd523e2609abf8e16e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt @@ -53,22 +53,24 @@ fun Throwable.isInvalidUIAAuth(): Boolean { * Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible */ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? { - return if (this is Failure.OtherServerError && this.httpCode == 401) { + return if (this is Failure.OtherServerError && httpCode == 401) { tryOrNull { MoshiProvider.providesMoshi() .adapter(RegistrationFlowResponse::class.java) - .fromJson(this.errorBody) + .fromJson(errorBody) } - } else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) { + } else if (this is Failure.ServerError && httpCode == 401 && error.code == MatrixError.M_FORBIDDEN) { // This happens when the submission for this stage was bad (like bad password) - if (this.error.session != null && this.error.flows != null) { + if (error.session != null && error.flows != null) { RegistrationFlowResponse( - flows = this.error.flows, - session = this.error.session, - completedStages = this.error.completedStages, - params = this.error.params + flows = error.flows, + session = error.session, + completedStages = error.completedStages, + params = error.params ) - } else null + } else { + null + } } else { null } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt new file mode 100644 index 0000000000000000000000000000000000000000..0761ef8d21a6fb731ceb2d1fe1acf0b72078429e --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 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.api.federation + +interface FederationService { + /** + * Get information about the homeserver + */ + suspend fun getFederationVersion(): FederationVersion +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt new file mode 100644 index 0000000000000000000000000000000000000000..2ed3f848b6af0af4202b824cd031cf9327291d6d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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.api.federation + +/** + * Ref: https://matrix.org/docs/spec/server_server/latest#get-matrix-federation-v1-version + */ +data class FederationVersion( + /** + * Arbitrary name that identify this implementation. + */ + val name: String?, + /** + * Version of this implementation. The version format depends on the implementation. + */ + val version: String? +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index ff7c9f0521c6d698c59e202f98b105e7207755b4..7a24ccac111224013585540df912b5f6bb4324dc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.accountdata.AccountDataService @@ -34,6 +35,7 @@ import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.identity.IdentityService +import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.media.MediaService import org.matrix.android.sdk.api.session.permalinks.PermalinkService @@ -48,6 +50,7 @@ import org.matrix.android.sdk.api.session.signout.SignOutService import org.matrix.android.sdk.api.session.sync.FilterService import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.terms.TermsService +import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService import org.matrix.android.sdk.api.session.typing.TypingUsersTracker import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.widgets.WidgetService @@ -212,6 +215,16 @@ interface Session : */ fun searchService(): SearchService + /** + * Returns the federation service associated with the session + */ + fun federationService(): FederationService + + /** + * Returns the third party service associated with the session + */ + fun thirdPartyService(): ThirdPartyService + /** * Add a listener to the session. * @param listener the listener to add. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt index eb327dfd56c18b6df0e75db011a8f6eabce9561f..1f28dbd8afbef2c21b0e9aaee1d080c44b4ae3a9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/account/AccountService.kt @@ -27,7 +27,8 @@ interface AccountService { * @param password Current password. * @param newPassword New password */ - suspend fun changePassword(password: String, newPassword: String) + suspend fun changePassword(password: String, + newPassword: String) /** * Deactivate the account. @@ -41,9 +42,10 @@ interface AccountService { * be shared with any new or unregistered users, but registered users who already have access to these messages will still * have access to their copy. * - * @param password the account password * @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see * an incomplete view of conversations + * @param userInteractiveAuthInterceptor see [UserInteractiveAuthInterceptor] */ - suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean) + suspend fun deactivateAccount(eraseAllData: Boolean, + userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallsListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt similarity index 67% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallsListener.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt index 37ab198487b555ff90561ac6da64b0fbbd1b0bf7..303add747f1715fb538978ea3998e45c3e1e8675 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallsListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt @@ -20,8 +20,11 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent +import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent +import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent +import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent -interface CallsListener { +interface CallListener { /** * Called when there is an incoming call within the room. */ @@ -39,5 +42,23 @@ interface CallsListener { */ fun onCallHangupReceived(callHangupContent: CallHangupContent) + /** + * Called when a called has been rejected + */ + fun onCallRejectReceived(callRejectContent: CallRejectContent) + + /** + * Called when an answer has been selected + */ + fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) + + /** + * Called when a negotiation is sent + */ + fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) + + /** + * Called when the call has been managed by an other session + */ fun onCallManagedByOtherSession(callId: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt index e28c1fa595aeee1a1912094084799a7b22978380..dc67aa536afd0a5b295ed52dae2255c181a7095d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt @@ -16,21 +16,20 @@ package org.matrix.android.sdk.api.session.call -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - interface CallSignalingService { - fun getTurnServer(callback: MatrixCallback<TurnServerResponse>): Cancelable + suspend fun getTurnServer(): TurnServerResponse + + fun getPSTNProtocolChecker(): PSTNProtocolChecker /** * Create an outgoing call */ fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall - fun addCallListener(listener: CallsListener) + fun addCallListener(listener: CallListener) - fun removeCallListener(listener: CallsListener) + fun removeCallListener(listener: CallListener) fun getCallWithId(callId: String): MxCall? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt index 757a09fb3f27089f85d7165a4b4a3cd35c13cdc0..2dbd1c9b01bf03bac7a6176552e69cb279fc8c46 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt @@ -16,13 +16,16 @@ package org.matrix.android.sdk.api.session.call -import org.webrtc.PeerConnection - sealed class CallState { /** Idle, setting up objects */ object Idle : CallState() + /** + * CreateOffer. Intermediate state between Idle and Dialing. + */ + object CreateOffer: CallState() + /** Dialing. Outgoing call is signaling the remote peer */ object Dialing : CallState() @@ -36,8 +39,8 @@ sealed class CallState { * Connected. Incoming/Outgoing call, ice layer connecting or connected * Notice that the PeerState failed is not always final, if you switch network, new ice candidtates * could be exchanged, and the connection could go back to connected - */ - data class Connected(val iceConnectionState: PeerConnection.PeerConnectionState) : CallState() + * */ + data class Connected(val iceConnectionState: MxPeerConnectionState) : CallState() /** Terminated. Incoming/Outgoing call, the call is terminated */ object Terminated : CallState() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/EglUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/EglUtils.kt deleted file mode 100644 index 131779a4dcee7b4d52a4901496c7fe6174c26a3d..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/EglUtils.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.api.session.call - -import org.webrtc.EglBase -import timber.log.Timber - -/** - * The root [EglBase] instance shared by the entire application for - * the sake of reducing the utilization of system resources (such as EGL - * contexts) - * by performing a runtime check. - */ -object EglUtils { - - // TODO how do we release that? - - /** - * Lazily creates and returns the one and only [EglBase] which will - * serve as the root for all contexts that are needed. - */ - @get:Synchronized var rootEglBase: EglBase? = null - get() { - if (field == null) { - val configAttributes = EglBase.CONFIG_PLAIN - try { - field = EglBase.createEgl14(configAttributes) - ?: EglBase.createEgl10(configAttributes) // Fall back to EglBase10. - } catch (ex: Throwable) { - Timber.e(ex, "Failed to create EglBase") - } - } - return field - } - private set - - val rootEglBaseContext: EglBase.Context? - get() { - val eglBase = rootEglBase - return eglBase?.eglBaseContext - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt index a1ab687894c3f3b61b7ba43ee650ad89947f6bd4..7533619eb074b111279a771e1b211b0357195b58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxCall.kt @@ -16,14 +16,17 @@ package org.matrix.android.sdk.api.session.call -import org.webrtc.IceCandidate -import org.webrtc.SessionDescription +import org.matrix.android.sdk.api.session.room.model.call.CallCandidate +import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities +import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent +import org.matrix.android.sdk.api.session.room.model.call.SdpType +import org.matrix.android.sdk.api.util.Optional interface MxCallDetail { val callId: String val isOutgoing: Boolean val roomId: String - val otherUserId: String + val opponentUserId: String val isVideoCall: Boolean } @@ -32,40 +35,64 @@ interface MxCallDetail { */ interface MxCall : MxCallDetail { + companion object { + const val VOIP_PROTO_VERSION = 1 + } + + val ourPartyId: String + var opponentPartyId: Optional<String>? + var opponentVersion: Int + + var capabilities: CallCapabilities? + var state: CallState /** * Pick Up the incoming call * It has no effect on outgoing call */ - fun accept(sdp: SessionDescription) + fun accept(sdpString: String) + + /** + * SDP negotiation for media pause, hold/resume, ICE restarts and voice/video call up/downgrading + */ + fun negotiate(sdpString: String, type: SdpType) + + /** + * This has to be sent by the caller's client once it has chosen an answer. + */ + fun selectAnswer() /** * Reject an incoming call - * It's an alias to hangUp */ - fun reject() = hangUp() + fun reject() /** * End the call */ - fun hangUp() + fun hangUp(reason: CallHangupContent.Reason? = null) /** * Start a call * Send offer SDP to the other participant. */ - fun offerSdp(sdp: SessionDescription) + fun offerSdp(sdpString: String) /** - * Send Ice candidate to the other participant. + * Send Call candidate to the other participant. */ - fun sendLocalIceCandidates(candidates: List<IceCandidate>) + fun sendLocalCallCandidates(candidates: List<CallCandidate>) /** * Send removed ICE candidates to the other participant. */ - fun sendLocalIceCandidateRemovals(candidates: List<IceCandidate>) + fun sendLocalIceCandidateRemovals(candidates: List<CallCandidate>) + + /** + * Send a m.call.replaces event to initiate call transfer. + */ + suspend fun transfer(targetUserId: String, targetRoomId: String?) fun addListener(listener: StateListener) fun removeListener(listener: StateListener) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxPeerConnectionState.java b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxPeerConnectionState.java new file mode 100644 index 0000000000000000000000000000000000000000..7ea0765809cd10ff6a83c3f1fbbaebb40a20cad4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/MxPeerConnectionState.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 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.api.session.call; + +/** + * This is a copy of https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState + * to avoid having the dependency over WebRtc library on sdk. + */ +public enum MxPeerConnectionState { + NEW, + CONNECTING, + CONNECTED, + DISCONNECTED, + FAILED, + CLOSED +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt new file mode 100644 index 0000000000000000000000000000000000000000..6627f62e24afff5b0685273986888cc2b082bba2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 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.api.session.call + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.session.thirdparty.GetThirdPartyProtocolsTask +import org.matrix.android.sdk.internal.task.TaskExecutor +import timber.log.Timber +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject + +private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn" +private const val PSTN_MATRIX_KEY = "m.protocol.pstn" + +/** + * This class is responsible for checking if the HS support the PSTN protocol. + * As long as the request succeed, it'll check only once by session. + */ +@SessionScope +class PSTNProtocolChecker @Inject internal constructor(private val taskExecutor: TaskExecutor, + private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask) { + + interface Listener { + fun onPSTNSupportUpdated() + } + + private var alreadyChecked = AtomicBoolean(false) + + private val pstnSupportListeners = mutableListOf<Listener>() + + fun addListener(listener: Listener) { + pstnSupportListeners.add(listener) + } + + fun removeListener(listener: Listener) { + pstnSupportListeners.remove(listener) + } + + var supportedPSTNProtocol: String? = null + private set + + fun checkForPSTNSupportIfNeeded() { + if (alreadyChecked.get()) return + taskExecutor.executorScope.checkForPSTNSupport() + } + + private fun CoroutineScope.checkForPSTNSupport() = launch { + try { + supportedPSTNProtocol = getSupportedPSTN(3) + alreadyChecked.set(true) + if (supportedPSTNProtocol != null) { + pstnSupportListeners.forEach { + tryOrNull { it.onPSTNSupportUpdated() } + } + } + } catch (failure: Throwable) { + Timber.v("Fail to get supported PSTN, will check again next time.") + } + } + + private suspend fun getSupportedPSTN(maxTries: Int): String? { + val thirdPartyProtocols: Map<String, ThirdPartyProtocol> = try { + getThirdPartyProtocolsTask.execute(Unit) + } catch (failure: Throwable) { + if (maxTries == 1) { + throw failure + } else { + // Wait for 10s before trying again + delay(10_000L) + return getSupportedPSTN(maxTries - 1) + } + } + return when { + thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY + thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY + else -> null + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index fa5ea359e8ff9ff7849bb91f5015fbdca97c10f1..1b7a5243e2ba00d191f21f6849f399ce164d9ef0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -56,8 +56,6 @@ interface CryptoService { fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback<Unit>) - fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) - fun getCryptoVersion(context: Context, longFormat: Boolean): String fun isCryptoEnabled(): Boolean @@ -158,4 +156,10 @@ interface CryptoService { fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? fun logDbUsageInfo() + + /** + * Perform any background tasks that can be done before a message is ready to + * send, in order to speed up sending of the message. + */ + fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 8f9f409b745f85264937dc9903fa0e944dff9f46..844e8dbbab575abfde48aa878390234f65b1087d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -66,7 +66,7 @@ inline fun <reified T> T.toContent(): Content { */ @JsonClass(generateAdapter = true) data class Event( - @Json(name = "type") val type: String, + @Json(name = "type") val type: String? = null, @Json(name = "event_id") val eventId: String? = null, @Json(name = "content") val content: Content? = null, @Json(name = "prev_content") val prevContent: Content? = null, @@ -98,6 +98,19 @@ data class Event( @Transient var ageLocalTs: Long? = null + /** + * Copy all fields, including transient fields + */ + fun copyAll(): Event { + return copy().also { + it.mxDecryptionResult = mxDecryptionResult + it.mCryptoError = mCryptoError + it.mCryptoErrorReason = mCryptoErrorReason + it.sendState = sendState + it.ageLocalTs = ageLocalTs + } + } + /** * Check if event is a state event. * @return true if event is state event. @@ -135,7 +148,7 @@ data class Event( * @return the event type */ fun getClearType(): String { - return mxDecryptionResult?.payload?.get("type")?.toString() ?: type + return mxDecryptionResult?.payload?.get("type")?.toString() ?: type ?: EventType.MISSING_TYPE } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 68874a1fb1ae975c712096de8c273b2c5bb80691..905e18b8e8b95cef884fca97ba468b14454e900f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -20,6 +20,8 @@ package org.matrix.android.sdk.api.session.events.model * Constants defining known event types from Matrix specifications. */ object EventType { + // Used when the type is missing, which should not happen + const val MISSING_TYPE = "org.matrix.android.sdk.missing_type" const val PRESENCE = "m.presence" const val MESSAGE = "m.room.message" @@ -68,7 +70,12 @@ object EventType { const val CALL_INVITE = "m.call.invite" const val CALL_CANDIDATES = "m.call.candidates" const val CALL_ANSWER = "m.call.answer" + const val CALL_SELECT_ANSWER = "m.call.select_answer" + const val CALL_NEGOTIATE = "m.call.negotiate" + const val CALL_REJECT = "m.call.reject" const val CALL_HANGUP = "m.call.hangup" + // This type is not processed by the client, just sent to the server + const val CALL_REPLACES = "m.call.replaces" // Key share events const val ROOM_KEY_REQUEST = "m.room_key_request" @@ -98,5 +105,9 @@ object EventType { || type == CALL_CANDIDATES || type == CALL_ANSWER || type == CALL_HANGUP + || type == CALL_SELECT_ANSWER + || type == CALL_NEGOTIATE + || type == CALL_REJECT + || type == CALL_REPLACES } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt index 2c9121ce4ad62847985f2c7f88ed54cae10a2a66..f12cbcd6db12b79a7a8dabf6436447bc5317869e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt @@ -21,6 +21,11 @@ package org.matrix.android.sdk.api.session.homeserver */ interface HomeServerCapabilitiesService { + /** + * Force a refresh of the stored data + */ + suspend fun refreshHomeServerCapabilities() + /** * Get the HomeServer capabilities */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitSyncStep.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitSyncStep.kt new file mode 100644 index 0000000000000000000000000000000000000000..901c1b2ffbc756fee00d912d7c2eb51e3523b840 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitSyncStep.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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.api.session.initsync + +enum class InitSyncStep { + ServerComputing, + Downloading, + ImportingAccount, + ImportingAccountCrypto, + ImportingAccountRoom, + ImportingAccountGroups, + ImportingAccountData, + ImportingAccountJoinedRooms, + ImportingAccountInvitedRooms, + ImportingAccountLeftRooms +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/InitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt similarity index 83% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/InitialSyncProgressService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt index 09c6b8e94cc06e189532e0efcafacf34bb96253a..b5d4ef4dbb4a55abe9e713b00ac156111b0d2415 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/InitialSyncProgressService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/InitialSyncProgressService.kt @@ -5,7 +5,7 @@ * 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 + * 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, @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.api.session +package org.matrix.android.sdk.api.session.initsync -import androidx.annotation.StringRes import androidx.lifecycle.LiveData interface InitialSyncProgressService { @@ -25,7 +24,7 @@ interface InitialSyncProgressService { sealed class Status { object Idle : Status() data class Progressing( - @StringRes val statusText: Int, + val initSyncStep: InitSyncStep, val percentProgress: Int = 0 ) : Status() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index 61970ce848cb2c424cedc83182897b562a5ca5e9..176de8e408be3108d8307c5736a8e0c49bdacf4f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -16,12 +16,9 @@ package org.matrix.android.sdk.api.session.room -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse -import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol -import org.matrix.android.sdk.api.util.Cancelable /** * This interface defines methods to get and join public rooms. It's implemented at the session level. @@ -31,15 +28,8 @@ interface RoomDirectoryService { /** * Get rooms from directory */ - fun getPublicRooms(server: String?, - publicRoomsParams: PublicRoomsParams, - callback: MatrixCallback<PublicRoomsResponse>): Cancelable - - /** - * Fetches the overall metadata about protocols supported by the homeserver. - * Includes both the available protocols and all fields required for queries against each protocol. - */ - fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable + suspend fun getPublicRooms(server: String?, + publicRoomsParams: PublicRoomsParams): PublicRoomsResponse /** * Get the visibility of a room in the directory diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt index 1251fd985732308b13c86872c7b549660d4d5868..6581247b90a69dcedda5984209486747e589a3fc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt @@ -30,4 +30,10 @@ interface RoomCryptoService { * Enable encryption of the room */ suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM) + + /** + * Ensures all members of the room are loaded and outbound session keys are shared. + * If this method is not called, CryptoService will ensure it before sending events. + */ + suspend fun prepareToEncrypt() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/EditAggregatedSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/EditAggregatedSummary.kt index 10fb81dc7f55ac8af1bf11ab0fe8069a59b5d707..67bab626cbcabed9130097f5b1203bfb4899b964 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/EditAggregatedSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/EditAggregatedSummary.kt @@ -18,7 +18,7 @@ package org.matrix.android.sdk.api.session.room.model import org.matrix.android.sdk.api.session.events.model.Content data class EditAggregatedSummary( - val aggregatedContent: Content? = null, + val latestContent: Content? = null, // The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk) val sourceEvents: List<String>, val localEchos: List<String>, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReferencesAggregatedContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReferencesAggregatedContent.kt index 0947c96bb0fa52a6d13d22786d0e8cbbd452d5ed..664d042e186f482774af3109af410fa078481076 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReferencesAggregatedContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReferencesAggregatedContent.kt @@ -17,7 +17,7 @@ package org.matrix.android.sdk.api.session.room.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.session.room.VerificationState +import org.matrix.android.sdk.api.crypto.VerificationState /** * Contains an aggregated summary info of the references. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt index 776acbd8eaa3640d6a263e47e79ce67091781682..56503e3e35624a1b1008c2ab88d9c32c56409085 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomThirdPartyInviteContent.kt @@ -30,24 +30,24 @@ data class RoomThirdPartyInviteContent( * This should not contain the user's third party ID, as otherwise when the invite * is accepted it would leak the association between the matrix ID and the third party ID. */ - @Json(name = "display_name") val displayName: String, + @Json(name = "display_name") val displayName: String?, /** * Required. A URL which can be fetched, with querystring public_key=public_key, to validate * whether the key has been revoked. The URL must return a JSON object containing a boolean property named 'valid'. */ - @Json(name = "key_validity_url") val keyValidityUrl: String, + @Json(name = "key_validity_url") val keyValidityUrl: String?, /** * Required. A base64-encoded ed25519 key with which token must be signed (though a signature from any entry in * public_keys is also sufficient). This exists for backwards compatibility. */ - @Json(name = "public_key") val publicKey: String, + @Json(name = "public_key") val publicKey: String?, /** * Keys with which the token may be signed. */ - @Json(name = "public_keys") val publicKeys: List<PublicKeys> = emptyList() + @Json(name = "public_keys") val publicKeys: List<PublicKeys>? = emptyList() ) @JsonClass(generateAdapter = true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt index c4d1f6486f2e19a9a33cbe380b223a5b91f5cae2..45a54bb379b58187445595641724fece2263a823 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt @@ -27,16 +27,24 @@ data class CallAnswerContent( /** * Required. The ID of the call this event relates to. */ - @Json(name = "call_id") val callId: String, + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, /** * Required. The session description object */ @Json(name = "answer") val answer: Answer, /** - * Required. The version of the VoIP specification this messages adheres to. This specification is version 0. + * Required. The version of the VoIP specification this messages adheres to. + */ + @Json(name = "version") override val version: String?, + /** + * Capability advertisement. */ - @Json(name = "version") val version: Int = 0 -) { + @Json(name = "capabilities") val capabilities: CallCapabilities? = null +): CallSignallingContent { @JsonClass(generateAdapter = true) data class Answer( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidate.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidate.kt new file mode 100644 index 0000000000000000000000000000000000000000..ace8c5a7570f0ecbc1d9338899255fd3fde241bf --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidate.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class CallCandidate( + /** + * Required. The SDP media type this candidate is intended for. + */ + @Json(name = "sdpMid") val sdpMid: String? = null, + /** + * Required. The index of the SDP 'm' line this candidate is intended for. + */ + @Json(name = "sdpMLineIndex") val sdpMLineIndex: Int = 0, + /** + * Required. The SDP 'a' line of the candidate. + */ + @Json(name = "candidate") val candidate: String? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt index cad2356c2db56da9d3c35b20f670ce321428af36..7bfe7a97acbdd6260c8d3eb183d83ae9599863b5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt @@ -28,30 +28,17 @@ data class CallCandidatesContent( /** * Required. The ID of the call this event relates to. */ - @Json(name = "call_id") val callId: String, + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, /** * Required. Array of objects describing the candidates. */ - @Json(name = "candidates") val candidates: List<Candidate> = emptyList(), + @Json(name = "candidates") val candidates: List<CallCandidate> = emptyList(), /** - * Required. The version of the VoIP specification this messages adheres to. This specification is version 0. + * Required. The version of the VoIP specification this messages adheres to. */ - @Json(name = "version") val version: Int = 0 -) { - - @JsonClass(generateAdapter = true) - data class Candidate( - /** - * Required. The SDP media type this candidate is intended for. - */ - @Json(name = "sdpMid") val sdpMid: String, - /** - * Required. The index of the SDP 'm' line this candidate is intended for. - */ - @Json(name = "sdpMLineIndex") val sdpMLineIndex: Int, - /** - * Required. The SDP 'a' line of the candidate. - */ - @Json(name = "candidate") val candidate: String - ) -} + @Json(name = "version") override val version: String? +): CallSignallingContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCapabilities.kt new file mode 100644 index 0000000000000000000000000000000000000000..d911ca3b88927bd8b5f6608019d83d1af0cedff7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCapabilities.kt @@ -0,0 +1,32 @@ +/* + * 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.extensions.orFalse + +@JsonClass(generateAdapter = true) +data class CallCapabilities( + /** + * If set to true, states that the sender of the event supports the m.call.replaces event and therefore supports + * being transferred to another destination + */ + @Json(name = "m.call.transferee") val transferee: Boolean? = null +) + +fun CallCapabilities?.supportCallTransfer() = this?.transferee.orFalse() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallHangupContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallHangupContent.kt index d30441df4b6567f363d0f30c103572d79b8527e7..0acc409053d714d561e6077ee9f6820c4500560f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallHangupContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallHangupContent.kt @@ -28,24 +28,41 @@ data class CallHangupContent( /** * Required. The ID of the call this event relates to. */ - @Json(name = "call_id") val callId: String, + @Json(name = "call_id") override val callId: String, /** - * Required. The version of the VoIP specification this message adheres to. This specification is version 0. + * Required. ID to let user identify remote echo of their own events */ - @Json(name = "version") val version: Int = 0, + @Json(name = "party_id") override val partyId: String? = null, + /** + * Required. The version of the VoIP specification this message adheres to. + */ + @Json(name = "version") override val version: String?, /** * Optional error reason for the hangup. This should not be provided when the user naturally ends or rejects the call. * When there was an error in the call negotiation, this should be `ice_failed` for when ICE negotiation fails - * or `invite_timeout` for when the other party did not answer in time. One of: ["ice_failed", "invite_timeout"] + * or `invite_timeout` for when the other party did not answer in time. + * One of: ["ice_failed", "invite_timeout"] */ @Json(name = "reason") val reason: Reason? = null -) { +) : CallSignallingContent { @JsonClass(generateAdapter = false) enum class Reason { @Json(name = "ice_failed") ICE_FAILED, + @Json(name = "ice_timeout") + ICE_TIMEOUT, + + @Json(name = "user_hangup") + USER_HANGUP, + + @Json(name = "user_media_failed") + USER_MEDIA_FAILED, + @Json(name = "invite_timeout") - INVITE_TIMEOUT + INVITE_TIMEOUT, + + @Json(name = "unknown_error") + UNKWOWN_ERROR } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt index b961a6f654b478ea0d8f76a673cb3a10323c19e0..42489bc0ceb199000cf65471444f35ac4c81b111 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt @@ -27,22 +27,35 @@ data class CallInviteContent( /** * Required. A unique identifier for the call. */ - @Json(name = "call_id") val callId: String?, + @Json(name = "call_id") override val callId: String?, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, /** * Required. The session description object */ @Json(name = "offer") val offer: Offer?, /** - * Required. The version of the VoIP specification this message adheres to. This specification is version 0. + * Required. The version of the VoIP specification this message adheres to. */ - @Json(name = "version") val version: Int? = 0, + @Json(name = "version") override val version: String?, /** * Required. The time in milliseconds that the invite is valid for. * Once the invite age exceeds this value, clients should discard it. * They should also no longer show the call as awaiting an answer in the UI. */ - @Json(name = "lifetime") val lifetime: Int? -) { + @Json(name = "lifetime") val lifetime: Int?, + /** + * The field should be added for all invites where the target is a specific user + */ + @Json(name = "invitee") val invitee: String? = null, + /** + * Capability advertisement. + */ + @Json(name = "capabilities") val capabilities: CallCapabilities? = null + +): CallSignallingContent { @JsonClass(generateAdapter = true) data class Offer( /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt new file mode 100644 index 0000000000000000000000000000000000000000..040993bb51dda98cba50c5730173e9d43ba6d68a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt @@ -0,0 +1,62 @@ +/* + * 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This introduces SDP negotiation semantics for media pause, hold/resume, ICE restarts and voice/video call up/downgrading. + */ +@JsonClass(generateAdapter = true) +data class CallNegotiateContent( + /** + * Required. The ID of the call this event relates to. + */ + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, + /** + * Required. The time in milliseconds that the negotiation is valid for. Once exceeded the sender + * of the negotiate event should consider the negotiation failed (timed out) and the recipient should ignore it. + **/ + @Json(name = "lifetime") val lifetime: Int?, + /** + * Required. The session description object + */ + @Json(name = "description") val description: Description? = null, + + /** + * Required. The version of the VoIP specification this message adheres to. + */ + @Json(name = "version") override val version: String? + + ): CallSignallingContent { + @JsonClass(generateAdapter = true) + data class Description( + /** + * Required. The type of session description. + */ + @Json(name = "type") val type: SdpType?, + /** + * Required. The SDP text of the session description. + */ + @Json(name = "sdp") val sdp: String? + ) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallRejectContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallRejectContent.kt new file mode 100644 index 0000000000000000000000000000000000000000..1da229b1799715ca934bf6f4c7b28dda909061ea --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallRejectContent.kt @@ -0,0 +1,40 @@ +/* + * 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Sent by either party to signal their termination of the call. This can be sent either once + * the call has been established or before to abort the call. + */ +@JsonClass(generateAdapter = true) +data class CallRejectContent( + /** + * Required. The ID of the call this event relates to. + */ + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, + /** + * Required. The version of the VoIP specification this message adheres to. + */ + @Json(name = "version") override val version: String? +) : CallSignallingContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt new file mode 100644 index 0000000000000000000000000000000000000000..97a3b8c7a72e576c5956852f1f2870e834275667 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt @@ -0,0 +1,82 @@ +/* + * 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This event is sent to signal the intent of a participant in a call to replace the call with another, + * such that the other participant ends up in a call with a new user. + */ +@JsonClass(generateAdapter = true) +data class CallReplacesContent( + /** + * Required. The ID of the call this event relates to. + */ + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, + /** + * An identifier for the call replacement itself, generated by the transferor. + */ + @Json(name = "replacement_id") val replacementId: String? = null, + /** + * Optional. If specified, the transferee client waits for an invite to this room and joins it + * (possibly waiting for user confirmation) and then continues the transfer in this room. + * If absent, the transferee contacts the Matrix User ID given in the target_user field in a room of its choosing. + */ + @Json(name = "target_room") val targerRoomId: String? = null, + /** + * An object giving information about the transfer target + */ + @Json(name = "target_user") val targetUser: TargetUser? = null, + /** + * If specified, gives the call ID for the transferee's client to use when placing the replacement call. + * Mutually exclusive with await_call + */ + @Json(name = "create_call") val createCall: String? = null, + /** + * If specified, gives the call ID that the transferee's client should wait for. + * Mutually exclusive with create_call. + */ + @Json(name = "await_call") val awaitCall: String? = null, + /** + * Required. The version of the VoIP specification this messages adheres to. + */ + @Json(name = "version") override val version: String? +): CallSignallingContent { + + @JsonClass(generateAdapter = true) + data class TargetUser( + /** + * Required. The matrix user ID of the transfer target + */ + @Json(name = "id") val id: String, + /** + * Optional. The display name of the transfer target. + */ + @Json(name = "display_name") val displayName: String?, + /** + * Optional. The avatar URL of the transfer target. + */ + @Json(name = "avatar_url") val avatarUrl: String? + + ) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt new file mode 100644 index 0000000000000000000000000000000000000000..6ea70ac990589b46bd456e3d37ed0a24a67df773 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt @@ -0,0 +1,44 @@ +/* + * 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.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This event is sent by the callee when they wish to answer the call. + */ +@JsonClass(generateAdapter = true) +data class CallSelectAnswerContent( + /** + * Required. The ID of the call this event relates to. + */ + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, + /** + * Required. Indicates the answer user has chosen. + */ + @Json(name = "selected_party_id") val selectedPartyId: String? = null, + + /** + * Required. The version of the VoIP specification this message adheres to. + */ + @Json(name = "version") override val version: String? +): CallSignallingContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSignallingContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSignallingContent.kt new file mode 100644 index 0000000000000000000000000000000000000000..f8d8c2a5e82d777f55cb33ca982c44eb4a2a0cab --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSignallingContent.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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.api.session.room.model.call + +interface CallSignallingContent { + /** + * Required. A unique identifier for the call. + */ + val callId: String? + + /** + * Required. ID to let user identify remote echo of their own events + */ + val partyId: String? + + /** + * Required. The version of the VoIP specification this message adheres to. This specification is version 0. + */ + val version: String? +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/SdpType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/SdpType.kt index ff393135ea85e2b3f8d700bfcba000e8a6eb9092..9b55ab80c754bbc9cd64e09df9f16415491ceff7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/SdpType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/SdpType.kt @@ -25,5 +25,5 @@ enum class SdpType { OFFER, @Json(name = "answer") - ANSWER + ANSWER; } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt index 49aa95924c8cb58bc681ae4b9995baf11f7622d2..59d84ef40fd82c6f458baba55fe856904b502ac1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.session.room.model.relation import androidx.lifecycle.LiveData -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -67,11 +66,11 @@ interface RelationService { /** * Edit a text message body. Limited to "m.text" contentType - * @param targetEventId The event to edit + * @param targetEvent The event to edit * @param newBodyText The edited body * @param compatibilityBodyText The text that will appear on clients that don't support yet edition */ - fun editTextMessage(targetEventId: String, + fun editTextMessage(targetEvent: TimelineEvent, msgType: String, newBodyText: CharSequence, newBodyAutoMarkdown: Boolean, @@ -92,8 +91,11 @@ interface RelationService { /** * Get the edit history of the given event + * The return list will contain the original event and all the editions of this event, done by the + * same sender, sorted in the reverse order (so the original event is the latest element, and the + * latest edition is the first element of the list) */ - fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) + suspend fun fetchEditHistory(eventId: String): List<Event> /** * Reply to an event in the timeline (must be in same room) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt index ecfddad0a4727b21bfcadc5f61826013117ca10e..5fe9bf6993a6c0afde6470782e8c0a3aca69269c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt @@ -17,14 +17,11 @@ package org.matrix.android.sdk.api.session.room.powerlevels -import androidx.annotation.StringRes -import org.matrix.android.sdk.R - -sealed class Role(open val value: Int, @StringRes val res: Int) : Comparable<Role> { - object Admin : Role(100, R.string.power_level_admin) - object Moderator : Role(50, R.string.power_level_moderator) - object Default : Role(0, R.string.power_level_default) - data class Custom(override val value: Int) : Role(value, R.string.power_level_custom) +sealed class Role(open val value: Int) : Comparable<Role> { + object Admin : Role(100) + object Moderator : Role(50) + object Default : Role(0) + data class Custom(override val value: Int) : Role(value) override fun compareTo(other: Role): Int { return value.compareTo(other.value) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 152a018e78a56c9394d2fafab4784a46b0b45b42..6ae42de90cdd7573cf240f4b134e4d7ec2bc25d0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -132,4 +132,9 @@ interface SendService { * Resend all failed messages one by one (and keep order) */ fun resendAllFailedMessages() + + /** + * Cancel all failed messages + */ + fun cancelAllFailedMessages() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt index 1b4368e3da5ee0b9f0a307e6a5cef686f285d26e..9b73136fc3792237b77013c38a776ccedc349f49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.api.session.room.sender +import org.matrix.android.sdk.internal.util.replaceSpaceChars + data class SenderInfo( val userId: String, /** @@ -27,8 +29,9 @@ data class SenderInfo( ) { val disambiguatedDisplayName: String get() = when { - displayName.isNullOrBlank() -> userId - isUniqueDisplayName -> displayName - else -> "$displayName ($userId)" + displayName == null -> userId + displayName.replaceSpaceChars().isBlank() -> "$displayName ($userId)" + isUniqueDisplayName -> displayName + else -> "$displayName ($userId)" } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 444366e9128fa876a9aa147fe993fbfc73fa387e..e614ea91d649b8cd712926d78c0a1a6176bdb2a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -65,13 +65,30 @@ interface StateService { */ suspend fun deleteAvatar() + /** + * Send a state event to the room + */ suspend fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict) + /** + * Get a state event of the room + */ fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? + /** + * Get a live state event of the room + */ fun getStateEventLive(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData<Optional<Event>> + /** + * Get state events of the room + * @param eventTypes Set of eventType. If empty, all state events will be returned + */ fun getStateEvents(eventTypes: Set<String>, stateKey: QueryStringValue = QueryStringValue.NoCondition): List<Event> + /** + * Get live state events of the room + * @param eventTypes Set of eventType to observe. If empty, all state events will be observed + */ fun getStateEventsLive(eventTypes: Set<String>, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData<List<Event>> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt index dcaf5f32768df57b7d66ae37798617b8ebd6703f..ef6300eae2ac011939a0b384a8e319711aeeb87f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt @@ -20,11 +20,15 @@ import org.matrix.android.sdk.api.session.events.model.EventType object RoomSummaryConstants { + /** + * + */ val PREVIEWABLE_TYPES = listOf( // TODO filter message type (KEY_VERIFICATION_READY, etc.) EventType.MESSAGE, EventType.CALL_INVITE, EventType.CALL_HANGUP, + EventType.CALL_REJECT, EventType.CALL_ANSWER, EventType.ENCRYPTED, EventType.STICKER, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt index b10fb540e112e44c8fb5917251661cb10d9e2dcd..32f6b94cd8d7a6c903df4e00739e942a2fc1c31e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt @@ -38,6 +38,9 @@ import org.matrix.android.sdk.api.util.ContentUtils.extractUsefulTextFromReply */ data class TimelineEvent( val root: Event, + /** + * Uniquely identify an event, computed locally by the sdk + */ val localId: Long, val eventId: String, val displayIndex: Int, @@ -52,6 +55,8 @@ data class TimelineEvent( } } + val roomId = root.roomId ?: "" + val metadata = HashMap<String, Any>() /** @@ -121,8 +126,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? { return if (root.getClearType() == EventType.STICKER) { root.getClearContent().toModel<MessageStickerContent>() } else { - annotations?.editSummary?.aggregatedContent?.toModel() - ?: root.getClearContent().toModel() + (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt index 2876ec69e56c817d4ba2133f47669ee1fe60487e..3c021384e13a6773465939996ce146aa414b9aa9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt @@ -36,9 +36,23 @@ interface TimelineService { */ fun createTimeline(eventId: String?, settings: TimelineSettings): Timeline + /** + * Returns a snapshot of TimelineEvent event with eventId. + * At the opposite of getTimeLineEventLive which will be updated when local echo event is synced, it will return null in this case. + * @param eventId the eventId to get the TimelineEvent + */ fun getTimeLineEvent(eventId: String): TimelineEvent? + /** + * Creates a LiveData of Optional TimelineEvent event with eventId. + * If the eventId is a local echo eventId, it will make the LiveData be updated with the synced TimelineEvent when coming through the sync. + * In this case, makes sure to use the new synced eventId from the TimelineEvent class if you want to interact, as the local echo is removed from the SDK. + * @param eventId the eventId to listen for TimelineEvent + */ fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> + /** + * Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO. + */ fun getAttachmentMessages(): List<TimelineEvent> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/FilterService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/FilterService.kt index 70de026857618a4e496f5dd235aef4464cd09a7e..6d1d12f1bc68857d84cfea3951a4449e3f4982ee 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/FilterService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/FilterService.kt @@ -22,9 +22,9 @@ interface FilterService { NoFilter, /** - * Filter for Riot, will include only known event type + * Filter for Element, will include only known event type */ - RiotFilter + ElementFilter } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt new file mode 100644 index 0000000000000000000000000000000000000000..2ae4562b0b50a47eb36679d4193fd0f036d010c9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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.api.session.thirdparty + +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser + +/** + * See https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-thirdparty-protocols + */ +interface ThirdPartyService { + + /** + * Fetches the overall metadata about protocols supported by the homeserver. + * Includes both the available protocols and all fields required for queries against each protocol. + */ + suspend fun getThirdPartyProtocols(): Map<String, ThirdPartyProtocol> + + /** + * Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. + * @param protocol Required. The name of the protocol. + * @param fields One or more custom fields that are passed to the AS to help identify the user. + */ + suspend fun getThirdPartyUser(protocol: String, fields: Map<String, String> = emptyMap()): List<ThirdPartyUser> +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt new file mode 100644 index 0000000000000000000000000000000000000000..d77dfcfe358e5ea79c1115ade71b0f42203750cf --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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.api.session.thirdparty.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class ThirdPartyUser( + /* + Required. A Matrix User ID represting a third party user. + */ + @Json(name = "userid") val userId: String, + /* + Required. The protocol ID that the third party location is a part of. + */ + @Json(name = "protocol") val protocol: String, + /* + Required. Information used to identify this third party location. + */ + @Json(name = "fields") val fields: JsonDict +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt index 037cd22b8758005cb988b92111881d98ff75c29f..bf3ff8959dc21d5e99fe39d9c21ea08d8a9557ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt @@ -56,6 +56,11 @@ interface WidgetService { excludedTypes: Set<String>? = null ): List<Widget> + /** + * Return the computed URL of a widget + */ + fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? + /** * Returns the live room widgets so you can listen to them. * Some widgets can be deactivated, so be sure to check for isActive. @@ -97,10 +102,11 @@ interface WidgetService { ): LiveData<List<Widget>> /** - * Creates a new widget in a room. It makes sure you have the rights to handle this. + * Creates and send a new widget in a room. It makes sure you have the rights to handle this. * - * @param roomId: the room where you want to deactivate the widget. - * @param widgetId: the widget to deactivate. + * @param roomId the room where you want to create the widget. + * @param widgetId the widget to create. + * @param content the content of the widget * @param callback the matrix callback to listen for result. * @return Cancelable */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/model/Widget.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/model/Widget.kt index c8465d4d2e6dff51685e1c7e0f3da4d3031c45e8..86aaba7f6fcb5f87186bcb3202b5479a6479125c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/model/Widget.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/model/Widget.kt @@ -25,7 +25,6 @@ data class Widget( val widgetId: String, val senderInfo: SenderInfo?, val isAddedByMe: Boolean, - val computedUrl: String?, val type: WidgetType ) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt index c82a929ee0bc2c9af9a7b196c101239c2353bfc6..1a00b85ff4e3d04934ae448f65ddeb3e849f7895 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt @@ -20,7 +20,7 @@ object ContentUtils { val lines = repliedBody.lines() var wellFormed = repliedBody.startsWith(">") var endOfPreviousFound = false - val usefullines = ArrayList<String>() + val usefulLines = ArrayList<String>() lines.forEach { if (it == "") { endOfPreviousFound = true @@ -29,10 +29,10 @@ object ContentUtils { if (!endOfPreviousFound) { wellFormed = wellFormed && it.startsWith(">") } else { - usefullines.add(it) + usefulLines.add(it) } } - return usefullines.joinToString("\n").takeIf { wellFormed } ?: repliedBody + return usefulLines.joinToString("\n").takeIf { wellFormed } ?: repliedBody } fun extractUsefulTextFromHtmlReply(repliedBody: String): String { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt index 17f27b251491c227c80df9d2f398fc09a384f9ac..d40de1a0df282c4c6b531dec0c3df980997c73f4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt @@ -35,3 +35,10 @@ fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder return this } + +fun StringBuilder.appendParamsToUrl(params: Map<String, String>): StringBuilder { + params.forEach { (param, value) -> + appendParamToUrl(param, value) + } + return this +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt index 2ec8900f7c08295800856e9e2fa193ed8a92fe76..bb62dbbfe92cfe8771d7ac173a24d3366077a913 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt @@ -20,7 +20,9 @@ import android.content.Context import dagger.Binds import dagger.Module import dagger.Provides +import io.realm.RealmConfiguration import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.internal.auth.db.AuthRealmMigration import org.matrix.android.sdk.internal.auth.db.AuthRealmModule @@ -32,8 +34,6 @@ import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthDatabase import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter import org.matrix.android.sdk.internal.wellknown.WellknownModule -import io.realm.RealmConfiguration -import org.matrix.android.sdk.api.auth.HomeServerHistoryService import java.io.File @Module(includes = [WellknownModule::class]) @@ -82,6 +82,9 @@ internal abstract class AuthModule { @Binds abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask + @Binds + abstract fun bindIsValidClientServerApiTask(task: DefaultIsValidClientServerApiTask): IsValidClientServerApiTask + @Binds abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index c99e9bd81c88b1c314a1c5fa3abeae4b7cf233c0..4f3451cf307dc9ce85c1b03a29ae3055665c28a7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -18,10 +18,7 @@ package org.matrix.android.sdk.internal.auth import android.net.Uri import dagger.Lazy -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import okhttp3.OkHttpClient -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig @@ -32,8 +29,6 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.wellknown.WellknownResult import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.api.util.appendParamToUrl import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse @@ -50,11 +45,6 @@ import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith -import org.matrix.android.sdk.internal.task.launchToCallback -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import org.matrix.android.sdk.internal.util.toCancelable import org.matrix.android.sdk.internal.wellknown.GetWellknownTask import javax.inject.Inject import javax.net.ssl.HttpsURLConnection @@ -63,14 +53,12 @@ internal class DefaultAuthenticationService @Inject constructor( @Unauthenticated private val okHttpClient: Lazy<OkHttpClient>, private val retrofitFactory: RetrofitFactory, - private val coroutineDispatchers: MatrixCoroutineDispatchers, private val sessionParamsStore: SessionParamsStore, private val sessionManager: SessionManager, private val sessionCreator: SessionCreator, private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, - private val directLoginTask: DirectLoginTask, - private val taskExecutor: TaskExecutor + private val directLoginTask: DirectLoginTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -89,15 +77,11 @@ internal class DefaultAuthenticationService @Inject constructor( } } - override fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable { + override suspend fun getLoginFlowOfSession(sessionId: String): LoginFlowResult { val homeServerConnectionConfig = sessionParamsStore.get(sessionId)?.homeServerConnectionConfig + ?: throw IllegalStateException("Session not found") - return if (homeServerConnectionConfig == null) { - callback.onFailure(IllegalStateException("Session not found")) - NoOpCancellable - } else { - getLoginFlow(homeServerConnectionConfig, callback) - } + return getLoginFlow(homeServerConnectionConfig) } override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? { @@ -146,70 +130,70 @@ internal class DefaultAuthenticationService @Inject constructor( ?.trim { it == '/' } } - override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable { + /** + * This is the entry point of the authentication service. + * homeServerConnectionConfig contains a homeserver URL probably entered by the user, which can be a + * valid homeserver API url, the url of Element Web, or anything else. + */ + override suspend fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { pendingSessionData = null - return taskExecutor.executorScope.launch(coroutineDispatchers.main) { - pendingSessionStore.delete() + pendingSessionStore.delete() - val result = runCatching { - getLoginFlowInternal(homeServerConnectionConfig) - } - result.fold( - { - if (it is LoginFlowResult.Success) { - // The homeserver exists and up to date, keep the config - // Homeserver url may have been changed, if it was a Riot url - val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy( - homeServerUri = Uri.parse(it.homeServerUrl) - ) - - pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig) - .also { data -> pendingSessionStore.savePendingSessionData(data) } - } - callback.onSuccess(it) - }, - { - if (it is UnrecognizedCertificateException) { - callback.onFailure(Failure.UnrecognizedCertificateFailure(homeServerConnectionConfig.homeServerUri.toString(), it.fingerprint)) - } else { - callback.onFailure(it) - } - } - ) + val result = runCatching { + getLoginFlowInternal(homeServerConnectionConfig) } - .toCancelable() + return result.fold( + { + if (it is LoginFlowResult.Success) { + // The homeserver exists and up to date, keep the config + // Homeserver url may have been changed, if it was a Riot url + val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy( + homeServerUri = Uri.parse(it.homeServerUrl) + ) + + pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig) + .also { data -> pendingSessionStore.savePendingSessionData(data) } + } + it + }, + { + if (it is UnrecognizedCertificateException) { + throw Failure.UnrecognizedCertificateFailure(homeServerConnectionConfig.homeServerUri.toString(), it.fingerprint) + } else { + throw it + } + } + ) } private suspend fun getLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { - return withContext(coroutineDispatchers.io) { - val authAPI = buildAuthAPI(homeServerConnectionConfig) + val authAPI = buildAuthAPI(homeServerConnectionConfig) - // First check the homeserver version - runCatching { - executeRequest<Versions>(null) { - apiCall = authAPI.versions() - } + // First check the homeserver version + return runCatching { + executeRequest<Versions>(null) { + apiCall = authAPI.versions() } - .map { versions -> - // Ok, it seems that the homeserver url is valid - getLoginFlowResult(authAPI, versions, homeServerConnectionConfig.homeServerUri.toString()) - } - .fold( - { - it - }, - { - if (it is Failure.OtherServerError - && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { - // It's maybe a Riot url? - getRiotDomainLoginFlowInternal(homeServerConnectionConfig) - } else { - throw it - } - } - ) } + .map { versions -> + // Ok, it seems that the homeserver url is valid + getLoginFlowResult(authAPI, versions, homeServerConnectionConfig.homeServerUri.toString()) + } + .fold( + { + it + }, + { + if (it is Failure.OtherServerError + && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + // It's maybe a Riot url? + getRiotDomainLoginFlowInternal(homeServerConnectionConfig) + } else { + throw it + } + } + ) } private suspend fun getRiotDomainLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { @@ -338,12 +322,9 @@ internal class DefaultAuthenticationService @Inject constructor( ?: let { pendingSessionData?.homeServerConnectionConfig?.let { DefaultRegistrationWizard( - buildClient(it), - retrofitFactory, - coroutineDispatchers, + buildAuthAPI(it), sessionCreator, - pendingSessionStore, - taskExecutor.executorScope + pendingSessionStore ).also { currentRegistrationWizard = it } @@ -359,12 +340,9 @@ internal class DefaultAuthenticationService @Inject constructor( ?: let { pendingSessionData?.homeServerConnectionConfig?.let { DefaultLoginWizard( - buildClient(it), - retrofitFactory, - coroutineDispatchers, + buildAuthAPI(it), sessionCreator, - pendingSessionStore, - taskExecutor.executorScope + pendingSessionStore ).also { currentLoginWizard = it } @@ -372,7 +350,7 @@ internal class DefaultAuthenticationService @Inject constructor( } } - override fun cancelPendingLoginOrRegistration() { + override suspend fun cancelPendingLoginOrRegistration() { currentLoginWizard = null currentRegistrationWizard = null @@ -381,61 +359,39 @@ internal class DefaultAuthenticationService @Inject constructor( pendingSessionData = pendingSessionData?.homeServerConnectionConfig ?.let { PendingSessionData(it) } .also { - taskExecutor.executorScope.launch(coroutineDispatchers.main) { - if (it == null) { - // Should not happen - pendingSessionStore.delete() - } else { - pendingSessionStore.savePendingSessionData(it) - } + if (it == null) { + // Should not happen + pendingSessionStore.delete() + } else { + pendingSessionStore.savePendingSessionData(it) } } } - override fun reset() { + override suspend fun reset() { currentLoginWizard = null currentRegistrationWizard = null pendingSessionData = null - taskExecutor.executorScope.launch(coroutineDispatchers.main) { - pendingSessionStore.delete() - } + pendingSessionStore.delete() } - override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, - credentials: Credentials, - callback: MatrixCallback<Session>): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - createSessionFromSso(credentials, homeServerConnectionConfig) - } + override suspend fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, + credentials: Credentials): Session { + return sessionCreator.createSession(credentials, homeServerConnectionConfig) } - override fun getWellKnownData(matrixId: String, - homeServerConnectionConfig: HomeServerConnectionConfig?, - callback: MatrixCallback<WellknownResult>): Cancelable { - return getWellknownTask - .configureWith(GetWellknownTask.Params(matrixId, homeServerConnectionConfig)) { - this.callback = callback - } - .executeBy(taskExecutor) - } - - override fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, - matrixId: String, - password: String, - initialDeviceName: String, - callback: MatrixCallback<Session>): Cancelable { - return directLoginTask - .configureWith(DirectLoginTask.Params(homeServerConnectionConfig, matrixId, password, initialDeviceName)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun getWellKnownData(matrixId: String, + homeServerConnectionConfig: HomeServerConnectionConfig?): WellknownResult { + return getWellknownTask.execute(GetWellknownTask.Params(matrixId, homeServerConnectionConfig)) } - private suspend fun createSessionFromSso(credentials: Credentials, - homeServerConnectionConfig: HomeServerConnectionConfig): Session = withContext(coroutineDispatchers.computation) { - sessionCreator.createSession(credentials, homeServerConnectionConfig) + override suspend fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, + matrixId: String, + password: String, + initialDeviceName: String): Session { + return directLoginTask.execute(DirectLoginTask.Params(homeServerConnectionConfig, matrixId, password, initialDeviceName)) } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt new file mode 100644 index 0000000000000000000000000000000000000000..b8416d69bfef0c1fa87f3c268392f940a0553f77 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 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.auth + +import dagger.Lazy +import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse +import org.matrix.android.sdk.internal.di.Unauthenticated +import org.matrix.android.sdk.internal.network.RetrofitFactory +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject +import javax.net.ssl.HttpsURLConnection + +internal interface IsValidClientServerApiTask : Task<IsValidClientServerApiTask.Params, Boolean> { + data class Params( + val homeServerConnectionConfig: HomeServerConnectionConfig + ) +} + +internal class DefaultIsValidClientServerApiTask @Inject constructor( + @Unauthenticated + private val okHttpClient: Lazy<OkHttpClient>, + private val retrofitFactory: RetrofitFactory +) : IsValidClientServerApiTask { + + override suspend fun execute(params: IsValidClientServerApiTask.Params): Boolean { + val client = buildClient(params.homeServerConnectionConfig) + val homeServerUrl = params.homeServerConnectionConfig.homeServerUri.toString() + + val authAPI = retrofitFactory.create(client, homeServerUrl) + .create(AuthAPI::class.java) + + return try { + executeRequest<LoginFlowResponse>(null) { + apiCall = authAPI.getLoginFlows() + } + // We get a response, so the API is valid + true + } catch (failure: Throwable) { + if (failure is Failure.OtherServerError + && failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + // Probably not valid + false + } else { + // Other error + throw failure + } + } + } + + private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient { + return okHttpClient.get() + .newBuilder() + .addSocketFactory(homeServerConnectionConfig) + .build() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt index 6743e7336e65f8609b0a294a3001cb93f292fd4d..7c4a0c38ec5d3aed2cbdbe36ca2a6ccd899c8049 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt @@ -20,6 +20,7 @@ import android.net.Uri import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.SessionManager import timber.log.Timber @@ -32,7 +33,8 @@ internal interface SessionCreator { internal class DefaultSessionCreator @Inject constructor( private val sessionParamsStore: SessionParamsStore, private val sessionManager: SessionManager, - private val pendingSessionStore: PendingSessionStore + private val pendingSessionStore: PendingSessionStore, + private val isValidClientServerApiTask: IsValidClientServerApiTask ) : SessionCreator { /** @@ -43,16 +45,28 @@ internal class DefaultSessionCreator @Inject constructor( // We can cleanup the pending session params pendingSessionStore.delete() + val overriddenUrl = credentials.discoveryInformation?.homeServer?.baseURL + // remove trailing "/" + ?.trim { it == '/' } + ?.takeIf { it.isNotBlank() } + ?.also { Timber.d("Overriding homeserver url to $it (will check if valid)") } + ?.let { Uri.parse(it) } + ?.takeIf { + // Validate the URL, if the configuration is wrong server side, do not override + tryOrNull { + isValidClientServerApiTask.execute( + IsValidClientServerApiTask.Params( + homeServerConnectionConfig.copy(homeServerUri = it) + ) + ) + .also { Timber.d("Overriding homeserver url: $it") } + } ?: true // In case of other error (no network, etc.), consider it is valid... + } + val sessionParams = SessionParams( credentials = credentials, homeServerConnectionConfig = homeServerConnectionConfig.copy( - homeServerUri = credentials.discoveryInformation?.homeServer?.baseURL - // remove trailing "/" - ?.trim { it == '/' } - ?.takeIf { it.isNotBlank() } - ?.also { Timber.d("Overriding homeserver url to $it") } - ?.let { Uri.parse(it) } - ?: homeServerConnectionConfig.homeServerUri, + homeServerUri = overriddenUrl ?: homeServerConnectionConfig.homeServerUri, identityServerUri = credentials.discoveryInformation?.identityServer?.baseURL // remove trailing "/" ?.trim { it == '/' } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index 108d0d4a420ba05778591f13ab0883447f184cf1..41678758497352821957812b5397c1a35254dbd9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -17,13 +17,10 @@ package org.matrix.android.sdk.internal.auth.login import android.util.Patterns -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.SessionCreator @@ -34,56 +31,19 @@ import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationParams import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationResponse import org.matrix.android.sdk.internal.auth.registration.RegisterAddThreePidTask -import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.task.launchToCallback -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient internal class DefaultLoginWizard( - okHttpClient: OkHttpClient, - retrofitFactory: RetrofitFactory, - private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val authAPI: AuthAPI, private val sessionCreator: SessionCreator, - private val pendingSessionStore: PendingSessionStore, - private val coroutineScope: CoroutineScope + private val pendingSessionStore: PendingSessionStore ) : LoginWizard { private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") - private val authAPI = retrofitFactory.create(okHttpClient, pendingSessionData.homeServerConnectionConfig.homeServerUri.toString()) - .create(AuthAPI::class.java) - - override fun login(login: String, - password: String, - deviceName: String, - callback: MatrixCallback<Session>): Cancelable { - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - loginInternal(login, password, deviceName) - } - } - - /** - * Ref: https://matrix.org/docs/spec/client_server/latest#handling-the-authentication-endpoint - */ - override fun loginWithToken(loginToken: String, callback: MatrixCallback<Session>): Cancelable { - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - val loginParams = TokenLoginParams( - token = loginToken - ) - val credentials = executeRequest<Credentials>(null) { - apiCall = authAPI.login(loginParams) - } - - sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) - } - } - - private suspend fun loginInternal(login: String, - password: String, - deviceName: String) = withContext(coroutineDispatchers.computation) { + override suspend fun login(login: String, + password: String, + deviceName: String): Session { val loginParams = if (Patterns.EMAIL_ADDRESS.matcher(login).matches()) { PasswordLoginParams.thirdPartyIdentifier(ThreePidMedium.EMAIL, login, password, deviceName) } else { @@ -93,16 +53,24 @@ internal class DefaultLoginWizard( apiCall = authAPI.login(loginParams) } - sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) } - override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable { - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - resetPasswordInternal(email, newPassword) + /** + * Ref: https://matrix.org/docs/spec/client_server/latest#handling-the-authentication-endpoint + */ + override suspend fun loginWithToken(loginToken: String): Session { + val loginParams = TokenLoginParams( + token = loginToken + ) + val credentials = executeRequest<Credentials>(null) { + apiCall = authAPI.login(loginParams) } + + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) } - private suspend fun resetPasswordInternal(email: String, newPassword: String) { + override suspend fun resetPassword(email: String, newPassword: String) { val param = RegisterAddThreePidTask.Params( RegisterThreePid.Email(email), pendingSessionData.clientSecret, @@ -120,21 +88,14 @@ internal class DefaultLoginWizard( .also { pendingSessionStore.savePendingSessionData(it) } } - override fun resetPasswordMailConfirmed(callback: MatrixCallback<Unit>): Cancelable { - val safeResetPasswordData = pendingSessionData.resetPasswordData ?: run { - callback.onFailure(IllegalStateException("developer error, no reset password in progress")) - return NoOpCancellable - } - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - resetPasswordMailConfirmedInternal(safeResetPasswordData) - } - } + override suspend fun resetPasswordMailConfirmed() { + val safeResetPasswordData = pendingSessionData.resetPasswordData + ?: throw IllegalStateException("developer error, no reset password in progress") - private suspend fun resetPasswordMailConfirmedInternal(resetPasswordData: ResetPasswordData) { val param = ResetPasswordMailConfirmed.create( pendingSessionData.clientSecret, - resetPasswordData.addThreePidRegistrationResponse.sid, - resetPasswordData.newPassword + safeResetPasswordData.addThreePidRegistrationResponse.sid, + safeResetPasswordData.newPassword ) executeRequest<Unit>(null) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index 163009d9184866f1046a81c3275417a999588cb7..91e414e689771e8f6939eb60d817ada4a82bcb55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -16,10 +16,7 @@ package org.matrix.android.sdk.internal.auth.registration -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import okhttp3.OkHttpClient -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.auth.registration.RegistrationResult @@ -27,31 +24,22 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.registration.toFlowResult import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.SessionCreator import org.matrix.android.sdk.internal.auth.db.PendingSessionData -import org.matrix.android.sdk.internal.network.RetrofitFactory -import org.matrix.android.sdk.internal.task.launchToCallback -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers /** * This class execute the registration request and is responsible to keep the session of interactive authentication */ internal class DefaultRegistrationWizard( - private val okHttpClient: OkHttpClient, - private val retrofitFactory: RetrofitFactory, - private val coroutineDispatchers: MatrixCoroutineDispatchers, + authAPI: AuthAPI, private val sessionCreator: SessionCreator, - private val pendingSessionStore: PendingSessionStore, - private val coroutineScope: CoroutineScope + private val pendingSessionStore: PendingSessionStore ) : RegistrationWizard { private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") - private val authAPI = buildAuthAPI() private val registerTask = DefaultRegisterTask(authAPI) private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI) private val validateCodeTask = DefaultValidateCodeTask(authAPI) @@ -71,70 +59,54 @@ internal class DefaultRegistrationWizard( override val isRegistrationStarted: Boolean get() = pendingSessionData.isRegistrationStarted - override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable { + override suspend fun getRegistrationFlow(): RegistrationResult { val params = RegistrationParams() - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - performRegistrationRequest(params) - } + return performRegistrationRequest(params) } - override fun createAccount(userName: String, - password: String, - initialDeviceDisplayName: String?, - callback: MatrixCallback<RegistrationResult>): Cancelable { + override suspend fun createAccount(userName: String, + password: String, + initialDeviceDisplayName: String?): RegistrationResult { val params = RegistrationParams( username = userName, password = password, initialDeviceDisplayName = initialDeviceDisplayName ) - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - performRegistrationRequest(params) - .also { - pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) - .also { pendingSessionStore.savePendingSessionData(it) } - } - } + return performRegistrationRequest(params) + .also { + pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) + .also { pendingSessionStore.savePendingSessionData(it) } + } } - override fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable { - val safeSession = pendingSessionData.currentSession ?: run { - callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) - return NoOpCancellable - } + override suspend fun performReCaptcha(response: String): RegistrationResult { + val safeSession = pendingSessionData.currentSession + ?: throw IllegalStateException("developer error, call createAccount() method first") + val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response)) - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - performRegistrationRequest(params) - } + return performRegistrationRequest(params) } - override fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable { - val safeSession = pendingSessionData.currentSession ?: run { - callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) - return NoOpCancellable - } + override suspend fun acceptTerms(): RegistrationResult { + val safeSession = pendingSessionData.currentSession + ?: throw IllegalStateException("developer error, call createAccount() method first") + val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession)) - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - performRegistrationRequest(params) - } + return performRegistrationRequest(params) } - override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable { - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - pendingSessionData = pendingSessionData.copy(currentThreePidData = null) - .also { pendingSessionStore.savePendingSessionData(it) } + override suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult { + pendingSessionData = pendingSessionData.copy(currentThreePidData = null) + .also { pendingSessionStore.savePendingSessionData(it) } - sendThreePid(threePid) - } + return sendThreePid(threePid) } - override fun sendAgainThreePid(callback: MatrixCallback<RegistrationResult>): Cancelable { - val safeCurrentThreePid = pendingSessionData.currentThreePidData?.threePid ?: run { - callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) - return NoOpCancellable - } - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - sendThreePid(safeCurrentThreePid) - } + override suspend fun sendAgainThreePid(): RegistrationResult { + val safeCurrentThreePid = pendingSessionData.currentThreePidData?.threePid + ?: throw IllegalStateException("developer error, call createAccount() method first") + + return sendThreePid(safeCurrentThreePid) } private suspend fun sendThreePid(threePid: RegisterThreePid): RegistrationResult { @@ -173,20 +145,15 @@ internal class DefaultRegistrationWizard( return performRegistrationRequest(params) } - override fun checkIfEmailHasBeenValidated(delayMillis: Long, callback: MatrixCallback<RegistrationResult>): Cancelable { - val safeParam = pendingSessionData.currentThreePidData?.registrationParams ?: run { - callback.onFailure(IllegalStateException("developer error, no pending three pid")) - return NoOpCancellable - } - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - performRegistrationRequest(safeParam, delayMillis) - } + override suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult { + val safeParam = pendingSessionData.currentThreePidData?.registrationParams + ?: throw IllegalStateException("developer error, no pending three pid") + + return performRegistrationRequest(safeParam, delayMillis) } - override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable { - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - validateThreePid(code) - } + override suspend fun handleValidateThreePid(code: String): RegistrationResult { + return validateThreePid(code) } private suspend fun validateThreePid(code: String): RegistrationResult { @@ -210,15 +177,12 @@ internal class DefaultRegistrationWizard( } } - override fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable { - val safeSession = pendingSessionData.currentSession ?: run { - callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) - return NoOpCancellable - } - return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) { - val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) - performRegistrationRequest(params) - } + override suspend fun dummy(): RegistrationResult { + val safeSession = pendingSessionData.currentSession + ?: throw IllegalStateException("developer error, call createAccount() method first") + + val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) + return performRegistrationRequest(params) } private suspend fun performRegistrationRequest(registrationParams: RegistrationParams, @@ -239,9 +203,4 @@ internal class DefaultRegistrationWizard( val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) return RegistrationResult.Success(session) } - - private fun buildAuthAPI(): AuthAPI { - val retrofit = retrofitFactory.create(okHttpClient, pendingSessionData.homeServerConnectionConfig.homeServerUri.toString()) - return retrofit.create(AuthAPI::class.java) - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt index 1a0383cb220a8d031d80aca9a54a4ce9eff9148b..da0866a5fd56b3e8e7ec58f907c930d78ce74bb4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt @@ -16,14 +16,25 @@ package org.matrix.android.sdk.internal.auth.registration +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse -import org.matrix.android.sdk.api.auth.UIABaseAuth import timber.log.Timber import kotlin.coroutines.suspendCoroutine -internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean { +/** + * Handle a UIA challenge + * + * @param failure the failure to handle + * @param interceptor see doc in [UserInteractiveAuthInterceptor] + * @param retryBlock called at the end of the process, in this block generally retry executing the task, with + * provided authUpdate + * @return true if UIA is handled without error + */ +internal suspend fun handleUIA(failure: Throwable, + interceptor: UserInteractiveAuthInterceptor, + retryBlock: suspend (UIABaseAuth) -> Unit): Boolean { Timber.d("## UIA: check error ${failure.message}") val flowResponse = failure.toRegistrationFlowResponse() ?: return false.also { @@ -38,16 +49,16 @@ internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveA suspendCoroutine<UIABaseAuth> { continuation -> interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation) } - } catch (failure: Throwable) { - Timber.w(failure, "## UIA: failed to participate") + } catch (failure2: Throwable) { + Timber.w(failure2, "## UIA: failed to participate") return false } - Timber.d("## UIA: updated auth $authUpdate") + Timber.d("## UIA: updated auth") return try { retryBlock(authUpdate) true - } catch (failure: Throwable) { - handleUIA(failure, interceptor, retryBlock) + } catch (failure3: Throwable) { + handleUIA(failure3, interceptor, retryBlock) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt index a786ebd4b2baaa7d7efa738d47f4dea307623694..e114f86a9956c614497337875f91df73eb6cf452 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt @@ -61,7 +61,6 @@ import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.DefaultClaimOneTimeKeysForUsersDevice import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceTask -import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceWithUserPasswordTask import org.matrix.android.sdk.internal.crypto.tasks.DefaultDownloadKeysForUsers import org.matrix.android.sdk.internal.crypto.tasks.DefaultEncryptEventTask import org.matrix.android.sdk.internal.crypto.tasks.DefaultGetDeviceInfoTask @@ -75,7 +74,6 @@ import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadKeysTask import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSignaturesTask import org.matrix.android.sdk.internal.crypto.tasks.DefaultUploadSigningKeysTask import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask -import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask import org.matrix.android.sdk.internal.crypto.tasks.EncryptEventTask import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask @@ -240,9 +238,6 @@ internal abstract class CryptoModule { @Binds abstract fun bindClaimOneTimeKeysForUsersDeviceTask(task: DefaultClaimOneTimeKeysForUsersDevice): ClaimOneTimeKeysForUsersDeviceTask - @Binds - abstract fun bindDeleteDeviceWithUserPasswordTask(task: DefaultDeleteDeviceWithUserPasswordTask): DeleteDeviceWithUserPasswordTask - @Binds abstract fun bindCrossSigningService(service: DefaultCrossSigningService): CrossSigningService diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 678bc9819f12dc8bc2ba4d7be820b1d8e6481fb0..17d25736ebd52b105cd70295eab5dc5f8a64a7d5 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction import org.matrix.android.sdk.internal.crypto.algorithms.IMXEncrypting +import org.matrix.android.sdk.internal.crypto.algorithms.IMXGroupEncryption import org.matrix.android.sdk.internal.crypto.algorithms.IMXWithHeldExtension import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory import org.matrix.android.sdk.internal.crypto.algorithms.olm.MXOlmEncryptionFactory @@ -75,7 +76,6 @@ import org.matrix.android.sdk.internal.crypto.model.toRest import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask -import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask @@ -98,7 +98,6 @@ import org.matrix.olm.OlmManager import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import kotlin.jvm.Throws import kotlin.math.max /** @@ -153,9 +152,8 @@ internal class DefaultCryptoService @Inject constructor( // Repository private val megolmEncryptionFactory: MXMegolmEncryptionFactory, private val olmEncryptionFactory: MXOlmEncryptionFactory, - private val deleteDeviceTask: DeleteDeviceTask, - private val deleteDeviceWithUserPasswordTask: DeleteDeviceWithUserPasswordTask, // Tasks + private val deleteDeviceTask: DeleteDeviceTask, private val getDevicesTask: GetDevicesTask, private val getDeviceInfoTask: GetDeviceInfoTask, private val setDeviceNameTask: SetDeviceNameTask, @@ -217,15 +215,6 @@ internal class DefaultCryptoService @Inject constructor( .executeBy(taskExecutor) } - override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) { - deleteDeviceWithUserPasswordTask - .configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) { - this.executionThread = TaskThread.CRYPTO - this.callback = callback - } - .executeBy(taskExecutor) - } - override fun getCryptoVersion(context: Context, longFormat: Boolean): String { return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version } @@ -678,7 +667,12 @@ internal class DefaultCryptoService @Inject constructor( override fun discardOutboundSession(roomId: String) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { - roomEncryptorsStore.get(roomId)?.discardSessionKey() + val roomEncryptor = roomEncryptorsStore.get(roomId) + if (roomEncryptor is IMXGroupEncryption) { + roomEncryptor.discardSessionKey() + } else { + Timber.e("## CRYPTO | discardOutboundSession() for:$roomId: Unable to handle IMXGroupEncryption") + } } } @@ -714,7 +708,7 @@ internal class DefaultCryptoService @Inject constructor( */ @Throws(MXCryptoError::class) private fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult { - return eventDecryptor.decryptEvent(event, timeline) + return eventDecryptor.decryptEvent(event, timeline) } /** @@ -1301,6 +1295,43 @@ internal class DefaultCryptoService @Inject constructor( cryptoStore.logDbUsageInfo() } + override fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>) { + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date") + // Ensure to load all room members + try { + loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId)) + } catch (failure: Throwable) { + Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members") + callback.onFailure(failure) + return@launch + } + + val userIds = getRoomUserIds(roomId) + val alg = roomEncryptorsStore.get(roomId) + ?: getEncryptionAlgorithm(roomId) + ?.let { setEncryptionInRoom(roomId, it, false, userIds) } + ?.let { roomEncryptorsStore.get(roomId) } + + if (alg == null) { + val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, MXCryptoError.NO_MORE_ALGORITHM_REASON) + Timber.e("## CRYPTO | prepareToEncrypt() : $reason") + callback.onFailure(IllegalArgumentException("Missing algorithm")) + return@launch + } + + runCatching { + (alg as? IMXGroupEncryption)?.preshareKey(userIds) + }.fold( + { callback.onSuccess(Unit) }, + { + Timber.e("## CRYPTO | prepareToEncrypt() failed.") + callback.onFailure(it) + } + ) + } + } + /* ========================================================================================== * For test only * ========================================================================================== */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index 92b77288909132f34d5b37724f2c937a7c7689ad..32324896fa3acd7868cb3cfadd83e4003f9e95df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -105,7 +105,7 @@ internal class EventDecryptor @Inject constructor( try { return alg.decryptEvent(event, timeline) } catch (mxCryptoError: MXCryptoError) { - Timber.d("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError") + Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError") if (algorithm == MXCRYPTO_ALGORITHM_OLM) { if (mxCryptoError is MXCryptoError.Base && mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt index c075c90eb9f09718941be100bab94b54e4673015..4a0a274f4f369e825d194b1f71fd261d021000ee 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListen 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.toModel +import org.matrix.android.sdk.internal.crypto.algorithms.IMXGroupEncryption import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent @@ -290,12 +291,16 @@ internal class IncomingGossipingRequestManager @Inject constructor( .also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) } cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { - val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey) + if (roomEncryptor is IMXGroupEncryption) { + val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey) - if (isSuccess) { - cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED) + if (isSuccess) { + cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED) + } else { + cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS) + } } else { - cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS) + Timber.e("## CRYPTO | handleKeyRequestFromOtherUser() from:$userId: Unable to handle IMXGroupEncryption.reshareKey for $alg") } } cryptoStore.updateGossipingRequestState(request, GossipingRequestState.RE_REQUESTED) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt index fc3ea08a2160bfb8126196e8b677a4c6db73f081..5294429198a81868ee0914bc824466afe89321a4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt @@ -32,34 +32,4 @@ internal interface IMXEncrypting { * @return the encrypted content */ suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Content - - /** - * In Megolm, each recipient maintains a record of the ratchet value which allows - * them to decrypt any messages sent in the session after the corresponding point - * in the conversation. If this value is compromised, an attacker can similarly - * decrypt past messages which were encrypted by a key derived from the - * compromised or subsequent ratchet values. This gives 'partial' forward - * secrecy. - * - * To mitigate this issue, the application should offer the user the option to - * discard historical conversations, by winding forward any stored ratchet values, - * or discarding sessions altogether. - */ - fun discardSessionKey() - - /** - * Re-shares a session key with devices if the key has already been - * sent to them. - * - * @param sessionId The id of the outbound session to share. - * @param userId The id of the user who owns the target device. - * @param deviceId The id of the target device. - * @param senderKey The key of the originating device for the session. - * - * @return true in case of success - */ - suspend fun reshareKey(sessionId: String, - userId: String, - deviceId: String, - senderKey: String): Boolean } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt new file mode 100644 index 0000000000000000000000000000000000000000..1fd5061a65c8e1b99c8aca3c064d153c4275a029 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt @@ -0,0 +1,52 @@ +/* + * 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.algorithms + +internal interface IMXGroupEncryption { + + /** + * In Megolm, each recipient maintains a record of the ratchet value which allows + * them to decrypt any messages sent in the session after the corresponding point + * in the conversation. If this value is compromised, an attacker can similarly + * decrypt past messages which were encrypted by a key derived from the + * compromised or subsequent ratchet values. This gives 'partial' forward + * secrecy. + * + * To mitigate this issue, the application should offer the user the option to + * discard historical conversations, by winding forward any stored ratchet values, + * or discarding sessions altogether. + */ + fun discardSessionKey() + + suspend fun preshareKey(userIds: List<String>) + + /** + * Re-shares a session key with devices if the key has already been + * sent to them. + * + * @param sessionId The id of the outbound session to share. + * @param userId The id of the user who owns the target device. + * @param deviceId The id of the target device. + * @param senderKey The key of the originating device for the session. + * + * @return true in case of success + */ + suspend fun reshareKey(sessionId: String, + userId: String, + deviceId: String, + senderKey: String): Boolean +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index fa8acafb83fabd4dcaffb55b8018bc79a4da536d..6b91c0b859adf52242d2b77713f7fa49d934bbea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -18,8 +18,6 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event @@ -30,6 +28,7 @@ import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.algorithms.IMXEncrypting +import org.matrix.android.sdk.internal.crypto.algorithms.IMXGroupEncryption import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap @@ -39,8 +38,6 @@ import org.matrix.android.sdk.internal.crypto.model.forEach import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.JsonCanonicalizer import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.convertToUTF8 @@ -54,14 +51,14 @@ internal class MXMegolmEncryption( private val cryptoStore: IMXCryptoStore, private val deviceListManager: DeviceListManager, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, - private val credentials: Credentials, + private val userId: String, + private val deviceId: String, private val sendToDeviceTask: SendToDeviceTask, private val messageEncrypter: MessageEncrypter, private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository, - private val taskExecutor: TaskExecutor, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val cryptoCoroutineScope: CoroutineScope -) : IMXEncrypting { +) : IMXEncrypting, IMXGroupEncryption { // OutboundSessionInfo. Null if we haven't yet started setting one up. Note // that even if this is non-null, it may not be ready for use (in which @@ -93,6 +90,7 @@ internal class MXMegolmEncryption( // annoyingly we have to serialize again the saved outbound session to store message index :/ // if not we would see duplicate message index errors olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId) + Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis") } } @@ -117,6 +115,16 @@ internal class MXMegolmEncryption( olmDevice.discardOutboundGroupSessionForRoom(roomId) } + override suspend fun preshareKey(userIds: List<String>) { + val ts = System.currentTimeMillis() + Timber.v("## CRYPTO | preshareKey : getDevicesInRoom") + val devices = getDevicesInRoom(userIds) + val outboundSession = ensureOutboundSession(devices.allowedDevices) + + notifyWithheldForSession(devices.withHeldDevices, outboundSession) + + Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis") + } /** * Prepare a new session. * @@ -252,7 +260,7 @@ internal class MXMegolmEncryption( continue } - Timber.i("## CRYPTO | shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID") contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo))) haveTargets = true } @@ -263,12 +271,14 @@ internal class MXMegolmEncryption( // attempted to share with) rather than the contentMap (those we did // share with), because we don't want to try to claim a one-time-key // for dead devices on every message. + val gossipingEventBuffer = arrayListOf<Event>() for ((userId, devicesToShareWith) in devicesByUser) { for ((deviceId) in devicesToShareWith) { session.sharedWithHelper.markedSessionAsShared(userId, deviceId, chainIndex) - cryptoStore.saveGossipingEvent(Event( + gossipingEventBuffer.add( + Event( type = EventType.ROOM_KEY, - senderId = credentials.userId, + senderId = this.userId, content = submap.apply { this["session_key"] = "" // we add a fake key for trail @@ -278,6 +288,8 @@ internal class MXMegolmEncryption( } } + cryptoStore.saveGossipingEvents(gossipingEventBuffer) + if (haveTargets) { t0 = System.currentTimeMillis() Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target") @@ -294,8 +306,11 @@ internal class MXMegolmEncryption( } } - private fun notifyKeyWithHeld(targets: List<UserDevice>, sessionId: String, senderKey: String?, code: WithHeldCode) { - Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId ") + private suspend fun notifyKeyWithHeld(targets: List<UserDevice>, + sessionId: String, + senderKey: String?, + code: WithHeldCode) { + Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code") val withHeldContent = RoomKeyWithHeldContent( roomId = roomId, senderKey = senderKey, @@ -311,13 +326,11 @@ internal class MXMegolmEncryption( } } ) - sendToDeviceTask.configureWith(params) { - callback = object : MatrixCallback<Unit> { - override fun onFailure(failure: Throwable) { - Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ") - } - } - }.executeBy(taskExecutor) + try { + sendToDeviceTask.execute(params) + } catch (failure: Throwable) { + Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ") + } } /** @@ -343,7 +356,7 @@ internal class MXMegolmEncryption( // Include our device ID so that recipients can send us a // m.new_device message if they don't have our session key. - map["device_id"] = credentials.deviceId!! + map["device_id"] = deviceId session.useCount++ return map } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt index f0cc15fb63cc89c99e79bb8e6dbfa5cf0b5c2182..9f6312ea97804478bdd3d8cdbaf1e80ab4dead1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import kotlinx.coroutines.CoroutineScope -import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction @@ -26,7 +25,8 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupServic import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask -import org.matrix.android.sdk.internal.task.TaskExecutor +import org.matrix.android.sdk.internal.di.DeviceId +import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject @@ -36,29 +36,29 @@ internal class MXMegolmEncryptionFactory @Inject constructor( private val cryptoStore: IMXCryptoStore, private val deviceListManager: DeviceListManager, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, - private val credentials: Credentials, + @UserId private val userId: String, + @DeviceId private val deviceId: String?, private val sendToDeviceTask: SendToDeviceTask, private val messageEncrypter: MessageEncrypter, private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository, - private val taskExecutor: TaskExecutor, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val cryptoCoroutineScope: CoroutineScope) { fun create(roomId: String): MXMegolmEncryption { return MXMegolmEncryption( - roomId, - olmDevice, - defaultKeysBackupService, - cryptoStore, - deviceListManager, - ensureOlmSessionsForDevicesAction, - credentials, - sendToDeviceTask, - messageEncrypter, - warnOnUnknownDevicesRepository, - taskExecutor, - coroutineDispatchers, - cryptoCoroutineScope + roomId = roomId, + olmDevice = olmDevice, + defaultKeysBackupService = defaultKeysBackupService, + cryptoStore = cryptoStore, + deviceListManager = deviceListManager, + ensureOlmSessionsForDevicesAction = ensureOlmSessionsForDevicesAction, + userId = userId, + deviceId = deviceId!!, + sendToDeviceTask = sendToDeviceTask, + messageEncrypter = messageEncrypter, + warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository, + coroutineDispatchers = coroutineDispatchers, + cryptoCoroutineScope = cryptoCoroutineScope ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt index 9acc9bc1b2e3043a2f88f8fcddb9f2a782f3d16e..65f78e11f0291b0f4b8a29f59da2b480cc1c0658 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -76,13 +76,4 @@ internal class MXOlmEncryption( deviceListManager.downloadKeys(users, false) ensureOlmSessionsForUsersAction.handle(users) } - - override fun discardSessionKey() { - // No need for olm - } - - override suspend fun reshareKey(sessionId: String, userId: String, deviceId: String, senderKey: String): Boolean { - // No need for olm - return false - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt index e494cb5b3136fd90b50830872ec64116d1bcfc5c..cf2d6aa2693a4e5561f59e8e8ba58172b9990c1f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt @@ -21,11 +21,11 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.util.JsonCanonicalizer import timber.log.Timber -fun CryptoDeviceInfo.canonicalSignable(): String { +internal fun CryptoDeviceInfo.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } -fun CryptoCrossSigningKey.canonicalSignable(): String { +internal fun CryptoCrossSigningKey.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } @@ -40,7 +40,7 @@ fun String.fromBase64(): ByteArray { /** * Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source */ -fun String.fromBase64Safe(): ByteArray? { +internal fun String.fromBase64Safe(): ByteArray? { return try { Base64.decode(this, Base64.DEFAULT) } catch (throwable: Throwable) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt index 202aa55624fa53ec9f00b6d985081626ea966144..606d2e3fc0c99a01ddb666336e62c36ab079d060 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt @@ -63,7 +63,7 @@ data class CryptoCrossSigningKey( ) } - data class Builder( + internal data class Builder( val userId: String, val usage: KeyUsage, private var base64Pkey: String? = null, @@ -97,7 +97,7 @@ data class CryptoCrossSigningKey( } } -enum class KeyUsage(val value: String) { +internal enum class KeyUsage(val value: String) { MASTER("master"), SELF_SIGNING("self_signing"), USER_SIGNING("user_signing") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 369a4976c9683a7799f8809e44584a8896bfe97c..b9213ba758297549950996c724cdfb279e5f87bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -83,6 +83,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey +import org.matrix.android.sdk.internal.crypto.store.db.model.deleteOnCascade import org.matrix.android.sdk.internal.crypto.store.db.query.create import org.matrix.android.sdk.internal.crypto.store.db.query.delete import org.matrix.android.sdk.internal.crypto.store.db.query.get @@ -94,6 +95,7 @@ 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.extensions.clearWith import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.olm.OlmAccount import org.matrix.olm.OlmException @@ -293,7 +295,7 @@ internal class RealmCryptoStore @Inject constructor( realm.insertOrUpdate(entity) } // Ensure all other devices are deleted - u.devices.deleteAllFromRealm() + u.devices.clearWith { it.deleteOnCascade() } u.devices.addAll(new) } } @@ -309,7 +311,7 @@ internal class RealmCryptoStore @Inject constructor( .let { userEntity -> if (masterKey == null || selfSigningKey == null) { // The user has disabled cross signing? - userEntity.crossSigningInfoEntity?.deleteFromRealm() + userEntity.crossSigningInfoEntity?.deleteOnCascade() userEntity.crossSigningInfoEntity = null } else { var shouldResetMyDevicesLocalTrust = false @@ -1633,7 +1635,7 @@ internal class RealmCryptoStore @Inject constructor( } else { // Just override existing, caller should check and untrust id needed val existing = CrossSigningInfoEntity.getOrCreate(realm, userId) - existing.crossSigningKeys.deleteAllFromRealm() + existing.crossSigningKeys.clearWith { it.deleteOnCascade() } existing.crossSigningKeys.addAll( info.crossSigningKeys.map { crossSigningKeysMapper.map(it) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt index fdd3e947545300f3a4f0862a0be35fce12d20a44..8599c972e9cedeb7d17c76928a6a25705526f178 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt @@ -16,10 +16,11 @@ package org.matrix.android.sdk.internal.crypto.store.db.model -import org.matrix.android.sdk.internal.crypto.model.KeyUsage import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.internal.crypto.model.KeyUsage +import org.matrix.android.sdk.internal.extensions.clearWith internal open class CrossSigningInfoEntity( @PrimaryKey @@ -56,3 +57,8 @@ internal open class CrossSigningInfoEntity( info?.let { crossSigningKeys.add(it) } } } + +internal fun CrossSigningInfoEntity.deleteOnCascade() { + crossSigningKeys.clearWith { it.deleteOnCascade() } + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt index 571b9bb05f3d02d9ab57e9526333b36cb8fd1d66..61870ec486a52ca403a83a20e03e0c220a79e157 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt @@ -47,3 +47,8 @@ internal open class DeviceInfoEntity( companion object } + +internal fun DeviceInfoEntity.deleteOnCascade() { + trustLevelEntity?.deleteFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt index 8f2357223ed686881ee79ff8a9380e42a2bf1f91..9133413589c83a7e2ce68c707a62799be2fac5b6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt @@ -30,3 +30,8 @@ internal open class KeyInfoEntity( var signatures: String? = null, var trustLevelEntity: TrustLevelEntity? = null ) : RealmObject() + +internal fun KeyInfoEntity.deleteOnCascade() { + trustLevelEntity?.deleteFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt index 52c30a27cc49d3fce3a8f4b269c7aec355b1805d..df9482bf9615cc9196566cef5d18515da0ec2b35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt @@ -19,13 +19,20 @@ package org.matrix.android.sdk.internal.crypto.store.db.model import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.internal.extensions.clearWith internal open class UserEntity( @PrimaryKey var userId: String? = null, var devices: RealmList<DeviceInfoEntity> = RealmList(), var crossSigningInfoEntity: CrossSigningInfoEntity? = null, - var deviceTrackingStatus: Int = 0) - : RealmObject() { + var deviceTrackingStatus: Int = 0 +) : RealmObject() { companion object } + +internal fun UserEntity.deleteOnCascade() { + devices.clearWith { it.deleteOnCascade() } + crossSigningInfoEntity?.deleteOnCascade() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt index a1f8bd72621da4aae1da62fa634c4e99a83296ac..5a3b8e53975c4f45891b7b8d4ddd1915804a4366 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt @@ -16,11 +16,12 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields +import org.matrix.android.sdk.internal.crypto.store.db.model.deleteOnCascade /** * Get or create a user @@ -39,5 +40,5 @@ internal fun UserEntity.Companion.delete(realm: Realm, userId: String) { realm.where<UserEntity>() .equalTo(UserEntityFields.USER_ID, userId) .findFirst() - ?.deleteFromRealm() + ?.deleteOnCascade() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt index ff25ac0f66d16e5d0021ba92a766f82833bbe234..61596bb5b6a58c30c32160d413dee4175ca55e3a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt @@ -47,12 +47,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor( } } catch (throwable: Throwable) { if (params.userInteractiveAuthInterceptor == null - || !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth -> - execute(params.copy(userAuthParam = auth)) - } + || !handleUIA( + failure = throwable, + interceptor = params.userInteractiveAuthInterceptor, + retryBlock = { authUpdate -> + execute(params.copy(userAuthParam = authUpdate)) + } + ) ) { Timber.d("## UIA: propagate failure") - throw throwable + throw throwable } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt deleted file mode 100644 index dc0077425ed58b6cf89650fc54ca13677cce5e3d..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.tasks - -import org.matrix.android.sdk.api.auth.data.LoginFlowTypes -import org.matrix.android.sdk.internal.crypto.api.CryptoApi -import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams -import org.matrix.android.sdk.api.auth.UserPasswordAuth -import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.task.Task -import javax.inject.Inject - -internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserPasswordTask.Params, Unit> { - data class Params( - val deviceId: String, - val authSession: String?, - val password: String - ) -} - -internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor( - private val cryptoApi: CryptoApi, - @UserId private val userId: String, - private val globalErrorReceiver: GlobalErrorReceiver -) : DeleteDeviceWithUserPasswordTask { - - override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) { - return executeRequest(globalErrorReceiver) { - apiCall = cryptoApi.deleteDevice(params.deviceId, - DeleteDeviceParams( - auth = UserPasswordAuth( - type = LoginFlowTypes.PASSWORD, - session = params.authSession, - user = userId, - password = params.password - ).asMap() - ) - ) - } - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/EncryptEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/EncryptEventTask.kt index 56b267decdcc9d1c2231770231551f65da40528b..627352f5680f2193a7b5452518966d90edf5da83 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/EncryptEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/EncryptEventTask.kt @@ -45,7 +45,7 @@ internal class DefaultEncryptEventTask @Inject constructor( // don't want to wait for any query // if (!params.crypto.isRoomEncrypted(params.roomId)) return params.event val localEvent = params.event - if (localEvent.eventId == null) { + if (localEvent.eventId == null || localEvent.type == null) { throw IllegalArgumentException() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt index ef31130f55c48f862f2502b1baeb26735ed1e683..f8a8354e482e0c4ad2aa3b5c144c8e767545d541 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt @@ -126,11 +126,16 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor( uploadSigningKeysTask.execute(uploadSigningKeysParams) } catch (failure: Throwable) { if (params.interactiveAuthInterceptor == null - || !handleUIA(failure, params.interactiveAuthInterceptor) { authUpdate -> - uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate)) - }) { + || !handleUIA( + failure = failure, + interceptor = params.interactiveAuthInterceptor, + retryBlock = { authUpdate -> + uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate)) + } + ) + ) { Timber.d("## UIA: propagate failure") - throw failure + throw failure } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt index b772bfbce227e365833162ee89f097d88613f6f9..573f2c3a5495a414a8971045b4fe1630b167ec72 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt @@ -51,14 +51,13 @@ internal class DefaultSendEventTask @Inject constructor( val event = handleEncryption(params) val localId = event.eventId!! - localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENDING) val executeRequest = executeRequest<SendResponse>(globalErrorReceiver) { apiCall = roomAPI.send( localId, roomId = event.roomId ?: "", content = event.content, - eventType = event.type + eventType = event.type ?: "" ) } localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENT) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt index c39dfb101668ed56ad6ace386a61395ac36de769..ab125135bb393392b4b8cf3d1329c76f34ff6c14 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt @@ -50,7 +50,7 @@ internal class DefaultSendVerificationMessageTask @Inject constructor( localId, roomId = event.roomId ?: "", content = event.content, - eventType = event.type + eventType = event.type ?: "" ) } localEchoRepository.updateSendState(localId, event.roomId, SendState.SENT) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt index 4c1e896a2156979b194dfaf885fe39501c05dfd9..052b3f4e7272fc69d3459134dcc99b70e359996d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt @@ -21,7 +21,7 @@ import org.matrix.olm.OlmPkEncryption import org.matrix.olm.OlmPkSigning import org.matrix.olm.OlmUtility -fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T { +internal fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T { val olmPkEncryption = OlmPkEncryption() try { return block(olmPkEncryption) @@ -30,7 +30,7 @@ fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T { } } -fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T { +internal fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T { val olmPkDecryption = OlmPkDecryption() try { return block(olmPkDecryption) @@ -39,7 +39,7 @@ fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T { } } -fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T { +internal fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T { val olmPkSigning = OlmPkSigning() try { return block(olmPkSigning) @@ -48,7 +48,7 @@ fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T { } } -fun <T> withOlmUtility(block: (OlmUtility) -> T): T { +internal fun <T> withOlmUtility(block: (OlmUtility) -> T): T { val olmUtility = OlmUtility() try { return block(olmUtility) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationStateExt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationStateExt.kt new file mode 100644 index 0000000000000000000000000000000000000000..0617f32c2424b7637796cc91b488f58cf82adfac --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationStateExt.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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.verification + +import org.matrix.android.sdk.api.crypto.VerificationState +import org.matrix.android.sdk.api.crypto.isCanceled + +// State transition with control +internal fun VerificationState?.toState(newState: VerificationState): VerificationState { + // Cancel is always prioritary ? + // Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to + // consider as canceled + if (newState.isCanceled()) { + return newState + } + // never move out of cancel + if (this?.isCanceled() == true) { + return this + } + return newState +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt index a447e659f5943012b8bd3c90452fe404b69873c8..ad490bcd277febb624a2feb19ae189a4339efcac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt @@ -24,7 +24,6 @@ import androidx.work.WorkInfo import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.matrix.android.sdk.R import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState @@ -46,23 +45,16 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVerification import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS -import org.matrix.android.sdk.internal.di.DeviceId -import org.matrix.android.sdk.internal.di.SessionId -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.util.StringProvider import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import timber.log.Timber import java.util.UUID import java.util.concurrent.TimeUnit -import javax.inject.Inject internal class VerificationTransportRoomMessage( private val workManagerProvider: WorkManagerProvider, - private val stringProvider: StringProvider, private val sessionId: String, private val userId: String, private val userDeviceId: String?, @@ -167,7 +159,8 @@ internal class VerificationTransportRoomMessage( ) val info = MessageVerificationRequestContent( - body = stringProvider.getString(R.string.key_verification_request_fallback_message, userId), + body = "$userId is requesting to verify your key, but your client does not support in-chat key verification." + + " You will need to use legacy key verification to verify keys.", fromDevice = validInfo.fromDevice, toUserId = otherUserId, timestamp = validInfo.timestamp, @@ -387,29 +380,3 @@ internal class VerificationTransportRoomMessage( Timber.w("## SAS ignored verification ready with methods: ${keyReq.methods}") } } - -internal class VerificationTransportRoomMessageFactory @Inject constructor( - private val workManagerProvider: WorkManagerProvider, - private val stringProvider: StringProvider, - @SessionId - private val sessionId: String, - @UserId - private val userId: String, - @DeviceId - private val deviceId: String?, - private val localEchoEventFactory: LocalEchoEventFactory, - private val taskExecutor: TaskExecutor -) { - - fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage { - return VerificationTransportRoomMessage(workManagerProvider, - stringProvider, - sessionId, - userId, - deviceId, - roomId, - localEchoEventFactory, - tx, - taskExecutor.executorScope) - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..f89127273b8d0ced5ada3d5277155bf986290425 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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.verification + +import org.matrix.android.sdk.internal.di.DeviceId +import org.matrix.android.sdk.internal.di.SessionId +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.di.WorkManagerProvider +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.task.TaskExecutor +import javax.inject.Inject + +internal class VerificationTransportRoomMessageFactory @Inject constructor( + private val workManagerProvider: WorkManagerProvider, + @SessionId + private val sessionId: String, + @UserId + private val userId: String, + @DeviceId + private val deviceId: String?, + private val localEchoEventFactory: LocalEchoEventFactory, + private val taskExecutor: TaskExecutor +) { + + fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage { + return VerificationTransportRoomMessage(workManagerProvider, + sessionId, + userId, + deviceId, + roomId, + localEchoEventFactory, + tx, + taskExecutor.executorScope) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt index 5fd1c5d650ef5cc037a975ed0fa60eed5a7091a3..0dbbe656c71bb7fac8e473c4e8ee8ab04bba1d7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.internal.crypto.verification import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.CancelCode +import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.message.MessageType @@ -33,11 +33,9 @@ import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask -import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import timber.log.Timber -import javax.inject.Inject internal class VerificationTransportToDevice( private var tx: DefaultVerificationTransaction?, @@ -248,13 +246,3 @@ internal class VerificationTransportToDevice( ) } } - -internal class VerificationTransportToDeviceFactory @Inject constructor( - private val sendToDeviceTask: SendToDeviceTask, - @DeviceId val myDeviceId: String?, - private val taskExecutor: TaskExecutor) { - - fun createTransport(tx: DefaultVerificationTransaction?): VerificationTransportToDevice { - return VerificationTransportToDevice(tx, sendToDeviceTask, myDeviceId, taskExecutor) - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..e9a2c65ef753323b04414257d5cd3275a5ad7b2d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 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.verification + +import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask +import org.matrix.android.sdk.internal.di.DeviceId +import org.matrix.android.sdk.internal.task.TaskExecutor +import javax.inject.Inject + +internal class VerificationTransportToDeviceFactory @Inject constructor( + private val sendToDeviceTask: SendToDeviceTask, + @DeviceId val myDeviceId: String?, + private val taskExecutor: TaskExecutor) { + + fun createTransport(tx: DefaultVerificationTransaction?): VerificationTransportToDevice { + return VerificationTransportToDevice(tx, sendToDeviceTask, myDeviceId, taskExecutor) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt index e305c7ea38c77a77db0dfeb3534ea8e7c88ea5b2..f11ecc5d75f8b6209b0739bca723e0e034318881 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt @@ -16,6 +16,10 @@ package org.matrix.android.sdk.internal.database +import io.realm.Realm +import io.realm.RealmConfiguration +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.internal.database.helper.nextDisplayIndex import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ChunkEntityFields @@ -23,14 +27,11 @@ import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.task.TaskExecutor -import io.realm.Realm -import io.realm.RealmConfiguration -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -46,7 +47,7 @@ private const val MIN_NUMBER_OF_EVENTS_BY_CHUNK = 300 internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration, private val taskExecutor: TaskExecutor) : SessionLifecycleObserver { - override fun onStart() { + override fun onSessionStarted() { taskExecutor.executorScope.launch(Dispatchers.Default) { awaitTransaction(realmConfiguration) { realm -> val allRooms = realm.where(RoomEntity::class.java).findAll() @@ -56,7 +57,7 @@ internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val } } - private suspend fun cleanUp(realm: Realm, threshold: Long) { + private fun cleanUp(realm: Realm, threshold: Long) { val numberOfEvents = realm.where(EventEntity::class.java).findAll().size val numberOfTimelineEvents = realm.where(TimelineEventEntity::class.java).findAll().size Timber.v("Number of events in db: $numberOfEvents | Number of timeline events in db: $numberOfTimelineEvents") @@ -76,20 +77,7 @@ internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val chunk.numberOfTimelineEvents = chunk.numberOfTimelineEvents - eventsToRemove.size eventsToRemove.forEach { val canDeleteRoot = it.root?.stateKey == null - if (canDeleteRoot) { - it.root?.deleteFromRealm() - } - it.readReceipts?.readReceipts?.deleteAllFromRealm() - it.readReceipts?.deleteFromRealm() - it.annotations?.apply { - editSummary?.deleteFromRealm() - pollResponseSummary?.deleteFromRealm() - referencesSummaryEntity?.deleteFromRealm() - reactionsSummary.deleteAllFromRealm() - } - it.annotations?.deleteFromRealm() - it.readReceipts?.deleteFromRealm() - it.deleteFromRealm() + it.deleteOnCascade(canDeleteRoot) } // We reset the prevToken so we will need to fetch again. chunk.prevToken = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt index 71f978c03cfd0097113b6daed321b1ec378f1000..88aa432fb30db27f758b781ae37287a75bf488d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt @@ -71,7 +71,6 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real return@forEach } val domainEvent = event.asDomain() -// decryptIfNeeded(domainEvent) processors.filter { it.shouldProcess(eventId, domainEvent.getClearType(), eventInsert.insertType) }.forEach { @@ -83,6 +82,7 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real .findAll() .deleteAllFromRealm() } + processors.forEach { it.onPostProcess() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt index 3e2160e666bfeed3dfc9a44f81de02168bf4e571..2a0cd963b2506c5a1f8547993661766a1899bad5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt @@ -46,7 +46,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r private val backgroundRealm = AtomicReference<Realm>() private lateinit var results: AtomicReference<RealmResults<T>> - override fun onStart() { + override fun onSessionStarted() { if (isStarted.compareAndSet(false, true)) { BACKGROUND_HANDLER.post { val realm = Realm.getInstance(realmConfiguration) @@ -58,7 +58,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r } } - override fun onStop() { + override fun onSessionStopped() { if (isStarted.compareAndSet(true, false)) { BACKGROUND_HANDLER.post { results.getAndSet(null).removeAllChangeListeners() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt index 1947cc83e37cf8d18cc889fb0350a7c04154fcd9..f8d5d323a5a4f9015709cbd84773b6cea57ae682 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt @@ -44,14 +44,14 @@ internal class RealmSessionProvider @Inject constructor(@SessionDatabase private } @MainThread - override fun onStart() { + override fun onSessionStarted() { realmThreadLocal.getOrSet { Realm.getInstance(monarchy.realmConfiguration) } } @MainThread - override fun onStop() { + override fun onSessionStopped() { realmThreadLocal.get()?.close() realmThreadLocal.remove() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 57002b5a600b622eed6a8af4c67567c0f45746f4..c7fe7ab447fb55f1242ca3d0a3db3a288a2e62cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.database import io.realm.DynamicRealm import io.realm.RealmMigration +import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields +import org.matrix.android.sdk.internal.database.model.EditionOfEventFields import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields @@ -30,7 +32,7 @@ import javax.inject.Inject class RealmSessionStoreMigration @Inject constructor() : RealmMigration { companion object { - const val SESSION_STORE_SCHEMA_VERSION = 7L + const val SESSION_STORE_SCHEMA_VERSION = 8L } override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { @@ -43,6 +45,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration { if (oldVersion <= 4) migrateTo5(realm) if (oldVersion <= 5) migrateTo6(realm) if (oldVersion <= 6) migrateTo7(realm) + if (oldVersion <= 7) migrateTo8(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -122,4 +125,28 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration { } ?.removeField("areAllMembersLoaded") } + + private fun migrateTo8(realm: DynamicRealm) { + Timber.d("Step 7 -> 8") + + val editionOfEventSchema = realm.schema.create("EditionOfEvent") + .apply { + // setEmbedded does not return `this`... + isEmbedded = true + } + .addField(EditionOfEventFields.CONTENT, String::class.java) + .addField(EditionOfEventFields.EVENT_ID, String::class.java) + .setRequired(EditionOfEventFields.EVENT_ID, true) + .addField(EditionOfEventFields.SENDER_ID, String::class.java) + .setRequired(EditionOfEventFields.SENDER_ID, true) + .addField(EditionOfEventFields.TIMESTAMP, Long::class.java) + .addField(EditionOfEventFields.IS_LOCAL_ECHO, Boolean::class.java) + + realm.schema.get("EditAggregatedSummaryEntity") + ?.removeField("aggregatedContent") + ?.removeField("sourceEvents") + ?.removeField("lastEditTs") + ?.removeField("sourceLocalEchoEvents") + ?.addRealmListField(EditAggregatedSummaryEntityFields.EDITIONS.`$`, editionOfEventSchema) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index f764c4da4b0960b1d1b917a39ac8a238cad784b7..e262b40419466b4435f5a687d7ce5340a3388f39 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -38,12 +38,6 @@ import io.realm.Sort import io.realm.kotlin.createObject import timber.log.Timber -internal fun ChunkEntity.deleteOnCascade() { - assertIsManaged() - this.timelineEvents.deleteAllFromRealm() - this.deleteFromRealm() -} - internal fun ChunkEntity.merge(roomId: String, chunkToMerge: ChunkEntity, direction: PaginationDirection) { assertIsManaged() val localRealm = this.realm @@ -103,7 +97,8 @@ internal fun ChunkEntity.addTimelineEvent(roomId: String, this.root = eventEntity this.eventId = eventId this.roomId = roomId - this.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() + this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst() + ?.also { it.cleanUp(eventEntity.sender) } this.readReceipts = readReceiptsSummaryEntity this.displayIndex = displayIndex val roomMemberContent = roomMemberContentsByUser[senderId] diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt index a4108f0966ba98d0d0728112483f1771b2c5e628..724f307e3bda3648bf01b53cb11e5dd7c4f7bb80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt @@ -19,12 +19,7 @@ package org.matrix.android.sdk.internal.database.helper import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.RoomEntity -internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { - chunks.remove(chunkEntity) - chunkEntity.deleteOnCascade() -} - -internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) { +internal fun RoomEntity.addIfNecessary(chunkEntity: ChunkEntity) { if (!chunks.contains(chunkEntity)) { chunks.add(chunkEntity) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt index 6f4dac182c54314caa63369d1a810e96b1829fe9..90e867749ee05bf4404cc512fb755de8930d99cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.database.helper import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields -import org.matrix.android.sdk.internal.extensions.assertIsManaged import io.realm.Realm internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long { @@ -29,11 +28,3 @@ internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long { currentIdNum.toLong() + 1 } } - -internal fun TimelineEventEntity.deleteOnCascade() { - assertIsManaged() - root?.deleteFromRealm() - annotations?.deleteFromRealm() - readReceipts?.deleteFromRealm() - deleteFromRealm() -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventAnnotationsSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventAnnotationsSummaryMapper.kt index 9ed2664068f266803ef40e3cd5495a6db091cf50..4a26b4c4bf940c94c7b9d25037cefc15d3d81899 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventAnnotationsSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventAnnotationsSummaryMapper.kt @@ -20,11 +20,7 @@ import org.matrix.android.sdk.api.session.room.model.EditAggregatedSummary import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedSummary -import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity -import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntity -import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntity -import io.realm.RealmList internal object EventAnnotationsSummaryMapper { fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary { @@ -40,14 +36,18 @@ internal object EventAnnotationsSummaryMapper { it.sourceLocalEcho.toList() ) }, - editSummary = annotationsSummary.editSummary?.let { - EditAggregatedSummary( - ContentMapper.map(it.aggregatedContent), - it.sourceEvents.toList(), - it.sourceLocalEchoEvents.toList(), - it.lastEditTs - ) - }, + editSummary = annotationsSummary.editSummary + ?.let { + val latestEdition = it.editions.maxByOrNull { editionOfEvent -> editionOfEvent.timestamp } ?: return@let null + EditAggregatedSummary( + latestContent = ContentMapper.map(latestEdition.content), + sourceEvents = it.editions.filter { editionOfEvent -> !editionOfEvent.isLocalEcho } + .map { editionOfEvent -> editionOfEvent.eventId }, + localEchos = it.editions.filter { editionOfEvent -> editionOfEvent.isLocalEcho } + .map { editionOfEvent -> editionOfEvent.eventId }, + lastEditTs = latestEdition.timestamp + ) + }, referencesAggregatedSummary = annotationsSummary.referencesSummaryEntity?.let { ReferencesAggregatedSummary( it.eventId, @@ -62,46 +62,6 @@ internal object EventAnnotationsSummaryMapper { ) } - - fun map(annotationsSummary: EventAnnotationsSummary, roomId: String): EventAnnotationsSummaryEntity { - val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity() - eventAnnotationsSummaryEntity.eventId = annotationsSummary.eventId - eventAnnotationsSummaryEntity.roomId = roomId - eventAnnotationsSummaryEntity.editSummary = annotationsSummary.editSummary?.let { - EditAggregatedSummaryEntity( - ContentMapper.map(it.aggregatedContent), - RealmList<String>().apply { addAll(it.sourceEvents) }, - RealmList<String>().apply { addAll(it.localEchos) }, - it.lastEditTs - ) - } - eventAnnotationsSummaryEntity.reactionsSummary = annotationsSummary.reactionsSummary.let { - RealmList<ReactionAggregatedSummaryEntity>().apply { - addAll(it.map { - ReactionAggregatedSummaryEntity( - it.key, - it.count, - it.addedByMe, - it.firstTimestamp, - RealmList<String>().apply { addAll(it.sourceEvents) }, - RealmList<String>().apply { addAll(it.localEchoEvents) } - ) - }) - } - } - eventAnnotationsSummaryEntity.referencesSummaryEntity = annotationsSummary.referencesAggregatedSummary?.let { - ReferencesAggregatedSummaryEntity( - it.eventId, - ContentMapper.map(it.content), - RealmList<String>().apply { addAll(it.sourceEvents) }, - RealmList<String>().apply { addAll(it.localEchos) } - ) - } - eventAnnotationsSummaryEntity.pollResponseSummary = annotationsSummary.pollResponseSummary?.let { - PollResponseAggregatedSummaryEntityMapper.map(it) - } - return eventAnnotationsSummaryEntity - } } internal fun EventAnnotationsSummaryEntity.asDomain(): EventAnnotationsSummary { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt index 66eccdfba0e7f3e869ed8185544e2c8816af0cdf..a4a2fadd21d9045e7fefe5eab3a7f132cfcce6d7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database.mapper import com.squareup.moshi.JsonDataException import org.matrix.android.sdk.api.session.crypto.MXCryptoError 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.UnsignedData import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult @@ -29,8 +30,6 @@ import timber.log.Timber internal object EventMapper { fun map(event: Event, roomId: String): EventEntity { - val uds = if (event.unsignedData == null) null - else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData) val eventEntity = EventEntity() // TODO change this as we shouldn't use event everywhere eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}" @@ -39,14 +38,16 @@ internal object EventMapper { eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent()) eventEntity.isUseless = IsUselessResolver.isUseless(event) eventEntity.stateKey = event.stateKey - eventEntity.type = event.type + eventEntity.type = event.type ?: EventType.MISSING_TYPE eventEntity.sender = event.senderId eventEntity.originServerTs = event.originServerTs eventEntity.redacts = event.redacts eventEntity.age = event.unsignedData?.age ?: event.originServerTs - eventEntity.unsignedData = uds + eventEntity.unsignedData = event.unsignedData?.let { + MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(it) + } eventEntity.decryptionResultJson = event.mxDecryptionResult?.let { - MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java).toJson(it) + MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java).toJson(it) } eventEntity.decryptionErrorReason = event.mCryptoErrorReason eventEntity.decryptionErrorCode = event.mCryptoError?.name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt index 9770352a9553bb01b18bb2e481848dac9df8487c..68533a3c1995f6da4f0da25f620b76297bcd4954 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt @@ -21,6 +21,8 @@ import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.Index import io.realm.annotations.LinkingObjects +import org.matrix.android.sdk.internal.extensions.assertIsManaged +import org.matrix.android.sdk.internal.extensions.clearWith internal open class ChunkEntity(@Index var prevToken: String? = null, // Because of gaps we can have several chunks with nextToken == null @@ -43,3 +45,12 @@ internal open class ChunkEntity(@Index var prevToken: String? = null, companion object } + +internal fun ChunkEntity.deleteOnCascade(deleteStateEvents: Boolean, canDeleteRoot: Boolean) { + assertIsManaged() + if (deleteStateEvents) { + stateEvents.deleteAllFromRealm() + } + timelineEvents.clearWith { it.deleteOnCascade(canDeleteRoot) } + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EditAggregatedSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EditAggregatedSummaryEntity.kt index 604afc1ab1e2df450803845b1e211d414ce1b466..0ed927a6b8618c64cb9655956e395e5cd2be9eb9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EditAggregatedSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EditAggregatedSummaryEntity.kt @@ -17,17 +17,24 @@ package org.matrix.android.sdk.internal.database.model import io.realm.RealmList import io.realm.RealmObject +import io.realm.annotations.RealmClass /** - * Keep the latest state of edition of a message + * Keep all the editions of a message */ internal open class EditAggregatedSummaryEntity( - var aggregatedContent: String? = null, - // The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk) - var sourceEvents: RealmList<String> = RealmList(), - var sourceLocalEchoEvents: RealmList<String> = RealmList(), - var lastEditTs: Long = 0 + // The list of the editions used to build the summary (might be out of sync if chunked received from message chunk) + var editions: RealmList<EditionOfEvent> = RealmList() ) : RealmObject() { companion object } + +@RealmClass(embedded = true) +internal open class EditionOfEvent( + var senderId: String = "", + var eventId: String = "", + var content: String? = null, + var timestamp: Long = 0, + var isLocalEcho: Boolean = false +) : RealmObject() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt index 3e5e27761386c64db14b5b3ded6e801da031167a..3e8813042004e6fd9c31c33449b185b2923e2d50 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import timber.log.Timber internal open class EventAnnotationsSummaryEntity( @PrimaryKey @@ -29,5 +30,28 @@ internal open class EventAnnotationsSummaryEntity( var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null ) : RealmObject() { + /** + * Cleanup undesired editions, done by users different from the originalEventSender + */ + fun cleanUp(originalEventSenderId: String?) { + originalEventSenderId ?: return + + editSummary?.editions?.filter { + it.senderId != originalEventSenderId + } + ?.forEach { + Timber.w("Deleting an edition from ${it.senderId} of event sent by $originalEventSenderId") + it.deleteFromRealm() + } + } + companion object } + +internal fun EventAnnotationsSummaryEntity.deleteOnCascade() { + reactionsSummary.deleteAllFromRealm() + editSummary?.deleteFromRealm() + referencesSummaryEntity?.deleteFromRealm() + pollResponseSummary?.deleteFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.java b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.kt similarity index 88% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.java rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.kt index 05153c5734e1a16e3f5603222e03191414493983..463ccb2f46c1c936251c7d4f16b1bcba4b0e633f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.java +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertType.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.database.model; +package org.matrix.android.sdk.internal.database.model -public enum EventInsertType { +internal enum class EventInsertType { INITIAL_SYNC, INCREMENTAL_SYNC, PAGINATION, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt index 85375c8064be35ac8137d8820c6b58ebb469298d..5bfe2833f8ad0371319d5a0a316e0dafcc3bafab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt @@ -40,3 +40,8 @@ internal open class PushRuleEntity( companion object } + +internal fun PushRuleEntity.deleteOnCascade() { + conditions?.deleteAllFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt index 21e3510cd2eb699322b370c4fca6b1ee43a7a8a4..571bc71c273425ecb87630239ffd420f8cecac07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model import org.matrix.android.sdk.api.pushrules.RuleKind import io.realm.RealmList import io.realm.RealmObject +import org.matrix.android.sdk.internal.extensions.clearWith internal open class PushRulesEntity( var scope: String = "", @@ -35,3 +36,8 @@ internal open class PushRulesEntity( companion object } + +internal fun PushRulesEntity.deleteOnCascade() { + pushRules.clearWith { it.deleteOnCascade() } + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt index f85c01c48aff1bbffa0ad630ddce38fb3431a345..af8e4f2d370ee62c03e1234a6b666846d921c9f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt @@ -15,8 +15,8 @@ */ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.session.pushers.PusherState import io.realm.RealmObject +import org.matrix.android.sdk.api.session.pushers.PusherState // TODO // at java.lang.Thread.run(Thread.java:764) @@ -54,3 +54,8 @@ internal open class PusherEntity( companion object } + +internal fun PusherEntity.deleteOnCascade() { + data?.deleteFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt index 98b4329076798ce9b57950a0a0e7282de4faa5c9..9ca4adc33ed255345903be44a34b11d526c5e046 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt @@ -34,3 +34,8 @@ internal open class ReadReceiptsSummaryEntity( companion object } + +internal fun ReadReceiptsSummaryEntity.deleteOnCascade() { + readReceipts.deleteAllFromRealm() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt index bca2c42c9eba97c445442015bb0850b42f9c01a3..6e6096cf8a62111742d2306224ff91a125830b97 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt @@ -43,6 +43,7 @@ import io.realm.annotations.RealmModule EventAnnotationsSummaryEntity::class, ReactionAggregatedSummaryEntity::class, EditAggregatedSummaryEntity::class, + EditionOfEvent::class, PollResponseAggregatedSummaryEntity::class, ReferencesAggregatedSummaryEntity::class, PushRulesEntity::class, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt index 7bd0dbbb8f3f50160396095e397dd26e37c7af46..30bbde70c2e4c6a201dce560bde2b9a8e6eeec61 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt @@ -20,6 +20,7 @@ import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.Index import io.realm.annotations.LinkingObjects +import org.matrix.android.sdk.internal.extensions.assertIsManaged internal open class TimelineEventEntity(var localId: Long = 0, @Index var eventId: String = "", @@ -39,3 +40,13 @@ internal open class TimelineEventEntity(var localId: Long = 0, companion object } + +internal fun TimelineEventEntity.deleteOnCascade(canDeleteRoot: Boolean) { + assertIsManaged() + if (canDeleteRoot) { + root?.deleteFromRealm() + } + annotations?.deleteOnCascade() + readReceipts?.deleteOnCascade() + deleteFromRealm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt index 6028697054c0f94c2bc8b2544cade3f20180edb9..f7d2823303bf3199ef9711c0c251616159423e5f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.database.query import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ChunkEntityFields -import org.matrix.android.sdk.internal.database.model.RoomEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.RealmResults @@ -27,7 +26,7 @@ import io.realm.kotlin.where internal fun ChunkEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<ChunkEntity> { return realm.where<ChunkEntity>() - .equalTo("${ChunkEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId) + .equalTo(ChunkEntityFields.ROOM.ROOM_ID, roomId) } internal fun ChunkEntity.Companion.find(realm: Realm, roomId: String, prevToken: String? = null, nextToken: String? = null): ChunkEntity? { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt index 9a298b7e79c18f32c172bfefd67c2e7ace938f17..c3cae3d2685677fb898cda0a0e1574ff7eb6b21c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt @@ -23,18 +23,10 @@ import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where -internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> { - val query = realm.where<EventAnnotationsSummaryEntity>() - query.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId) - return query -} - -internal fun EventAnnotationsSummaryEntity.Companion.whereInRoom(realm: Realm, roomId: String?): RealmQuery<EventAnnotationsSummaryEntity> { - val query = realm.where<EventAnnotationsSummaryEntity>() - if (roomId != null) { - query.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId) - } - return query +internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> { + return realm.where<EventAnnotationsSummaryEntity>() + .equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId) + .equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId) } internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity { @@ -49,6 +41,6 @@ internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId } 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 } + return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst() + ?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt index d78bda23170dfc64009db952fd3940346a598301..359b25684435fb323243aad5b804ce90b0fda724 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt @@ -48,6 +48,6 @@ internal fun PushRuleEntity.Companion.where(realm: Realm, scope: String, ruleId: String): RealmQuery<PushRuleEntity> { return realm.where<PushRuleEntity>() - .equalTo("${PushRuleEntityFields.PARENT}.${PushRulesEntityFields.SCOPE}", scope) + .equalTo(PushRuleEntityFields.PARENT.SCOPE, scope) .equalTo(PushRuleEntityFields.RULE_ID, ruleId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt index 35f317f192b323cf873018cbe1c76712c59c7241..4af4da0a22a998331e75ce4ee8fd4e7b67f76a41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.UserDraftsEntity import org.matrix.android.sdk.internal.database.model.UserDraftsEntityFields import io.realm.Realm @@ -26,7 +25,7 @@ import io.realm.kotlin.where internal fun UserDraftsEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<UserDraftsEntity> { val query = realm.where<UserDraftsEntity>() if (roomId != null) { - query.equalTo(UserDraftsEntityFields.ROOM_SUMMARY_ENTITY + "." + RoomSummaryEntityFields.ROOM_ID, roomId) + query.equalTo(UserDraftsEntityFields.ROOM_SUMMARY_ENTITY.ROOM_ID, roomId) } return query } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt index 48fa41b35070bc5e2d62537837de4afdeee325dd..074f8dc43e193e5eba72fe38d008bf89f9796223 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt @@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.network.parsing.ForceToBooleanJsonAdapter import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory import org.matrix.android.sdk.internal.network.parsing.TlsVersionMoshiAdapter import org.matrix.android.sdk.internal.network.parsing.UriMoshiAdapter +import org.matrix.android.sdk.internal.session.sync.parsing.DefaultLazyRoomSyncEphemeralJsonAdapter object MoshiProvider { @@ -44,6 +45,8 @@ object MoshiProvider { .add(ForceToBooleanJsonAdapter()) .add(CipherSuiteMoshiAdapter()) .add(TlsVersionMoshiAdapter()) + // Use addLast here so we can inject a SplitLazyRoomSyncJsonAdapter later to override the default parsing. + .addLast(DefaultLazyRoomSyncEphemeralJsonAdapter()) .add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java) .registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT) .registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt index 0718096fd57556048bd1afc8f3c8fd3970e7fa2f..e52e32e16ad0ea2f280478fc96b24fd43a425917 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt @@ -16,8 +16,18 @@ package org.matrix.android.sdk.internal.extensions +import io.realm.RealmList import io.realm.RealmObject internal fun RealmObject.assertIsManaged() { check(isManaged) { "${javaClass.simpleName} entity should be managed to use this function" } } + +/** + * Clear a RealmList by deleting all its items calling the provided lambda + */ +internal fun <T> RealmList<T>.clearWith(delete: (T) -> Unit) { + while (!isEmpty()) { + first()?.let { delete.invoke(it) } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt new file mode 100644 index 0000000000000000000000000000000000000000..862a4855cc0902ba116f3b0c986698d970097a50 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.api.federation.FederationService +import org.matrix.android.sdk.api.federation.FederationVersion +import javax.inject.Inject + +internal class DefaultFederationService @Inject constructor( + private val getFederationVersionTask: GetFederationVersionTask +) : FederationService { + override suspend fun getFederationVersion(): FederationVersion { + return getFederationVersionTask.execute(Unit) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt new file mode 100644 index 0000000000000000000000000000000000000000..1816616336c9a683a0605b91daf9da666989a625 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.GET + +internal interface FederationAPI { + @GET(NetworkConstants.URI_FEDERATION_PATH + "version") + fun getVersion(): Call<FederationGetVersionResult> +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt new file mode 100644 index 0000000000000000000000000000000000000000..3d84008be6ce56febc92c8becc5b4bc438b21e1a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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.federation + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Ref: https://matrix.org/docs/spec/server_server/latest#get-matrix-federation-v1-version + */ +@JsonClass(generateAdapter = true) +internal data class FederationGetVersionResult( + @Json(name = "server") + val server: FederationGetVersionServer? +) + +@JsonClass(generateAdapter = true) +internal data class FederationGetVersionServer( + /** + * Arbitrary name that identify this implementation. + */ + @Json(name = "name") + val name: String?, + /** + * Version of this implementation. The version format depends on the implementation. + */ + @Json(name = "version") + val version: String? +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..320bf1d4454df3ec98294597e50191480671d707 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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.federation + +import dagger.Binds +import dagger.Lazy +import dagger.Module +import dagger.Provides +import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.federation.FederationService +import org.matrix.android.sdk.internal.di.Unauthenticated +import org.matrix.android.sdk.internal.network.RetrofitFactory + +@Module +internal abstract class FederationModule { + + @Module + companion object { + @Provides + @JvmStatic + fun providesFederationAPI(@Unauthenticated okHttpClient: Lazy<OkHttpClient>, + sessionParams: SessionParams, + retrofitFactory: RetrofitFactory): FederationAPI { + return retrofitFactory.create(okHttpClient, sessionParams.homeServerUrl).create(FederationAPI::class.java) + } + } + + @Binds + abstract fun bindFederationService(service: DefaultFederationService): FederationService + + @Binds + abstract fun bindGetFederationVersionTask(task: DefaultGetFederationVersionTask): GetFederationVersionTask +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt new file mode 100644 index 0000000000000000000000000000000000000000..ce35e48f6b53f13e6d9c37f4f59a2df515a7fc98 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.api.federation.FederationVersion +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetFederationVersionTask : Task<Unit, FederationVersion> + +internal class DefaultGetFederationVersionTask @Inject constructor( + private val federationAPI: FederationAPI +) : GetFederationVersionTask { + + override suspend fun execute(params: Unit): FederationVersion { + val result = executeRequest<FederationGetVersionResult>(null) { + apiCall = federationAPI.getVersion() + } + + return FederationVersion( + name = result.server?.name, + version = result.server?.version + ) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt index a14c86efb62e2bc48cc763dd22a4f32424a03fa1..99c12255cd337a92f404aa40231171fef8c8886b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt @@ -36,4 +36,7 @@ internal object NetworkConstants { // Integration const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/" + + // Federation + const val URI_FEDERATION_PATH = "_matrix/federation/v1/" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultInitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultInitialSyncProgressService.kt deleted file mode 100644 index 9918e83fbc13da9e199ce8ce13f928e2053ef668..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultInitialSyncProgressService.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.session - -import androidx.annotation.StringRes -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import org.matrix.android.sdk.api.session.InitialSyncProgressService -import timber.log.Timber -import javax.inject.Inject - -@SessionScope -class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService { - - private val status = MutableLiveData<InitialSyncProgressService.Status>() - - private var rootTask: TaskInfo? = null - - override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> { - return status - } - - fun startTask(@StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) { - // Create a rootTask, or add a child to the leaf - if (rootTask == null) { - rootTask = TaskInfo(nameRes, totalProgress) - } else { - val currentLeaf = rootTask!!.leaf() - - val newTask = TaskInfo(nameRes, - totalProgress, - currentLeaf, - parentWeight) - - currentLeaf.child = newTask - } - reportProgress(0) - } - - fun reportProgress(progress: Int) { - rootTask?.leaf()?.setProgress(progress) - } - - fun endTask(nameRes: Int) { - val endedTask = rootTask?.leaf() - if (endedTask?.nameRes == nameRes) { - // close it - val parent = endedTask.parent - parent?.child = null - parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt()) - } - if (endedTask?.parent == null) { - status.postValue(InitialSyncProgressService.Status.Idle) - } - } - - fun endAll() { - rootTask = null - status.postValue(InitialSyncProgressService.Status.Idle) - } - - private inner class TaskInfo(@StringRes var nameRes: Int, - var totalProgress: Int, - var parent: TaskInfo? = null, - var parentWeight: Float = 1f, - var offset: Int = parent?.currentProgress ?: 0) { - var child: TaskInfo? = null - var currentProgress: Int = 0 - - /** - * Get the further child - */ - fun leaf(): TaskInfo { - var last = this - while (last.child != null) { - last = last.child!! - } - return last - } - - /** - * Set progress of the parent if any (which will post value), or post the value - */ - fun setProgress(progress: Int) { - currentProgress = progress -// val newProgress = Math.min(currentProgress + progress, totalProgress) - parent?.let { - val parentProgress = (currentProgress * parentWeight).toInt() - it.setProgress(offset + parentProgress) - } ?: run { - Timber.v("--- ${leaf().nameRes}: $currentProgress") - status.postValue(InitialSyncProgressService.Status.Progressing(leaf().nameRes, currentProgress)) - } - } - } -} - -inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?, - @StringRes nameRes: Int, - totalProgress: Int, - parentWeight: Float = 1f, - block: () -> T): T { - reporter?.startTask(nameRes, totalProgress, parentWeight) - return block().also { - reporter?.endTask(nameRes) - } -} - -inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: DefaultInitialSyncProgressService?, - taskId: Int, - weight: Float, - transform: (Map.Entry<K, V>) -> R): List<R> { - val total = count().toFloat() - var current = 0 - reporter?.startTask(taskId, 100, weight) - return map { - reporter?.reportProgress((current / total * 100).toInt()) - current++ - transform.invoke(it) - }.also { - reporter?.endTask(taskId) - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index e09c051c81a25d3c1cf2be408ac3662a3daa7305..45fcc5af2d90042a7e3fd21f1924f1bfc99f00b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -22,8 +22,9 @@ import io.realm.RealmConfiguration import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.pushrules.PushRuleService -import org.matrix.android.sdk.api.session.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.accountdata.AccountDataService @@ -49,6 +50,7 @@ import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageServi import org.matrix.android.sdk.api.session.signout.SignOutService import org.matrix.android.sdk.api.session.sync.FilterService import org.matrix.android.sdk.api.session.terms.TermsService +import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService import org.matrix.android.sdk.api.session.typing.TypingUsersTracker import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.widgets.WidgetService @@ -63,7 +65,6 @@ import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.network.GlobalErrorHandler import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService -import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor import org.matrix.android.sdk.internal.session.sync.SyncTokenStore import org.matrix.android.sdk.internal.session.sync.job.SyncThread import org.matrix.android.sdk.internal.session.sync.job.SyncWorker @@ -87,6 +88,7 @@ internal class DefaultSession @Inject constructor( private val groupService: Lazy<GroupService>, private val userService: Lazy<UserService>, private val filterService: Lazy<FilterService>, + private val federationService: Lazy<FederationService>, private val cacheService: Lazy<CacheService>, private val signOutService: Lazy<SignOutService>, private val pushRuleService: Lazy<PushRuleService>, @@ -114,10 +116,10 @@ internal class DefaultSession @Inject constructor( private val accountService: Lazy<AccountService>, private val defaultIdentityService: DefaultIdentityService, private val integrationManagerService: IntegrationManagerService, + private val thirdPartyService: Lazy<ThirdPartyService>, private val callSignalingService: Lazy<CallSignalingService>, @UnauthenticatedWithCertificate - private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>, - private val eventSenderProcessor: EventSenderProcessor + private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient> ) : Session, RoomService by roomService.get(), RoomDirectoryService by roomDirectoryService.get(), @@ -154,10 +156,9 @@ internal class DefaultSession @Inject constructor( isOpen = true cryptoService.get().ensureDevice() uiHandler.post { - lifecycleObservers.forEach { it.onStart() } + lifecycleObservers.forEach { it.onSessionStarted() } } globalErrorHandler.listener = this - eventSenderProcessor.start() } override fun requireBackgroundSync() { @@ -196,12 +197,11 @@ internal class DefaultSession @Inject constructor( stopSync() // timelineEventDecryptor.destroy() uiHandler.post { - lifecycleObservers.forEach { it.onStop() } + lifecycleObservers.forEach { it.onSessionStopped() } } cryptoService.get().close() isOpen = false globalErrorHandler.listener = null - eventSenderProcessor.interrupt() } override fun getSyncStateLive() = getSyncThread().liveState() @@ -258,6 +258,10 @@ internal class DefaultSession @Inject constructor( override fun searchService(): SearchService = searchService.get() + override fun federationService(): FederationService = federationService.get() + + override fun thirdPartyService(): ThirdPartyService = thirdPartyService.get() + override fun getOkHttpClient(): OkHttpClient { return unauthenticatedWithCertificateOkHttpClient.get() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt index 53b1a735447cbc899e5c9fba83cbe1d10bea87a0..7a687b774bfdfc745e0b1bd045738ecfa98d2c0b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt @@ -25,4 +25,12 @@ internal interface EventInsertLiveProcessor { fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean suspend fun process(realm: Realm, event: Event) + + /** + * Called after transaction. + * Maybe you prefer to process the events outside of the realm transaction. + */ + suspend fun onPostProcess() { + // Noop by default + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index f5eade1704e06669b5b3fb76181052f56d3a5946..7e1e3d0f7097b89daefecebc24c5fd3d89a0c0fe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.internal.crypto.SendGossipWorker import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker import org.matrix.android.sdk.internal.di.MatrixComponent +import org.matrix.android.sdk.internal.federation.FederationModule import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.session.account.AccountModule import org.matrix.android.sdk.internal.session.cache.CacheModule @@ -56,6 +57,7 @@ import org.matrix.android.sdk.internal.session.sync.SyncTask import org.matrix.android.sdk.internal.session.sync.SyncTokenStore import org.matrix.android.sdk.internal.session.sync.job.SyncWorker import org.matrix.android.sdk.internal.session.terms.TermsModule +import org.matrix.android.sdk.internal.session.thirdparty.ThirdPartyModule import org.matrix.android.sdk.internal.session.user.UserModule import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataModule import org.matrix.android.sdk.internal.session.widgets.WidgetModule @@ -86,8 +88,10 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers AccountDataModule::class, ProfileModule::class, AccountModule::class, + FederationModule::class, CallModule::class, - SearchModule::class + SearchModule::class, + ThirdPartyModule::class ] ) @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionLifecycleObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionLifecycleObserver.kt index d26e9861d03421a9f3f41231e51ff0f6623d2a32..cb37fbec75054ad2eff7be0f5ccf8585034865db 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionLifecycleObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionLifecycleObserver.kt @@ -27,7 +27,7 @@ internal interface SessionLifecycleObserver { Called when the session is opened */ @MainThread - fun onStart() { + fun onSessionStarted() { // noop } @@ -43,7 +43,7 @@ internal interface SessionLifecycleObserver { Called when the session is closed */ @MainThread - fun onStop() { + fun onSessionStopped() { // noop } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index 468c193ad3c55649fe0df57a7e0599f987a9d43e..f10eb67921d03d1c4398ed5f5bb250518d8955ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.api.crypto.MXCryptoConfig -import org.matrix.android.sdk.api.session.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.AccountDataService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService @@ -77,11 +77,14 @@ import org.matrix.android.sdk.internal.session.call.CallEventProcessor import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService +import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessorCoroutine import org.matrix.android.sdk.internal.session.room.tombstone.RoomTombstoneEventProcessor import org.matrix.android.sdk.internal.session.securestorage.DefaultSecureStorageService import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker @@ -338,6 +341,10 @@ internal abstract class SessionModule { @IntoSet abstract fun bindRealmSessionProvider(provider: RealmSessionProvider): SessionLifecycleObserver + @Binds + @IntoSet + abstract fun bindEventSenderProcessorAsSessionLifecycleObserver(processor: EventSenderProcessorCoroutine): SessionLifecycleObserver + @Binds abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService @@ -361,4 +368,7 @@ internal abstract class SessionModule { @Binds abstract fun bindRedactEventTask(task: DefaultRedactEventTask): RedactEventTask + + @Binds + abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt index d67b21567e8a752bab63d2e96723b409a7d91b21..ca6b0554a94c2d7d10329030d71f315c2341ea59 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt @@ -16,10 +16,9 @@ package org.matrix.android.sdk.internal.session.account +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.internal.auth.registration.handleUIA -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.cleanup.CleanupSession @@ -30,8 +29,8 @@ import javax.inject.Inject internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Unit> { data class Params( - val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, val eraseAllData: Boolean, + val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, val userAuthParam: UIABaseAuth? = null ) } @@ -39,7 +38,6 @@ internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Un internal class DefaultDeactivateAccountTask @Inject constructor( private val accountAPI: AccountAPI, private val globalErrorReceiver: GlobalErrorReceiver, - @UserId private val userId: String, private val identityDisconnectTask: IdentityDisconnectTask, private val cleanupSession: CleanupSession ) : DeactivateAccountTask { @@ -47,23 +45,33 @@ internal class DefaultDeactivateAccountTask @Inject constructor( override suspend fun execute(params: DeactivateAccountTask.Params) { val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData) - try { + val canCleanup = try { executeRequest<Unit>(globalErrorReceiver) { apiCall = accountAPI.deactivate(deactivateAccountParams) } + true } catch (throwable: Throwable) { - if (!handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth -> - execute(params.copy(userAuthParam = auth)) - } + if (!handleUIA( + failure = throwable, + interceptor = params.userInteractiveAuthInterceptor, + retryBlock = { authUpdate -> + execute(params.copy(userAuthParam = authUpdate)) + } + ) ) { Timber.d("## UIA: propagate failure") - throw throwable + throw throwable + } else { + false } } - // Logout from identity server if any, ignoring errors - runCatching { identityDisconnectTask.execute(Unit) } - .onFailure { Timber.w(it, "Unable to disconnect identity server") } - cleanupSession.handle() + if (canCleanup) { + // Logout from identity server if any, ignoring errors + runCatching { identityDisconnectTask.execute(Unit) } + .onFailure { Timber.w(it, "Unable to disconnect identity server") } + + cleanupSession.handle() + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt index 25b67159a9fa1cf4f61dda94139f61501bacab03..dc77d7bffb07b5fcf85120e35e90df3613f4628d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DefaultAccountService.kt @@ -27,7 +27,7 @@ internal class DefaultAccountService @Inject constructor(private val changePassw changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword)) } - override suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean) { - deactivateAccountTask.execute(DeactivateAccountTask.Params(userInteractiveAuthInterceptor, eraseAllData)) + override suspend fun deactivateAccount(eraseAllData: Boolean, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) { + deactivateAccountTask.execute(DeactivateAccountTask.Params(eraseAllData, userInteractiveAuthInterceptor)) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt index f789a6450059ec6b370cbbc0904739126cf0d5b6..4887351709ffde85aa2ccdb66e03cca66de8ce1a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt @@ -16,28 +16,30 @@ package org.matrix.android.sdk.internal.session.call +import io.realm.Realm 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.internal.database.model.EventInsertType -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor -import io.realm.Realm import timber.log.Timber import javax.inject.Inject -internal class CallEventProcessor @Inject constructor( - @UserId private val userId: String, - private val callService: DefaultCallSignalingService -) : EventInsertLiveProcessor { +internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler) + : EventInsertLiveProcessor { private val allowedTypes = listOf( EventType.CALL_ANSWER, + EventType.CALL_SELECT_ANSWER, + EventType.CALL_REJECT, + EventType.CALL_NEGOTIATE, EventType.CALL_CANDIDATES, EventType.CALL_INVITE, EventType.CALL_HANGUP, EventType.ENCRYPTED ) + private val eventsToPostProcess = mutableListOf<Event>() + override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean { if (insertType != EventInsertType.INCREMENTAL_SYNC) { return false @@ -46,10 +48,17 @@ internal class CallEventProcessor @Inject constructor( } override suspend fun process(realm: Realm, event: Event) { - update(realm, event) + eventsToPostProcess.add(event) } - private fun update(realm: Realm, event: Event) { + override suspend fun onPostProcess() { + eventsToPostProcess.forEach { + dispatchToCallSignalingHandlerIfNeeded(it) + } + eventsToPostProcess.clear() + } + + private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) { val now = System.currentTimeMillis() // TODO might check if an invite is not closed (hangup/answsered) in the same event batch? event.roomId ?: return Unit.also { @@ -60,10 +69,6 @@ internal class CallEventProcessor @Inject constructor( // To old to ring? return } - event.ageLocalTs - if (EventType.isCallEvent(event.getClearType())) { - callService.onCallEvent(event) - } - Timber.v("$realm : $userId") + callSignalingHandler.onCallEvent(event) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..1de2d8a106e66beeb21fec80d3abc7d85d88e2dd --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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.session.call + +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.call.CallListener +import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent +import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent +import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent +import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent +import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent +import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent + +/** + * Dispatch each method safely to all listeners. + */ +internal class CallListenersDispatcher(private val listeners: Set<CallListener>) : CallListener { + + override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) = dispatch { + it.onCallInviteReceived(mxCall, callInviteContent) + } + + override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) = dispatch { + it.onCallIceCandidateReceived(mxCall, iceCandidatesContent) + } + + override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) = dispatch { + it.onCallAnswerReceived(callAnswerContent) + } + + override fun onCallHangupReceived(callHangupContent: CallHangupContent) = dispatch { + it.onCallHangupReceived(callHangupContent) + } + + override fun onCallRejectReceived(callRejectContent: CallRejectContent) = dispatch { + it.onCallRejectReceived(callRejectContent) + } + + override fun onCallManagedByOtherSession(callId: String) = dispatch { + it.onCallManagedByOtherSession(callId) + } + + override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) = dispatch { + it.onCallSelectAnswerReceived(callSelectAnswerContent) + } + + override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) = dispatch { + it.onCallNegotiateReceived(callNegotiateContent) + } + + private fun dispatch(lambda: (CallListener) -> Unit) { + listeners.toList().forEach { + tryOrNull { + lambda(it) + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt new file mode 100644 index 0000000000000000000000000000000000000000..7e54301f63e7d27522e52bd1532bfc7a99d0b8d0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt @@ -0,0 +1,218 @@ +/* + * Copyright (c) 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.session.call + +import org.matrix.android.sdk.api.session.call.CallListener +import org.matrix.android.sdk.api.session.call.CallState +import org.matrix.android.sdk.api.session.call.MxCall +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.toModel +import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent +import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities +import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent +import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent +import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent +import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent +import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallSignallingContent +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.SessionScope +import timber.log.Timber +import java.math.BigDecimal +import javax.inject.Inject + +@SessionScope +internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler, + private val mxCallFactory: MxCallFactory, + @UserId private val userId: String) { + + private val callListeners = mutableSetOf<CallListener>() + private val callListenersDispatcher = CallListenersDispatcher(callListeners) + + fun addCallListener(listener: CallListener) { + callListeners.add(listener) + } + + fun removeCallListener(listener: CallListener) { + callListeners.remove(listener) + } + + fun onCallEvent(event: Event) { + when (event.getClearType()) { + EventType.CALL_ANSWER -> { + handleCallAnswerEvent(event) + } + EventType.CALL_INVITE -> { + handleCallInviteEvent(event) + } + EventType.CALL_HANGUP -> { + handleCallHangupEvent(event) + } + EventType.CALL_REJECT -> { + handleCallRejectEvent(event) + } + EventType.CALL_CANDIDATES -> { + handleCallCandidatesEvent(event) + } + EventType.CALL_SELECT_ANSWER -> { + handleCallSelectAnswerEvent(event) + } + EventType.CALL_NEGOTIATE -> { + handleCallNegotiateEvent(event) + } + } + } + + private fun handleCallNegotiateEvent(event: Event) { + val content = event.getClearContent().toModel<CallNegotiateContent>() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo + return + } + callListenersDispatcher.onCallNegotiateReceived(content) + } + + private fun handleCallSelectAnswerEvent(event: Event) { + val content = event.getClearContent().toModel<CallSelectAnswerContent>() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo + return + } + if (call.isOutgoing) { + Timber.v("Got selectAnswer for an outbound call: ignoring") + return + } + val selectedPartyId = content.selectedPartyId + if (selectedPartyId == null) { + Timber.w("Got nonsensical select_answer with null selected_party_id: ignoring") + return + } + callListenersDispatcher.onCallSelectAnswerReceived(content) + } + + private fun handleCallCandidatesEvent(event: Event) { + val content = event.getClearContent().toModel<CallCandidatesContent>() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo + return + } + if (call.opponentPartyId != null && !call.partyIdsMatches(content)) { + Timber.v("Ignoring candidates from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}") + return + } + callListenersDispatcher.onCallIceCandidateReceived(call, content) + } + + private fun handleCallRejectEvent(event: Event) { + val content = event.getClearContent().toModel<CallRejectContent>() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo + return + } + activeCallHandler.removeCall(content.callId) + if (event.senderId == userId) { + // discard current call, it's rejected by another of my session + callListenersDispatcher.onCallManagedByOtherSession(content.callId) + return + } + // No need to check party_id for reject because if we'd received either + // an answer or reject, we wouldn't be in state InviteSent + if (call.state != CallState.Dialing) { + return + } + callListenersDispatcher.onCallRejectReceived(content) + } + + private fun handleCallHangupEvent(event: Event) { + val content = event.getClearContent().toModel<CallHangupContent>() ?: return + val call = content.getCall() ?: return + // party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen + // a partner yet but we're treating the hangup as a reject as per VoIP v0) + if (call.opponentPartyId != null && !call.partyIdsMatches(content)) { + Timber.v("Ignoring hangup from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}") + return + } + if (call.state != CallState.Terminated) { + activeCallHandler.removeCall(content.callId) + callListenersDispatcher.onCallHangupReceived(content) + } + } + + private fun handleCallInviteEvent(event: Event) { + if (event.senderId == userId) { + // ignore invites you send + return + } + if (event.roomId == null || event.senderId == null) { + return + } + val content = event.getClearContent().toModel<CallInviteContent>() ?: return + val incomingCall = mxCallFactory.createIncomingCall( + roomId = event.roomId, + opponentUserId = event.senderId, + content = content + ) ?: return + activeCallHandler.addCall(incomingCall) + callListenersDispatcher.onCallInviteReceived(incomingCall, content) + } + + private fun handleCallAnswerEvent(event: Event) { + val content = event.getClearContent().toModel<CallAnswerContent>() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo + return + } + if (event.senderId == userId) { + // discard current call, it's answered by another of my session + activeCallHandler.removeCall(call.callId) + callListenersDispatcher.onCallManagedByOtherSession(content.callId) + } else { + if (call.opponentPartyId != null) { + Timber.v("Ignoring answer from party ID ${content.partyId} we already have an answer from ${call.opponentPartyId}") + return + } + call.apply { + opponentPartyId = Optional.from(content.partyId) + opponentVersion = content.version?.let { BigDecimal(it).intValueExact() } ?: MxCall.VOIP_PROTO_VERSION + capabilities = content.capabilities ?: CallCapabilities() + } + callListenersDispatcher.onCallAnswerReceived(content) + } + } + + private fun MxCall.partyIdsMatches(contentSignallingContent: CallSignallingContent): Boolean { + return opponentPartyId?.getOrNull() == contentSignallingContent.partyId + } + + private fun CallSignallingContent.getCall(): MxCall? { + val currentCall = callId?.let { + activeCallHandler.getCallWithId(it) + } + if (currentCall == null) { + Timber.v("Call with id $callId is null") + } + return currentCall + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt index 019da27d27efc20511c2f2e8eb4bba50cfeba2c8..7d046cb6422e15e0028653e16fa395ed27770919 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt @@ -16,106 +16,44 @@ package org.matrix.android.sdk.internal.session.call -import android.os.SystemClock -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.CallSignalingService -import org.matrix.android.sdk.api.session.call.CallState -import org.matrix.android.sdk.api.session.call.CallsListener import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker import org.matrix.android.sdk.api.session.call.TurnServerResponse -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.toModel -import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent -import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent -import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent -import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.session.call.model.MxCallImpl -import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor -import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import timber.log.Timber -import java.util.UUID import javax.inject.Inject @SessionScope internal class DefaultCallSignalingService @Inject constructor( - @UserId - private val userId: String, + private val callSignalingHandler: CallSignalingHandler, + private val mxCallFactory: MxCallFactory, private val activeCallHandler: ActiveCallHandler, - private val localEchoEventFactory: LocalEchoEventFactory, - private val eventSenderProcessor: EventSenderProcessor, - private val taskExecutor: TaskExecutor, - private val turnServerTask: GetTurnServerTask + private val turnServerDataSource: TurnServerDataSource, + private val pstnProtocolChecker: PSTNProtocolChecker ) : CallSignalingService { - private val callListeners = mutableSetOf<CallsListener>() - - private val cachedTurnServerResponse = object { - // Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it. - private val MIN_TTL = 60 - - private val now = { SystemClock.elapsedRealtime() / 1000 } - - private var expiresAt: Long = 0 - - var data: TurnServerResponse? = null - get() = if (expiresAt > now()) field else null - set(value) { - expiresAt = now() + (value?.ttl ?: 0) - MIN_TTL - field = value - } + override suspend fun getTurnServer(): TurnServerResponse { + return turnServerDataSource.getTurnServer() } - override fun getTurnServer(callback: MatrixCallback<TurnServerResponse>): Cancelable { - if (cachedTurnServerResponse.data != null) { - cachedTurnServerResponse.data?.let { callback.onSuccess(it) } - return NoOpCancellable - } - return turnServerTask - .configureWith(GetTurnServerTask.Params) { - this.callback = object : MatrixCallback<TurnServerResponse> { - override fun onSuccess(data: TurnServerResponse) { - cachedTurnServerResponse.data = data - callback.onSuccess(data) - } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - } - } - .executeBy(taskExecutor) + override fun getPSTNProtocolChecker(): PSTNProtocolChecker { + return pstnProtocolChecker } override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall { - val call = MxCallImpl( - callId = UUID.randomUUID().toString(), - isOutgoing = true, - roomId = roomId, - userId = userId, - otherUserId = otherUserId, - isVideoCall = isVideoCall, - localEchoEventFactory = localEchoEventFactory, - eventSenderProcessor = eventSenderProcessor - ) - activeCallHandler.addCall(call).also { - return call + return mxCallFactory.createOutgoingCall(roomId, otherUserId, isVideoCall).also { + activeCallHandler.addCall(it) } } - override fun addCallListener(listener: CallsListener) { - callListeners.add(listener) + override fun addCallListener(listener: CallListener) { + callSignalingHandler.addCallListener(listener) } - override fun removeCallListener(listener: CallsListener) { - callListeners.remove(listener) + override fun removeCallListener(listener: CallListener) { + callSignalingHandler.removeCallListener(listener) } override fun getCallWithId(callId: String): MxCall? { @@ -127,129 +65,6 @@ internal class DefaultCallSignalingService @Inject constructor( return activeCallHandler.getActiveCallsLiveData().value?.isNotEmpty() == true } - internal fun onCallEvent(event: Event) { - when (event.getClearType()) { - EventType.CALL_ANSWER -> { - event.getClearContent().toModel<CallAnswerContent>()?.let { - if (event.senderId == userId) { - // ok it's an answer from me.. is it remote echo or other session - val knownCall = getCallWithId(it.callId) - if (knownCall == null) { - Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${it.callId} send by me") - } else if (!knownCall.isOutgoing) { - // incoming call - // if it was anwsered by this session, the call state would be in Answering(or connected) state - if (knownCall.state == CallState.LocalRinging) { - // discard current call, it's answered by another of my session - onCallManageByOtherSession(it.callId) - } - } - return - } - - onCallAnswer(it) - } - } - EventType.CALL_INVITE -> { - if (event.senderId == userId) { - // Always ignore local echos of invite - return - } - - event.getClearContent().toModel<CallInviteContent>()?.let { content -> - val incomingCall = MxCallImpl( - callId = content.callId ?: return@let, - isOutgoing = false, - roomId = event.roomId ?: return@let, - userId = userId, - otherUserId = event.senderId ?: return@let, - isVideoCall = content.isVideo(), - localEchoEventFactory = localEchoEventFactory, - eventSenderProcessor = eventSenderProcessor - ) - activeCallHandler.addCall(incomingCall) - onCallInvite(incomingCall, content) - } - } - EventType.CALL_HANGUP -> { - event.getClearContent().toModel<CallHangupContent>()?.let { content -> - - if (event.senderId == userId) { - // ok it's an answer from me.. is it remote echo or other session - val knownCall = getCallWithId(content.callId) - if (knownCall == null) { - Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${content.callId} send by me") - } else if (!knownCall.isOutgoing) { - // incoming call - if (knownCall.state == CallState.LocalRinging) { - // discard current call, it's answered by another of my session - onCallManageByOtherSession(content.callId) - } - } - return - } - - activeCallHandler.removeCall(content.callId) - onCallHangup(content) - } - } - EventType.CALL_CANDIDATES -> { - if (event.senderId == userId) { - // Always ignore local echos of invite - return - } - event.getClearContent().toModel<CallCandidatesContent>()?.let { content -> - activeCallHandler.getCallWithId(content.callId)?.let { - onCallIceCandidate(it, content) - } - } - } - } - } - - private fun onCallHangup(hangup: CallHangupContent) { - callListeners.toList().forEach { - tryOrNull { - it.onCallHangupReceived(hangup) - } - } - } - - private fun onCallAnswer(answer: CallAnswerContent) { - callListeners.toList().forEach { - tryOrNull { - it.onCallAnswerReceived(answer) - } - } - } - - private fun onCallManageByOtherSession(callId: String) { - callListeners.toList().forEach { - tryOrNull { - it.onCallManagedByOtherSession(callId) - } - } - } - - private fun onCallInvite(incomingCall: MxCall, invite: CallInviteContent) { - // Ignore the invitation from current user - if (incomingCall.otherUserId == userId) return - - callListeners.toList().forEach { - tryOrNull { - it.onCallInviteReceived(incomingCall, invite) - } - } - } - - private fun onCallIceCandidate(incomingCall: MxCall, candidates: CallCandidatesContent) { - callListeners.toList().forEach { - tryOrNull { - it.onCallIceCandidateReceived(incomingCall, candidates) - } - } - } - companion object { const val CALL_TIMEOUT_MS = 120_000 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..b14cdca63c5c6e059600b8e635019afdc6248a82 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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.session.call + +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities +import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.di.DeviceId +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.call.model.MxCallImpl +import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import java.math.BigDecimal +import java.util.UUID +import javax.inject.Inject + +internal class MxCallFactory @Inject constructor( + @DeviceId private val deviceId: String?, + private val localEchoEventFactory: LocalEchoEventFactory, + private val eventSenderProcessor: EventSenderProcessor, + private val matrixConfiguration: MatrixConfiguration, + private val getProfileInfoTask: GetProfileInfoTask, + @UserId private val userId: String +) { + + fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? { + content.callId ?: return null + return MxCallImpl( + callId = content.callId, + isOutgoing = false, + roomId = roomId, + userId = userId, + ourPartyId = deviceId ?: "", + opponentUserId = opponentUserId, + isVideoCall = content.isVideo(), + localEchoEventFactory = localEchoEventFactory, + eventSenderProcessor = eventSenderProcessor, + matrixConfiguration = matrixConfiguration, + getProfileInfoTask = getProfileInfoTask + ).apply { + opponentPartyId = Optional.from(content.partyId) + opponentVersion = content.version?.let { BigDecimal(it).intValueExact() } ?: MxCall.VOIP_PROTO_VERSION + capabilities = content.capabilities ?: CallCapabilities() + } + } + + fun createOutgoingCall(roomId: String, opponentUserId: String, isVideoCall: Boolean): MxCall { + return MxCallImpl( + callId = UUID.randomUUID().toString(), + isOutgoing = true, + roomId = roomId, + userId = userId, + ourPartyId = deviceId ?: "", + opponentUserId = opponentUserId, + isVideoCall = isVideoCall, + localEchoEventFactory = localEchoEventFactory, + eventSenderProcessor = eventSenderProcessor, + matrixConfiguration = matrixConfiguration, + getProfileInfoTask = getProfileInfoTask + ) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/TurnServerDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/TurnServerDataSource.kt new file mode 100644 index 0000000000000000000000000000000000000000..8e2ac5e17e7626dc04ef64d1285d08d1d8cbc24c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/TurnServerDataSource.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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.session.call + +import android.os.SystemClock +import org.matrix.android.sdk.api.session.call.TurnServerResponse +import javax.inject.Inject + +internal class TurnServerDataSource @Inject constructor(private val turnServerTask: GetTurnServerTask) { + + private val cachedTurnServerResponse = object { + // Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it. + private val MIN_TTL = 60 + + private val now = { SystemClock.elapsedRealtime() / 1000 } + + private var expiresAt: Long = 0 + + var data: TurnServerResponse? = null + get() = if (expiresAt > now()) field else null + set(value) { + expiresAt = now() + (value?.ttl ?: 0) - MIN_TTL + field = value + } + } + + suspend fun getTurnServer(): TurnServerResponse { + return cachedTurnServerResponse.data ?: turnServerTask.execute(GetTurnServerTask.Params).also { + cachedTurnServerResponse.data = it + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt index 6c0d437a60103d9d4d79a6f08218e2e15d84324a..88fba0ea859a3ed43c5fa91d73fbf159912a6303 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.call.model +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.events.model.Content @@ -24,28 +25,44 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.LocalEcho import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallCandidate import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent +import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent +import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent +import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent +import org.matrix.android.sdk.api.session.room.model.call.CallReplacesContent +import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.SdpType +import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService -import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory -import org.webrtc.IceCandidate -import org.webrtc.SessionDescription +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor import timber.log.Timber +import java.util.UUID internal class MxCallImpl( override val callId: String, override val isOutgoing: Boolean, override val roomId: String, private val userId: String, - override val otherUserId: String, + override val opponentUserId: String, override val isVideoCall: Boolean, + override val ourPartyId: String, private val localEchoEventFactory: LocalEchoEventFactory, - private val eventSenderProcessor: EventSenderProcessor + private val eventSenderProcessor: EventSenderProcessor, + private val matrixConfiguration: MatrixConfiguration, + private val getProfileInfoTask: GetProfileInfoTask ) : MxCall { + override var opponentPartyId: Optional<String>? = null + override var opponentVersion: Int = MxCall.VOIP_PROTO_VERSION + override var capabilities: CallCapabilities? = null + override var state: CallState = CallState.Idle set(value) { field = value @@ -81,60 +98,135 @@ internal class MxCallImpl( } } - override fun offerSdp(sdp: SessionDescription) { + override fun offerSdp(sdpString: String) { if (!isOutgoing) return Timber.v("## VOIP offerSdp $callId") state = CallState.Dialing CallInviteContent( callId = callId, + partyId = ourPartyId, lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS, - offer = CallInviteContent.Offer(sdp = sdp.description) + offer = CallInviteContent.Offer(sdp = sdpString), + version = MxCall.VOIP_PROTO_VERSION.toString(), + capabilities = buildCapabilities() ) .let { createEventAndLocalEcho(type = EventType.CALL_INVITE, roomId = roomId, content = it.toContent()) } .also { eventSenderProcessor.postEvent(it) } } - override fun sendLocalIceCandidates(candidates: List<IceCandidate>) { + override fun sendLocalCallCandidates(candidates: List<CallCandidate>) { + Timber.v("Send local call canditates $callId: $candidates") CallCandidatesContent( callId = callId, - candidates = candidates.map { - CallCandidatesContent.Candidate( - sdpMid = it.sdpMid, - sdpMLineIndex = it.sdpMLineIndex, - candidate = it.sdp - ) - } + partyId = ourPartyId, + candidates = candidates, + version = MxCall.VOIP_PROTO_VERSION.toString() ) .let { createEventAndLocalEcho(type = EventType.CALL_CANDIDATES, roomId = roomId, content = it.toContent()) } .also { eventSenderProcessor.postEvent(it) } } - override fun sendLocalIceCandidateRemovals(candidates: List<IceCandidate>) { + override fun sendLocalIceCandidateRemovals(candidates: List<CallCandidate>) { // For now we don't support this flow } - override fun hangUp() { + override fun reject() { + if (opponentVersion < 1) { + Timber.v("Opponent version is less than 1 ($opponentVersion): sending hangup instead of reject") + hangUp() + return + } + Timber.v("## VOIP reject $callId") + CallRejectContent( + callId = callId, + partyId = ourPartyId, + version = MxCall.VOIP_PROTO_VERSION.toString() + ) + .let { createEventAndLocalEcho(type = EventType.CALL_REJECT, roomId = roomId, content = it.toContent()) } + .also { eventSenderProcessor.postEvent(it) } + state = CallState.Terminated + } + + override fun hangUp(reason: CallHangupContent.Reason?) { Timber.v("## VOIP hangup $callId") CallHangupContent( - callId = callId + callId = callId, + partyId = ourPartyId, + reason = reason ?: CallHangupContent.Reason.USER_HANGUP, + version = MxCall.VOIP_PROTO_VERSION.toString() ) .let { createEventAndLocalEcho(type = EventType.CALL_HANGUP, roomId = roomId, content = it.toContent()) } .also { eventSenderProcessor.postEvent(it) } state = CallState.Terminated } - override fun accept(sdp: SessionDescription) { + override fun accept(sdpString: String) { Timber.v("## VOIP accept $callId") if (isOutgoing) return state = CallState.Answering CallAnswerContent( callId = callId, - answer = CallAnswerContent.Answer(sdp = sdp.description) + partyId = ourPartyId, + answer = CallAnswerContent.Answer(sdp = sdpString), + version = MxCall.VOIP_PROTO_VERSION.toString(), + capabilities = buildCapabilities() ) .let { createEventAndLocalEcho(type = EventType.CALL_ANSWER, roomId = roomId, content = it.toContent()) } .also { eventSenderProcessor.postEvent(it) } } + override fun negotiate(sdpString: String, type: SdpType) { + Timber.v("## VOIP negotiate $callId") + CallNegotiateContent( + callId = callId, + partyId = ourPartyId, + lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS, + description = CallNegotiateContent.Description(sdp = sdpString, type = type), + version = MxCall.VOIP_PROTO_VERSION.toString() + ) + .let { createEventAndLocalEcho(type = EventType.CALL_NEGOTIATE, roomId = roomId, content = it.toContent()) } + .also { eventSenderProcessor.postEvent(it) } + } + + override fun selectAnswer() { + Timber.v("## VOIP select answer $callId") + if (isOutgoing) return + state = CallState.Answering + CallSelectAnswerContent( + callId = callId, + partyId = ourPartyId, + selectedPartyId = opponentPartyId?.getOrNull(), + version = MxCall.VOIP_PROTO_VERSION.toString() + ) + .let { createEventAndLocalEcho(type = EventType.CALL_SELECT_ANSWER, roomId = roomId, content = it.toContent()) } + .also { eventSenderProcessor.postEvent(it) } + } + + override suspend fun transfer(targetUserId: String, targetRoomId: String?) { + val profileInfoParams = GetProfileInfoTask.Params(targetUserId) + val profileInfo = try { + getProfileInfoTask.execute(profileInfoParams) + } catch (failure: Throwable) { + Timber.v("Fail fetching profile info of $targetUserId while transferring call") + null + } + CallReplacesContent( + callId = callId, + partyId = ourPartyId, + replacementId = UUID.randomUUID().toString(), + version = MxCall.VOIP_PROTO_VERSION.toString(), + targetUser = CallReplacesContent.TargetUser( + id = targetUserId, + displayName = profileInfo?.get(ProfileService.DISPLAY_NAME_KEY) as? String, + avatarUrl = profileInfo?.get(ProfileService.AVATAR_URL_KEY) as? String + ), + targerRoomId = targetRoomId, + createCall = UUID.randomUUID().toString() + ) + .let { createEventAndLocalEcho(type = EventType.CALL_REPLACES, roomId = roomId, content = it.toContent()) } + .also { eventSenderProcessor.postEvent(it) } + } + private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event { return Event( roomId = roomId, @@ -147,4 +239,12 @@ internal class MxCallImpl( ) .also { localEchoEventFactory.createLocalEcho(it) } } + + private fun buildCapabilities(): CallCapabilities? { + return if (matrixConfiguration.supportsCallTransfer) { + CallCapabilities(true) + } else { + null + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt index 2eac0a5059ca147819aacc3dd05bd28c89cfa495..7415b988a43eadd878a12006e35328445aea2da5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt @@ -33,11 +33,11 @@ internal object FilterFactory { return FilterUtil.enableLazyLoading(Filter(), true) } - fun createRiotFilter(): Filter { + fun createElementFilter(): Filter { return Filter( room = RoomFilter( - timeline = createRiotTimelineFilter(), - state = createRiotStateFilter() + timeline = createElementTimelineFilter(), + state = createElementStateFilter() ) ) } @@ -48,7 +48,7 @@ internal object FilterFactory { ) } - fun createRiotRoomFilter(): RoomEventFilter { + fun createElementRoomFilter(): RoomEventFilter { return RoomEventFilter( lazyLoadMembers = true // TODO Enable this for optimization @@ -56,26 +56,26 @@ internal object FilterFactory { ) } - private fun createRiotTimelineFilter(): RoomEventFilter { - return RoomEventFilter().apply { + private fun createElementTimelineFilter(): RoomEventFilter? { + return null // RoomEventFilter().apply { // TODO Enable this for optimization // types = listOfSupportedEventTypes.toMutableList() - } + // } } - private fun createRiotStateFilter(): RoomEventFilter { + private fun createElementStateFilter(): RoomEventFilter { return RoomEventFilter( lazyLoadMembers = true ) } - // Get only managed types by Riot + // Get only managed types by Element private val listOfSupportedEventTypes = listOf( // TODO Complete the list EventType.MESSAGE ) - // Get only managed types by Riot + // Get only managed types by Element private val listOfSupportedStateEventTypes = listOf( // TODO Complete the list EventType.STATE_ROOM_MEMBER diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt index da747934e2676173cc23d067b41e75d58dbc1853..d42962d54ace83a4874b7cc3707fec818b3eb7c5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt @@ -42,18 +42,18 @@ internal class DefaultSaveFilterTask @Inject constructor( override suspend fun execute(params: SaveFilterTask.Params) { val filterBody = when (params.filterPreset) { - FilterService.FilterPreset.RiotFilter -> { - FilterFactory.createRiotFilter() + FilterService.FilterPreset.ElementFilter -> { + FilterFactory.createElementFilter() } - FilterService.FilterPreset.NoFilter -> { + FilterService.FilterPreset.NoFilter -> { FilterFactory.createDefaultFilter() } } val roomFilter = when (params.filterPreset) { - FilterService.FilterPreset.RiotFilter -> { - FilterFactory.createRiotRoomFilter() + FilterService.FilterPreset.ElementFilter -> { + FilterFactory.createElementRoomFilter() } - FilterService.FilterPreset.NoFilter -> { + FilterService.FilterPreset.NoFilter -> { FilterFactory.createDefaultRoomFilter() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt index 27396aac80137c1beb0d730b9d350da7f2445055..0ed690d972e829f85798daa7b27f8efc84e85e57 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt @@ -17,16 +17,23 @@ package org.matrix.android.sdk.internal.session.homeserver import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.internal.database.mapper.HomeServerCapabilitiesMapper import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity import org.matrix.android.sdk.internal.database.query.get import org.matrix.android.sdk.internal.di.SessionDatabase -import io.realm.Realm import javax.inject.Inject -internal class DefaultHomeServerCapabilitiesService @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : HomeServerCapabilitiesService { +internal class DefaultHomeServerCapabilitiesService @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, + private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask +) : HomeServerCapabilitiesService { + + override suspend fun refreshHomeServerCapabilities() { + getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = true)) + } override fun getHomeServerCapabilities(): HomeServerCapabilities { return Realm.getInstance(monarchy.realmConfiguration).use { realm -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 845cfb392e5730b1af63e50e85b30df97cc804a5..84c9132d6136d7051b8ede3ce2904a9cd94d1cb1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -38,7 +38,11 @@ import timber.log.Timber import java.util.Date import javax.inject.Inject -internal interface GetHomeServerCapabilitiesTask : Task<Unit, Unit> +internal interface GetHomeServerCapabilitiesTask : Task<GetHomeServerCapabilitiesTask.Params, Unit> { + data class Params( + val forceRefresh: Boolean + ) +} internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( private val capabilitiesAPI: CapabilitiesAPI, @@ -52,12 +56,14 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( private val userId: String ) : GetHomeServerCapabilitiesTask { - override suspend fun execute(params: Unit) { - var doRequest = false - monarchy.awaitTransaction { realm -> - val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) + override suspend fun execute(params: GetHomeServerCapabilitiesTask.Params) { + var doRequest = params.forceRefresh + if (!doRequest) { + monarchy.awaitTransaction { realm -> + val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) - doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time + doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time + } } if (!doRequest) { @@ -123,7 +129,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( } companion object { - // 8 hours like on Riot Web + // 8 hours like on Element Web private const val MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS = 8 * 60 * 60 * 1000 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index c6fb34151c8dbba34843129168fae7ea63b996b7..948e387cb18c47a607e2a11a77470a4e60dda6bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -92,7 +92,7 @@ internal class DefaultIdentityService @Inject constructor( private val listeners = mutableSetOf<IdentityServiceListener>() - override fun onStart() { + override fun onSessionStarted() { lifecycleRegistry.currentState = Lifecycle.State.STARTED // Observe the account data change accountDataDataSource @@ -117,7 +117,7 @@ internal class DefaultIdentityService @Inject constructor( } } - override fun onStop() { + override fun onSessionStopped() { lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt new file mode 100644 index 0000000000000000000000000000000000000000..eb3e3066b128ee15d6e5af97ad01eef954ece171 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt @@ -0,0 +1,94 @@ +/* + * 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.session.initsync + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import org.matrix.android.sdk.api.session.initsync.InitSyncStep +import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.internal.session.SessionScope +import javax.inject.Inject + +@SessionScope +internal class DefaultInitialSyncProgressService @Inject constructor() + : InitialSyncProgressService, + ProgressReporter { + + private val status = MutableLiveData<InitialSyncProgressService.Status>() + + private var rootTask: TaskInfo? = null + + override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> { + return status + } + + /** + * Create a rootTask + */ + fun startRoot(initSyncStep: InitSyncStep, + totalProgress: Int) { + endAll() + rootTask = TaskInfo(initSyncStep, totalProgress, null, 1F) + reportProgress(0F) + } + + /** + * Add a child to the leaf + */ + override fun startTask(initSyncStep: InitSyncStep, + totalProgress: Int, + parentWeight: Float) { + val currentLeaf = rootTask?.leaf() ?: return + currentLeaf.child = TaskInfo( + initSyncStep = initSyncStep, + totalProgress = totalProgress, + parent = currentLeaf, + parentWeight = parentWeight + ) + reportProgress(0F) + } + + override fun reportProgress(progress: Float) { + rootTask?.let { root -> + root.leaf().let { leaf -> + // Update the progress of the leaf and all its parents + leaf.setProgress(progress) + // Then update the live data using leaf wording and root progress + status.postValue(InitialSyncProgressService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt())) + } + } + } + + override fun endTask() { + rootTask?.leaf()?.let { endedTask -> + // Ensure the task progress is complete + reportProgress(endedTask.totalProgress.toFloat()) + endedTask.parent?.child = null + + if (endedTask.parent != null) { + // And close it + endedTask.parent.child = null + } else { + status.postValue(InitialSyncProgressService.Status.Idle) + } + } + } + + fun endAll() { + rootTask = null + status.postValue(InitialSyncProgressService.Status.Idle) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/Extensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..b40b1a56bfefd4c1a81271ef36477386b3129c95 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/Extensions.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2021 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.session.initsync + +import org.matrix.android.sdk.api.session.initsync.InitSyncStep + +internal inline fun <T> reportSubtask(reporter: ProgressReporter?, + initSyncStep: InitSyncStep, + totalProgress: Int, + parentWeight: Float, + block: () -> T): T { + reporter?.startTask(initSyncStep, totalProgress, parentWeight) + return block().also { + reporter?.endTask() + } +} + +internal inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: ProgressReporter?, + initSyncStep: InitSyncStep, + parentWeight: Float, + transform: (Map.Entry<K, V>) -> R): List<R> { + var current = 0F + reporter?.startTask(initSyncStep, count() + 1, parentWeight) + return map { + reporter?.reportProgress(current) + current++ + transform.invoke(it) + }.also { + reporter?.endTask() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/ProgressReporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/ProgressReporter.kt new file mode 100644 index 0000000000000000000000000000000000000000..8a7b26b4b8fbe6d9791e044a44af83fa2e4352f7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/ProgressReporter.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 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.session.initsync + +import org.matrix.android.sdk.api.session.initsync.InitSyncStep + +internal interface ProgressReporter { + fun startTask(initSyncStep: InitSyncStep, + totalProgress: Int, + parentWeight: Float) + + fun reportProgress(progress: Float) + + fun endTask() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/TaskInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/TaskInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..3e4cce2e1fbbc79bbddf8ecebe2c246673ecb655 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/TaskInfo.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2021 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.session.initsync + +import org.matrix.android.sdk.api.session.initsync.InitSyncStep +import timber.log.Timber + +internal class TaskInfo(val initSyncStep: InitSyncStep, + val totalProgress: Int, + val parent: TaskInfo?, + val parentWeight: Float) { + var child: TaskInfo? = null + var currentProgress = 0F + private set + private val offset = parent?.currentProgress ?: 0F + + /** + * Get the further child + */ + fun leaf(): TaskInfo { + var last = this + while (last.child != null) { + last = last.child!! + } + return last + } + + /** + * Set progress of this task and update the parent progress iteratively + */ + fun setProgress(progress: Float) { + Timber.v("setProgress: $progress / $totalProgress") + currentProgress = progress + + parent?.let { + val parentProgress = (currentProgress / totalProgress) * (parentWeight * it.totalProgress) + it.setProgress(offset + parentProgress) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt index ebd57ce657a95cb169665eb9a48722ac803d1f80..e34615d269dd8e097115f45ba8c3893908dace55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt @@ -37,7 +37,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataS import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence -import org.matrix.android.sdk.internal.task.TaskExecutor import timber.log.Timber import javax.inject.Inject @@ -55,7 +54,6 @@ import javax.inject.Inject */ @SessionScope internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration, - private val taskExecutor: TaskExecutor, @SessionDatabase private val monarchy: Monarchy, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val accountDataDataSource: AccountDataDataSource, @@ -79,7 +77,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri currentConfigs.add(defaultConfig) } - override fun onStart() { + override fun onSessionStarted() { lifecycleRegistry.currentState = Lifecycle.State.STARTED observeWellknownConfig() accountDataDataSource @@ -107,7 +105,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri } } - override fun onStop() { + override fun onSessionStopped() { lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt index a218f3f93cd6862061179a9d329ccec90095f5d4..d85e471f1d1945ef42a87793c928df6f488a76c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction +import org.matrix.android.sdk.internal.util.unescapeHtml import java.util.Date import javax.inject.Inject @@ -73,9 +74,9 @@ internal class DefaultGetPreviewUrlTask @Inject constructor( private fun JsonDict.toPreviewUrlData(url: String): PreviewUrlData { return PreviewUrlData( url = (get("og:url") as? String) ?: url, - siteName = get("og:site_name") as? String, - title = get("og:title") as? String, - description = get("og:description") as? String, + siteName = (get("og:site_name") as? String)?.unescapeHtml(), + title = (get("og:title") as? String)?.unescapeHtml(), + description = (get("og:description") as? String)?.unescapeHtml(), mxcUrl = get("og:image") as? String ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt index 6137b4152cb48582d06ae80eac6fdb6aca3b8fdb..d1fb5b98ffacd0ec5ddf05cc374bec2372301b07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt @@ -21,6 +21,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent +import org.matrix.android.sdk.api.session.room.timeline.isReply +import org.matrix.android.sdk.api.util.ContentUtils import javax.inject.Inject internal class UrlsExtractor @Inject constructor() { @@ -35,7 +37,14 @@ internal class UrlsExtractor @Inject constructor() { || it.msgType == MessageType.MSGTYPE_NOTICE || it.msgType == MessageType.MSGTYPE_EMOTE } - ?.body + ?.let { messageContent -> + if (event.isReply()) { + // This is a reply, strip the reply fallback + ContentUtils.extractUsefulTextFromReply(messageContent.body) + } else { + messageContent.body + } + } ?.let { urlRegex.findAll(it) } ?.map { it.value } ?.filter { it.startsWith("https://") || it.startsWith("http://") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index 7763251a01f056abdf01b951fe49952af80f9e0e..54883b51e663a0672d3500d00050780d90e1675f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -80,7 +80,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor( val allRedactedEvents = params.syncResponse.join .asSequence() - .mapNotNull { (_, value) -> value.timeline?.events } + .mapNotNull { it.value.timeline?.events } .flatten() .filter { it.type == EventType.REDACTION } .mapNotNull { it.redacts } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt index 916a6029360f7e7503aa15e2e15d08f2842bc747..c2a38af0938a17ed1e64335191a375912c5de5d2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt @@ -26,7 +26,6 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task @@ -47,11 +46,12 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor( private val profileAPI: ProfileAPI, @SessionDatabase private val monarchy: Monarchy, private val pendingThreePidMapper: PendingThreePidMapper, - @UserId private val userId: String, private val globalErrorReceiver: GlobalErrorReceiver) : FinalizeAddingThreePidTask() { override suspend fun execute(params: Params) { - if (params.userWantsToCancel.not()) { + val canCleanup = if (params.userWantsToCancel) { + true + } else { // Get the required pending data val pendingThreePids = monarchy.fetchAllMappedSync( { it.where(PendingThreePidEntity::class.java) }, @@ -69,21 +69,30 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor( ) apiCall = profileAPI.finalizeAddThreePid(body) } + true } catch (throwable: Throwable) { if (params.userInteractiveAuthInterceptor == null - || !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth -> - execute(params.copy(userAuthParam = auth)) - } + || !handleUIA( + failure = throwable, + interceptor = params.userInteractiveAuthInterceptor, + retryBlock = { authUpdate -> + execute(params.copy(userAuthParam = authUpdate)) + } + ) ) { Timber.d("## UIA: propagate failure") - throw throwable.toRegistrationFlowResponse() + throw throwable.toRegistrationFlowResponse() ?.let { Failure.RegistrationFlowError(it) } ?: throwable + } else { + false } } } - cleanupDatabase(params) + if (canCleanup) { + cleanupDatabase(params) + } } private suspend fun cleanupDatabase(params: Params) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt index 4c7d370446ec41ed82efdf4dcefa52b45022993b..125c8f0022d6dad1301fbfb9eb3049ca6baa1a89 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt @@ -19,6 +19,7 @@ import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.pushers.PusherState import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.PusherEntity +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest @@ -41,7 +42,8 @@ internal class DefaultGetPushersTask @Inject constructor( monarchy.awaitTransaction { realm -> // clear existings? realm.where(PusherEntity::class.java) - .findAll().deleteAllFromRealm() + .findAll() + .forEach { it.deleteOnCascade() } response.pushers?.forEach { jsonPusher -> jsonPusher.toEntity().also { it.state = PusherState.REGISTERED diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt index 6ba769a3b75bfd72aeb79aafb454599e81b816c6..6a4b891ecffe36b6735b68ace3354e9425939da5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.pushrules.RuleSetKey import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper import org.matrix.android.sdk.internal.database.model.PushRulesEntity +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction @@ -40,7 +41,7 @@ internal class DefaultSavePushRulesTask @Inject constructor(@SessionDatabase pri // clear current push rules realm.where(PushRulesEntity::class.java) .findAll() - .deleteAllFromRealm() + .forEach { it.deleteOnCascade() } // Save only global rules for the moment val globalRules = params.pushRules.global diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 7a819250cfda39f0c7735e468667c7cd20351355..8e817ec31a0dcb4537bbf4a4422207bfaefd16fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -45,6 +45,7 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSourc import org.matrix.android.sdk.internal.session.search.SearchTask import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith +import org.matrix.android.sdk.internal.util.awaitCallback import java.security.InvalidParameterException import javax.inject.Inject @@ -104,6 +105,12 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, return cryptoService.shouldEncryptForInvitedMembers(roomId) } + override suspend fun prepareToEncrypt() { + awaitCallback<Unit> { + cryptoService.prepareToEncrypt(roomId, it) + } + } + override suspend fun enableEncryption(algorithm: String) { when { isEncrypted() -> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt index 0d41c6f35eb256b8f54ef0bf8a6b92a56deadcbd..218d846afb5cf3e9452826f7512c02177093eec7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt @@ -16,44 +16,24 @@ package org.matrix.android.sdk.internal.session.room -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse -import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask -import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import javax.inject.Inject internal class DefaultRoomDirectoryService @Inject constructor( private val getPublicRoomTask: GetPublicRoomTask, - private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask, - private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask, - private val taskExecutor: TaskExecutor) : RoomDirectoryService { + private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask +) : RoomDirectoryService { - override fun getPublicRooms(server: String?, - publicRoomsParams: PublicRoomsParams, - callback: MatrixCallback<PublicRoomsResponse>): Cancelable { - return getPublicRoomTask - .configureWith(GetPublicRoomTask.Params(server, publicRoomsParams)) { - this.callback = callback - } - .executeBy(taskExecutor) - } - - override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable { - return getThirdPartyProtocolsTask - .configureWith { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun getPublicRooms(server: String?, + publicRoomsParams: PublicRoomsParams): PublicRoomsResponse { + return getPublicRoomTask.execute(GetPublicRoomTask.Params(server, publicRoomsParams)) } override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index d090ba5296dad810416bbffc5137bf2cf36aade4..60440c6359f54dffa16e99f743db07724f53c4ae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room import io.realm.Realm +import org.matrix.android.sdk.api.crypto.VerificationState import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -31,9 +32,11 @@ import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponse import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent +import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.EventMapper import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity +import org.matrix.android.sdk.internal.database.model.EditionOfEvent import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventInsertType @@ -50,33 +53,6 @@ import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor import timber.log.Timber import javax.inject.Inject -enum class VerificationState { - REQUEST, - WAITING, - CANCELED_BY_ME, - CANCELED_BY_OTHER, - DONE -} - -fun VerificationState.isCanceled(): Boolean { - return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER -} - -// State transition with control -private fun VerificationState?.toState(newState: VerificationState): VerificationState { - // Cancel is always prioritary ? - // Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to - // consider as canceled - if (newState.isCanceled()) { - return newState - } - // never move out of cancel - if (this?.isCanceled() == true) { - return this - } - return newState -} - internal class EventRelationsAggregationProcessor @Inject constructor(@UserId private val userId: String) : EventInsertLiveProcessor { @@ -118,13 +94,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr Timber.v("###REACTION Agreggation in room $roomId for event ${event.eventId}") handleInitialAggregatedRelations(event, roomId, event.unsignedData.relations.annotations, realm) - EventAnnotationsSummaryEntity.where(realm, event.eventId - ?: "").findFirst()?.let { - TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId - ?: "").findFirst()?.let { tet -> - tet.annotations = it - } - } + EventAnnotationsSummaryEntity.where(realm, roomId, event.eventId ?: "").findFirst() + ?.let { + TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId ?: "").findFirst() + ?.let { tet -> tet.annotations = it } + } } val content: MessageContent? = event.content.toModel() @@ -216,63 +190,78 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr // OPT OUT serer aggregation until API mature enough private val SHOULD_HANDLE_SERVER_AGREGGATION = false - private fun handleReplace(realm: Realm, event: Event, content: MessageContent, roomId: String, isLocalEcho: Boolean, relatedEventId: String? = null) { + private fun handleReplace(realm: Realm, + event: Event, + content: MessageContent, + roomId: String, + isLocalEcho: Boolean, + relatedEventId: String? = null) { val eventId = event.eventId ?: return val targetEventId = relatedEventId ?: content.relatesTo?.eventId ?: return val newContent = content.newContent ?: return + + // Check that the sender is the same + val editedEvent = EventEntity.where(realm, targetEventId).findFirst() + if (editedEvent == null) { + // We do not know yet about the edited event + } else if (editedEvent.sender != event.senderId) { + // Edited by someone else, ignore + Timber.w("Ignore edition by someone else") + return + } + // ok, this is a replace - val existing = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId) + val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId) // we have it - val existingSummary = existing.editSummary + val existingSummary = eventAnnotationsSummaryEntity.editSummary if (existingSummary == null) { Timber.v("###REPLACE new edit summary for $targetEventId, creating one (localEcho:$isLocalEcho)") // create the edit summary - val editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java) - editSummary.aggregatedContent = ContentMapper.map(newContent) - if (isLocalEcho) { - editSummary.lastEditTs = 0 - editSummary.sourceLocalEchoEvents.add(eventId) - } else { - editSummary.lastEditTs = event.originServerTs ?: 0 - editSummary.sourceEvents.add(eventId) - } - - existing.editSummary = editSummary + eventAnnotationsSummaryEntity.editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java) + .also { editSummary -> + editSummary.editions.add( + EditionOfEvent( + senderId = event.senderId ?: "", + eventId = event.eventId, + content = ContentMapper.map(newContent), + timestamp = if (isLocalEcho) 0 else event.originServerTs ?: 0, + isLocalEcho = isLocalEcho + ) + ) + } } else { - if (existingSummary.sourceEvents.contains(eventId)) { + if (existingSummary.editions.any { it.eventId == eventId }) { // ignore this event, we already know it (??) Timber.v("###REPLACE ignoring event for summary, it's known $eventId") return } val txId = event.unsignedData?.transactionId // is it a remote echo? - if (!isLocalEcho && existingSummary.sourceLocalEchoEvents.contains(txId)) { + if (!isLocalEcho && existingSummary.editions.any { it.eventId == txId }) { // ok it has already been managed Timber.v("###REPLACE Receiving remote echo of edit (edit already done)") - existingSummary.sourceLocalEchoEvents.remove(txId) - existingSummary.sourceEvents.add(event.eventId) - } else if ( - isLocalEcho // do not rely on ts for local echo, take it - || event.originServerTs ?: 0 >= existingSummary.lastEditTs - ) { - Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)") - if (!isLocalEcho) { - // Do not take local echo originServerTs here, could mess up ordering (keep old ts) - existingSummary.lastEditTs = event.originServerTs ?: System.currentTimeMillis() - } - existingSummary.aggregatedContent = ContentMapper.map(newContent) - if (isLocalEcho) { - existingSummary.sourceLocalEchoEvents.add(eventId) - } else { - existingSummary.sourceEvents.add(eventId) + existingSummary.editions.firstOrNull { it.eventId == txId }?.let { + it.eventId = event.eventId + it.timestamp = event.originServerTs ?: System.currentTimeMillis() + it.isLocalEcho = false } } else { - // ignore this event for the summary (back paginate) - if (!isLocalEcho) { - existingSummary.sourceEvents.add(eventId) - } - Timber.v("###REPLACE ignoring event for summary, it's to old $eventId") + Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)") + existingSummary.editions.add( + EditionOfEvent( + senderId = event.senderId ?: "", + eventId = event.eventId, + content = ContentMapper.map(newContent), + timestamp = if (isLocalEcho) { + System.currentTimeMillis() + } else { + // Do not take local echo originServerTs here, could mess up ordering (keep old ts) + event.originServerTs ?: System.currentTimeMillis() + }, + isLocalEcho = isLocalEcho + ) + ) } } } @@ -290,7 +279,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr val eventTimestamp = event.originServerTs ?: return // ok, this is a poll response - var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst() + var existing = EventAnnotationsSummaryEntity.where(realm, roomId, targetEventId).findFirst() if (existing == null) { Timber.v("## POLL creating new relation summary for $targetEventId") existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId) @@ -370,7 +359,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr aggregation.chunk?.forEach { if (it.type == EventType.REACTION) { val eventId = event.eventId ?: "" - val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() + val existing = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst() if (existing == null) { val eventSummary = EventAnnotationsSummaryEntity.create(realm, roomId, eventId) val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java) @@ -454,46 +443,29 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr */ private fun handleRedactionOfReplace(redacted: EventEntity, relatedEventId: String, realm: Realm) { Timber.d("Handle redaction of m.replace") - val eventSummary = EventAnnotationsSummaryEntity.where(realm, relatedEventId).findFirst() + val eventSummary = EventAnnotationsSummaryEntity.where(realm, redacted.roomId, relatedEventId).findFirst() if (eventSummary == null) { Timber.w("Redaction of a replace targeting an unknown event $relatedEventId") return } - val sourceEvents = eventSummary.editSummary?.sourceEvents - val sourceToDiscard = sourceEvents?.indexOf(redacted.eventId) + val sourceToDiscard = eventSummary.editSummary?.editions?.firstOrNull { it.eventId == redacted.eventId } if (sourceToDiscard == null) { Timber.w("Redaction of a replace that was not known in aggregation $sourceToDiscard") return } - // Need to remove this event from the redaction list and compute new aggregation state - sourceEvents.removeAt(sourceToDiscard) - val previousEdit = sourceEvents.mapNotNull { EventEntity.where(realm, it).findFirst() }.sortedBy { it.originServerTs }.lastOrNull() - if (previousEdit == null) { - // revert to original - eventSummary.editSummary?.deleteFromRealm() - } else { - // I have the last event - ContentMapper.map(previousEdit.content)?.toModel<MessageContent>()?.newContent?.let { newContent -> - eventSummary.editSummary?.lastEditTs = previousEdit.originServerTs - ?: System.currentTimeMillis() - eventSummary.editSummary?.aggregatedContent = ContentMapper.map(newContent) - } ?: run { - Timber.e("Failed to udate edited summary") - // TODO how to reccover that - } - } + // Need to remove this event from the edition list + sourceToDiscard.deleteFromRealm() } - fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) { + private fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) { Timber.v("REDACTION of reaction ${eventToPrune.eventId}") // delete a reaction, need to update the annotation summary if any - val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel() - ?: return + val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel() ?: return val eventThatWasReacted = reactionContent.relatesTo?.eventId ?: return val reactionKey = reactionContent.relatesTo.key Timber.v("REMOVE reaction for key $reactionKey") - val summary = EventAnnotationsSummaryEntity.where(realm, eventThatWasReacted).findFirst() + val summary = EventAnnotationsSummaryEntity.where(realm, eventToPrune.roomId, eventThatWasReacted).findFirst() if (summary != null) { summary.reactionsSummary.where() .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reactionKey) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index aa92c1cb3b3bef7c7e238bde1ddf071fbd4cbc9a..20cb49ee8a8e771840b8ff4a5c3258bff5f62d35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse -import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.session.room.alias.GetAliasesResponse @@ -50,14 +49,6 @@ import retrofit2.http.Query internal interface RoomAPI { - /** - * Get the third party server protocols. - * - * Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-thirdparty-protocols - */ - @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols") - fun thirdPartyProtocols(): Call<Map<String, ThirdPartyProtocol>> - /** * Lists the public rooms on the server, with optional filter. * This API returns paginated responses. The rooms are ordered by the number of joined members, with the largest rooms first. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 92f4ea2aea03b7385b082c2e96e091f416100f9f..66b727236077cb25f132323659fa9092891e711e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -39,11 +39,9 @@ import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask import org.matrix.android.sdk.internal.session.room.directory.DefaultGetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.DefaultGetRoomDirectoryVisibilityTask -import org.matrix.android.sdk.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask -import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask @@ -153,9 +151,6 @@ internal abstract class RoomModule { @Binds abstract fun bindSetRoomDirectoryVisibilityTask(task: DefaultSetRoomDirectoryVisibilityTask): SetRoomDirectoryVisibilityTask - @Binds - abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask - @Binds abstract fun bindInviteTask(task: DefaultInviteTask): InviteTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt index 2be90bf8e3aa21618acc985d9e031c9787182343..97cfcdaa443bc285080dcd84a9fedf080b1f6113 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt @@ -105,12 +105,17 @@ internal class DefaultLoadRoomMembersTask @Inject constructor( ?: realm.createObject(roomId) val now = System.currentTimeMillis() for (roomMemberEvent in response.roomMemberEvents) { - if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null) { + if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null || roomMemberEvent.type == null) { continue } val ageLocalTs = roomMemberEvent.unsignedData?.age?.let { now - it } val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) - CurrentStateEventEntity.getOrCreate(realm, roomId, roomMemberEvent.stateKey, roomMemberEvent.type).apply { + CurrentStateEventEntity.getOrCreate( + realm, + roomId, + roomMemberEvent.stateKey, + roomMemberEvent.type + ).apply { eventId = roomMemberEvent.eventId root = eventEntity } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 784b610af706f94192edecd3d87a9ee5a21ed18f..0e18e30b130ab73a6551f1087f50e94c63853c9a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -17,7 +17,7 @@ package org.matrix.android.sdk.internal.session.room.membership import io.realm.Realm -import org.matrix.android.sdk.R +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.Membership @@ -32,17 +32,18 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.util.StringProvider import javax.inject.Inject /** * This class computes room display name */ internal class RoomDisplayNameResolver @Inject constructor( - private val stringProvider: StringProvider, + matrixConfiguration: MatrixConfiguration, @UserId private val userId: String ) { + private val roomDisplayNameFallbackProvider = matrixConfiguration.roomDisplayNameFallbackProvider + /** * Compute the room display name * @@ -82,7 +83,7 @@ internal class RoomDisplayNameResolver @Inject constructor( .findFirst() ?.displayName } else { - stringProvider.getString(R.string.room_displayname_room_invite) + roomDisplayNameFallbackProvider.getNameForRoomInvite() } } else if (roomEntity?.membership == Membership.JOIN) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() @@ -104,25 +105,25 @@ internal class RoomDisplayNameResolver @Inject constructor( val otherMembersCount = otherMembersSubset.count() name = when (otherMembersCount) { 0 -> { - stringProvider.getString(R.string.room_displayname_empty_room) + roomDisplayNameFallbackProvider.getNameForEmptyRoom() // TODO (was xx and yyy) ... } 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) 2 -> { - stringProvider.getString(R.string.room_displayname_two_members, + roomDisplayNameFallbackProvider.getNameFor2members( resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers) ) } 3 -> { - stringProvider.getString(R.string.room_displayname_3_members, + roomDisplayNameFallbackProvider.getNameFor3members( resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), resolveRoomMemberName(otherMembersSubset[2], roomMembers) ) } 4 -> { - stringProvider.getString(R.string.room_displayname_4_members, + roomDisplayNameFallbackProvider.getNameFor4members( resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), resolveRoomMemberName(otherMembersSubset[2], roomMembers), @@ -131,9 +132,7 @@ internal class RoomDisplayNameResolver @Inject constructor( } else -> { val remainingCount = invitedCount + joinedCount - otherMembersCount + 1 - stringProvider.getQuantityString( - R.plurals.room_displayname_four_and_more_members, - remainingCount, + roomDisplayNameFallbackProvider.getNameFor4membersAndMore( resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), resolveRoomMemberName(otherMembersSubset[2], roomMembers), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt index 5a82d74537040b8c97fb5b56f2b6ecae398a120f..5b211c505fe5b48a9bd2d665db1b52c82880cf1a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.peeking import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent @@ -65,23 +66,29 @@ internal class DefaultPeekRoomTask @Inject constructor( } // Is it a public room? - val publicRepoResult = when (getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))) { - RoomDirectoryVisibility.PRIVATE -> { - // We cannot resolve this room :/ - null - } - RoomDirectoryVisibility.PUBLIC -> { + val visibilityRes = tryOrNull("## PEEK: failed to get visibility") { + getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId)) + } + val publicRepoResult = when (visibilityRes) { + RoomDirectoryVisibility.PUBLIC -> { // Try to find it in directory val filter = if (isAlias) PublicRoomsFilter(searchTerm = params.roomIdOrAlias.substring(1)) else null - getPublicRoomTask.execute(GetPublicRoomTask.Params( - server = serverList.firstOrNull(), - publicRoomsParams = PublicRoomsParams( - filter = filter, - limit = 20.takeIf { filter != null } ?: 100 - ) - )).chunk?.firstOrNull { it.roomId == roomId } + tryOrNull("## PEEK: failed to GetPublicRoomTask") { + getPublicRoomTask.execute(GetPublicRoomTask.Params( + server = serverList.firstOrNull(), + publicRoomsParams = PublicRoomsParams( + filter = filter, + limit = 20.takeIf { filter != null } ?: 100 + ) + )) + }?.chunk?.firstOrNull { it.roomId == roomId } + } + else -> { + // RoomDirectoryVisibility.PRIVATE or null + // We cannot resolve this room :/ + null } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index b7caf62865ab6c26216f9486ae91282766b8a282..9693e56ff0768a133c195bf494f829fdf430f7a7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -17,14 +17,13 @@ package org.matrix.android.sdk.internal.session.room.relation import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory -import com.zhuinden.monarchy.Monarchy +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary -import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.relation.RelationService import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.util.Cancelable @@ -47,6 +46,7 @@ import timber.log.Timber internal class DefaultRelationService @AssistedInject constructor( @Assisted private val roomId: String, + private val eventEditor: EventEditor, private val eventSenderProcessor: EventSenderProcessor, private val eventFactory: LocalEchoEventFactory, private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, @@ -112,41 +112,23 @@ internal class DefaultRelationService @AssistedInject constructor( .executeBy(taskExecutor) } - override fun editTextMessage(targetEventId: String, + override fun editTextMessage(targetEvent: TimelineEvent, msgType: String, newBodyText: CharSequence, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable { - val event = eventFactory - .createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, msgType, compatibilityBodyText) - .also { saveLocalEcho(it) } - return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + return eventEditor.editTextMessage(targetEvent, msgType, newBodyText, newBodyAutoMarkdown, compatibilityBodyText) } override fun editReply(replyToEdit: TimelineEvent, originalTimelineEvent: TimelineEvent, newBodyText: String, compatibilityBodyText: String): Cancelable { - val event = eventFactory.createReplaceTextOfReply( - roomId, - replyToEdit, - originalTimelineEvent, - newBodyText, - true, - MessageType.MSGTYPE_TEXT, - compatibilityBodyText - ) - .also { saveLocalEcho(it) } - return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + return eventEditor.editReply(replyToEdit, originalTimelineEvent, newBodyText, compatibilityBodyText) } - override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) { - val params = FetchEditHistoryTask.Params(roomId, eventId) - fetchEditHistoryTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun fetchEditHistory(eventId: String): List<Event> { + return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId)) } override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? { @@ -159,7 +141,7 @@ internal class DefaultRelationService @AssistedInject constructor( override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? { return monarchy.fetchCopyMap( - { EventAnnotationsSummaryEntity.where(it, eventId).findFirst() }, + { EventAnnotationsSummaryEntity.where(it, roomId, eventId).findFirst() }, { entity, _ -> entity.asDomain() } @@ -168,7 +150,7 @@ internal class DefaultRelationService @AssistedInject constructor( override fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> { val liveData = monarchy.findAllMappedWithChanges( - { EventAnnotationsSummaryEntity.where(it, eventId) }, + { EventAnnotationsSummaryEntity.where(it, roomId, eventId) }, { it.asDomain() } ) return Transformations.map(liveData) { results -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt new file mode 100644 index 0000000000000000000000000000000000000000..5fe06287d277aea08a8d68a1ec3ecfd8a20f1d1c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2021 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.session.room.relation + +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.api.util.NoOpCancellable +import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider +import org.matrix.android.sdk.internal.database.mapper.toEntity +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import timber.log.Timber +import javax.inject.Inject + +internal class EventEditor @Inject constructor(private val eventSenderProcessor: EventSenderProcessor, + private val eventFactory: LocalEchoEventFactory, + private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, + private val localEchoRepository: LocalEchoRepository) { + + fun editTextMessage(targetEvent: TimelineEvent, + msgType: String, + newBodyText: CharSequence, + newBodyAutoMarkdown: Boolean, + compatibilityBodyText: String): Cancelable { + val roomId = targetEvent.roomId + if (targetEvent.root.sendState.hasFailed()) { + // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. + val editedEvent = eventFactory.createTextEvent(roomId, msgType, newBodyText, newBodyAutoMarkdown).copy( + eventId = targetEvent.eventId + ) + updateFailedEchoWithEvent(roomId, targetEvent.eventId, editedEvent) + return eventSenderProcessor.postEvent(editedEvent, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else if (targetEvent.root.sendState.isSent()) { + val event = eventFactory + .createReplaceTextEvent(roomId, targetEvent.eventId, newBodyText, newBodyAutoMarkdown, msgType, compatibilityBodyText) + .also { localEchoRepository.createLocalEcho(it) } + return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else { + // Should we throw? + Timber.w("Can't edit a sending event") + return NoOpCancellable + } + } + + fun editReply(replyToEdit: TimelineEvent, + originalTimelineEvent: TimelineEvent, + newBodyText: String, + compatibilityBodyText: String): Cancelable { + val roomId = replyToEdit.roomId + if (replyToEdit.root.sendState.hasFailed()) { + // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. + val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText, false)?.copy( + eventId = replyToEdit.eventId + ) ?: return NoOpCancellable + updateFailedEchoWithEvent(roomId, replyToEdit.eventId, editedEvent) + return eventSenderProcessor.postEvent(editedEvent, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else if (replyToEdit.root.sendState.isSent()) { + val event = eventFactory.createReplaceTextOfReply( + roomId, + replyToEdit, + originalTimelineEvent, + newBodyText, + true, + MessageType.MSGTYPE_TEXT, + compatibilityBodyText + ) + .also { localEchoRepository.createLocalEcho(it) } + return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else { + // Should we throw? + Timber.w("Can't edit a sending event") + return NoOpCancellable + } + } + + private fun updateFailedEchoWithEvent(roomId: String, failedEchoEventId: String, editedEvent: Event) { + val editedEventEntity = editedEvent.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis()) + localEchoRepository.updateEchoAsync(failedEchoEventId) { _, entity -> + entity.content = editedEventEntity.content + entity.ageLocalTs = editedEventEntity.ageLocalTs + entity.age = editedEventEntity.age + entity.originServerTs = editedEventEntity.originServerTs + entity.sendState = editedEventEntity.sendState + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt index 854585ca2935654197edb19b83363f75df2b6d56..f9fd5f9348755bc43f58b5cf80f009ec045791cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt @@ -49,8 +49,11 @@ internal class DefaultFetchEditHistoryTask @Inject constructor( ) } - val events = response.chunks.toMutableList() - response.originalEvent?.let { events.add(it) } - return events + // Filter out edition form other users, and redacted editions + val originalSenderId = response.originalEvent?.senderId + val events = response.chunks + .filter { it.senderId == originalSenderId } + .filter { !it.isRedacted() } + return events + listOfNotNull(response.originalEvent) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FindReactionEventForUndoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FindReactionEventForUndoTask.kt index fa6db2ee3749483287bd031dd0a4df316733ebf4..863ae4f5ce7a0c6b77331ebdbc9e0fd5dbadc8c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FindReactionEventForUndoTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FindReactionEventForUndoTask.kt @@ -45,16 +45,16 @@ internal class DefaultFindReactionEventForUndoTask @Inject constructor( override suspend fun execute(params: FindReactionEventForUndoTask.Params): FindReactionEventForUndoTask.Result { val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm -> - getReactionToRedact(realm, params.reaction, params.eventId)?.eventId + getReactionToRedact(realm, params)?.eventId } return FindReactionEventForUndoTask.Result(eventId) } - private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String): EventEntity? { - val summary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() ?: return null + private fun getReactionToRedact(realm: Realm, params: FindReactionEventForUndoTask.Params): EventEntity? { + val summary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst() ?: return null val rase = summary.reactionsSummary.where() - .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction) + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction) .findFirst() ?: return null // want to find the event originated by me! diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt index c12597bea0a44d7efeac40ea7039a787727c8578..403aa274fed8e541cb0f3eabf8495bfb64b25699 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt @@ -89,7 +89,7 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) roomId = roomId, parentId = relatedEventId, relationType = relationType, - eventType = localEvent.type, + eventType = localEvent.type!!, content = localEvent.content ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt index 1f68a700ad0715bed10b8ee548139b89c9d07569..32d6c5aa7eebbbf6ce723f6722110d2acc743c4d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt @@ -47,22 +47,22 @@ internal class DefaultUpdateQuickReactionTask @Inject constructor(@SessionDataba override suspend fun execute(params: UpdateQuickReactionTask.Params): UpdateQuickReactionTask.Result { var res: Pair<String?, List<String>?>? = null monarchy.doWithRealm { realm -> - res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId) + res = updateQuickReaction(realm, params) } return UpdateQuickReactionTask.Result(res?.first, res?.second.orEmpty()) } - private fun updateQuickReaction(realm: Realm, reaction: String, oppositeReaction: String, eventId: String): Pair<String?, List<String>?> { + private fun updateQuickReaction(realm: Realm, params: UpdateQuickReactionTask.Params): Pair<String?, List<String>?> { // the emoji reaction has been selected, we need to check if we have reacted it or not - val existingSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() - ?: return Pair(reaction, null) + val existingSummary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst() + ?: return Pair(params.reaction, null) // Ok there is already reactions on this event, have we reacted to it val aggregationForReaction = existingSummary.reactionsSummary.where() - .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction) + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction) .findFirst() val aggregationForOppositeReaction = existingSummary.reactionsSummary.where() - .equalTo(ReactionAggregatedSummaryEntityFields.KEY, oppositeReaction) + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.oppositeReaction) .findFirst() if (aggregationForReaction == null || !aggregationForReaction.addedByMe) { @@ -72,7 +72,7 @@ internal class DefaultUpdateQuickReactionTask @Inject constructor(@SessionDataba val entity = EventEntity.where(realm, it).findFirst() if (entity?.sender == userId) entity.eventId else null } - return Pair(reaction, toRedact) + return Pair(params.reaction, toRedact) } else { // I already added it, so i need to undo it (like a toggle) // find all m.redaction coming from me to readact them diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index a12962b51ffeda75b2fe8006cd4451ced1b0a03a..c5b8b42b3c48916acb37e1d5460ec044217d2074 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -232,6 +232,14 @@ internal class DefaultSendService @AssistedInject constructor( } } + override fun cancelAllFailedMessages() { + taskExecutor.executorScope.launch { + localEchoRepository.getAllFailedEventsToResend(roomId).forEach { event -> + cancelSend(event.eventId) + } + } + } + override fun sendMedia(attachment: ContentAttachmentData, compressBeforeSending: Boolean, roomIds: Set<String>): Cancelable { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index c01923055b14bec3194707a31d6f2da9944e1f14..432a4af062046896c48ea15929b4e971ad67a1f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -20,7 +20,6 @@ import android.content.Context import android.graphics.Bitmap import android.media.MediaMetadataRetriever import androidx.exifinterface.media.ExifInterface -import org.matrix.android.sdk.R import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event @@ -42,7 +41,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageOptionsConte import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType -import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_POLL import org.matrix.android.sdk.api.session.room.model.message.OptionItem @@ -59,7 +57,6 @@ import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils -import org.matrix.android.sdk.internal.util.StringProvider import javax.inject.Inject /** @@ -74,7 +71,6 @@ import javax.inject.Inject internal class LocalEchoEventFactory @Inject constructor( private val context: Context, @UserId private val userId: String, - private val stringProvider: StringProvider, private val markdownParser: MarkdownParser, private val textPillsUtils: TextPillsUtils, private val localEchoRepository: LocalEchoRepository, @@ -334,25 +330,6 @@ internal class LocalEchoEventFactory @Inject constructor( ) } - fun createVerificationRequest(roomId: String, fromDevice: String, toUserId: String, methods: List<String>): Event { - val localId = LocalEcho.createLocalEchoId() - return Event( - roomId = roomId, - originServerTs = dummyOriginServerTs(), - senderId = userId, - eventId = localId, - type = EventType.MESSAGE, - content = MessageVerificationRequestContent( - body = stringProvider.getString(R.string.key_verification_request_fallback_message, userId), - fromDevice = fromDevice, - toUserId = toUserId, - timestamp = System.currentTimeMillis(), - methods = methods - ).toContent(), - unsignedData = UnsignedData(age = null, transactionId = localId) - ) - } - private fun dummyOriginServerTs(): Long { return System.currentTimeMillis() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt index f742271fa703a20f42f60f7451daf93bdf139691..70245cbd5e49eead24cd825787ecc5bad2588016 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt @@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.asyncTransaction -import org.matrix.android.sdk.internal.database.helper.nextId import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.toEntity @@ -45,6 +44,7 @@ import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.awaitTransaction import timber.log.Timber +import java.util.UUID import javax.inject.Inject internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy, @@ -56,15 +56,15 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private fun createLocalEcho(event: Event) { val roomId = event.roomId ?: throw IllegalStateException("You should have set a roomId for your event") - val senderId = event.senderId ?: throw IllegalStateException("You should have set a senderIf for your event") - if (event.eventId == null) { - throw IllegalStateException("You should have set an eventId for your event") - } + val senderId = event.senderId ?: throw IllegalStateException("You should have set a senderId for your event") + event.eventId ?: throw IllegalStateException("You should have set an eventId for your event") + event.type ?: throw IllegalStateException("You should have set a type for your event") + val timelineEventEntity = realmSessionProvider.withRealm { realm -> val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis()) val roomMemberHelper = RoomMemberHelper(realm, roomId) val myUser = roomMemberHelper.getLastRoomMember(senderId) - val localId = TimelineEventEntity.nextId(realm) + val localId = UUID.randomUUID().mostSignificantBits TimelineEventEntity(localId).also { it.root = eventEntity it.eventId = event.eventId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index 5014d945582da3739d477e220a8c9387fd5d7e88..8bafa5f882c7f7d32c41664b3554cf15ca8113a0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2021 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 + * 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, @@ -16,234 +16,21 @@ package org.matrix.android.sdk.internal.session.room.send.queue -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.api.auth.data.sessionId -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.failure.MatrixError -import org.matrix.android.sdk.api.failure.isTokenError -import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.task.TaskExecutor -import timber.log.Timber -import java.io.IOException -import java.net.InetAddress -import java.net.InetSocketAddress -import java.net.Socket -import java.util.Timer -import java.util.TimerTask -import java.util.concurrent.LinkedBlockingQueue -import javax.inject.Inject -import kotlin.concurrent.schedule +import org.matrix.android.sdk.internal.session.SessionLifecycleObserver -/** - * A simple ever running thread unique for that session responsible of sending events in order. - * Each send is retried 3 times, if there is no network (e.g if cannot ping home server) it will wait and - * periodically test reachability before resume (does not count as a retry) - * - * If the app is killed before all event were sent, on next wakeup the scheduled events will be re posted - */ -@SessionScope -internal class EventSenderProcessor @Inject constructor( - private val cryptoService: CryptoService, - private val sessionParams: SessionParams, - private val queuedTaskFactory: QueuedTaskFactory, - private val taskExecutor: TaskExecutor, - private val memento: QueueMemento -) : Thread("SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}") { - - private fun markAsManaged(task: QueuedTask) { - memento.track(task) - } - - private fun markAsFinished(task: QueuedTask) { - memento.unTrack(task) - } - - // API - fun postEvent(event: Event): Cancelable { - return postEvent(event, event.roomId?.let { cryptoService.isRoomEncrypted(it) } ?: false) - } - - override fun start() { - super.start() - // We should check for sending events not handled because app was killed - // But we should be careful of only took those that was submitted to us, because if it's - // for example it's a media event it is handled by some worker and he will handle it - // This is a bit fragile :/ - // also some events cannot be retried manually by users, e.g reactions - // they were previously relying on workers to do the work :/ and was expected to always finally succeed - // Also some echos are not to be resent like redaction echos (fake event created for aggregation) - - tryOrNull { - taskExecutor.executorScope.launch { - Timber.d("## Send relaunched pending events on restart") - memento.restoreTasks(this@EventSenderProcessor) - } - } - } - - fun postEvent(event: Event, encrypt: Boolean): Cancelable { - val task = queuedTaskFactory.createSendTask(event, encrypt) - return postTask(task) - } - - fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable { - return postRedaction(redactionLocalEcho.eventId!!, redactionLocalEcho.redacts!!, redactionLocalEcho.roomId!!, reason) - } - - fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable { - val task = queuedTaskFactory.createRedactTask(redactionLocalEchoId, eventToRedactId, roomId, reason) - return postTask(task) - } - - fun postTask(task: QueuedTask): Cancelable { - // non blocking add to queue - sendingQueue.add(task) - markAsManaged(task) - return task - } - - fun cancel(eventId: String, roomId: String) { - (currentTask as? SendEventQueuedTask) - ?.takeIf { it -> it.event.eventId == eventId && it.event.roomId == roomId } - ?.cancel() - } - - companion object { - private const val RETRY_WAIT_TIME_MS = 10_000L - } - - private var currentTask: QueuedTask? = null - - private var sendingQueue = LinkedBlockingQueue<QueuedTask>() +internal interface EventSenderProcessor: SessionLifecycleObserver { - private var networkAvailableLock = Object() - private var canReachServer = true - private var retryNoNetworkTask: TimerTask? = null + fun postEvent(event: Event): Cancelable - override fun run() { - Timber.v("## SendThread started ts:${System.currentTimeMillis()}") - try { - while (!isInterrupted) { - Timber.v("## SendThread wait for task to process") - val task = sendingQueue.take() - .also { currentTask = it } - Timber.v("## SendThread Found task to process $task") + fun postEvent(event: Event, encrypt: Boolean): Cancelable - if (task.isCancelled()) { - Timber.v("## SendThread send cancelled for $task") - // we do not execute this one - continue - } - // we check for network connectivity - while (!canReachServer) { - Timber.v("## SendThread cannot reach server, wait ts:${System.currentTimeMillis()}") - // schedule to retry - waitForNetwork() - // if thread as been killed meanwhile -// if (state == State.KILLING) break - } - Timber.v("## Server is Reachable") - // so network is available + fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable - runBlocking { - retryLoop@ while (task.retryCount < 3) { - try { - // SendPerformanceProfiler.startStage(task.event.eventId!!, SendPerformanceProfiler.Stages.SEND_WORKER) - Timber.v("## SendThread retryLoop for $task retryCount ${task.retryCount}") - task.execute() - // sendEventTask.execute(SendEventTask.Params(task.event, task.encrypt, cryptoService)) - // SendPerformanceProfiler.stopStage(task.event.eventId, SendPerformanceProfiler.Stages.SEND_WORKER) - break@retryLoop - } catch (exception: Throwable) { - when { - exception is IOException || exception is Failure.NetworkConnection -> { - canReachServer = false - task.retryCount++ - if (task.retryCount >= 3) task.onTaskFailed() - while (!canReachServer) { - Timber.v("## SendThread retryLoop cannot reach server, wait ts:${System.currentTimeMillis()}") - // schedule to retry - waitForNetwork() - } - } - (exception is Failure.ServerError && exception.error.code == MatrixError.M_LIMIT_EXCEEDED) -> { - task.retryCount++ - if (task.retryCount >= 3) task.onTaskFailed() - Timber.v("## SendThread retryLoop retryable error for $task reason: ${exception.localizedMessage}") - // wait a bit - // Todo if its a quota exception can we get timout? - sleep(3_000) - continue@retryLoop - } - exception.isTokenError() -> { - Timber.v("## SendThread retryLoop retryable TOKEN error, interrupt") - // we can exit the loop - task.onTaskFailed() - throw InterruptedException() - } - exception is CancellationException -> { - Timber.v("## SendThread task has been cancelled") - break@retryLoop - } - else -> { - Timber.v("## SendThread retryLoop Un-Retryable error, try next task") - // this task is in error, check next one? - break@retryLoop - } - } - } - } - } - markAsFinished(task) - } - } catch (interruptionException: InterruptedException) { - // will be thrown is thread is interrupted while seeping - interrupt() - Timber.v("## InterruptedException!! ${interruptionException.localizedMessage}") - } -// state = State.KILLED - // is this needed? - retryNoNetworkTask?.cancel() - Timber.w("## SendThread finished ${System.currentTimeMillis()}") - } + fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable - private fun waitForNetwork() { - retryNoNetworkTask = Timer(SyncState.NoNetwork.toString(), false).schedule(RETRY_WAIT_TIME_MS) { - synchronized(networkAvailableLock) { - canReachServer = checkHostAvailable().also { - Timber.v("## SendThread checkHostAvailable $it") - } - networkAvailableLock.notify() - } - } - synchronized(networkAvailableLock) { networkAvailableLock.wait() } - } + fun postTask(task: QueuedTask): Cancelable - /** - * Check if homeserver is reachable. - */ - private fun checkHostAvailable(): Boolean { - val host = sessionParams.homeServerConnectionConfig.homeServerUri.host ?: return false - val port = sessionParams.homeServerConnectionConfig.homeServerUri.port.takeIf { it != -1 } ?: 80 - val timeout = 30_000 - try { - Socket().use { socket -> - val inetAddress: InetAddress = InetAddress.getByName(host) - val inetSocketAddress = InetSocketAddress(inetAddress, port) - socket.connect(inetSocketAddress, timeout) - return true - } - } catch (e: IOException) { - Timber.v("## EventSender isHostAvailable failure ${e.localizedMessage}") - return false - } - } + fun cancel(eventId: String, roomId: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt new file mode 100644 index 0000000000000000000000000000000000000000..297233298956f176a8494dc0182e68ea853ff21f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt @@ -0,0 +1,200 @@ +/* + * Copyright 2021 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.session.room.send.queue + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.crypto.CryptoService +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.task.CoroutineSequencer +import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer +import org.matrix.android.sdk.internal.task.TaskExecutor +import org.matrix.android.sdk.internal.util.toCancelable +import timber.log.Timber +import java.io.IOException +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException + +private const val RETRY_WAIT_TIME_MS = 10_000L +private const val MAX_RETRY_COUNT = 3 + +/** + * This class is responsible for sending events in order in each room. It uses the QueuedTask.queueIdentifier to execute tasks sequentially. + * Each send is retried 3 times, if there is no network (e.g if cannot ping home server) it will wait and + * periodically test reachability before resume (does not count as a retry) + * + * If the app is killed before all event were sent, on next wakeup the scheduled events will be re posted + * + */ +@SessionScope +internal class EventSenderProcessorCoroutine @Inject constructor( + private val cryptoService: CryptoService, + private val sessionParams: SessionParams, + private val queuedTaskFactory: QueuedTaskFactory, + private val taskExecutor: TaskExecutor, + private val memento: QueueMemento +) : EventSenderProcessor { + + private val waitForNetworkSequencer = SemaphoreCoroutineSequencer() + + /** + * sequencers use QueuedTask.queueIdentifier as key + */ + private val sequencers = ConcurrentHashMap<String, CoroutineSequencer>() + + /** + * cancelableBag use QueuedTask.taskIdentifier as key + */ + private val cancelableBag = ConcurrentHashMap<String, Cancelable>() + + override fun onSessionStarted() { + // We should check for sending events not handled because app was killed + // But we should be careful of only took those that was submitted to us, because if it's + // for example it's a media event it is handled by some worker and he will handle it + // This is a bit fragile :/ + // also some events cannot be retried manually by users, e.g reactions + // they were previously relying on workers to do the work :/ and was expected to always finally succeed + // Also some echos are not to be resent like redaction echos (fake event created for aggregation) + taskExecutor.executorScope.launch { + Timber.d("## Send relaunched pending events on restart") + try { + memento.restoreTasks(this@EventSenderProcessorCoroutine) + } catch (failure: Throwable) { + Timber.e(failure, "Fail restoring send tasks") + } + } + } + + override fun postEvent(event: Event): Cancelable { + return postEvent(event, event.roomId?.let { cryptoService.isRoomEncrypted(it) } ?: false) + } + + override fun postEvent(event: Event, encrypt: Boolean): Cancelable { + val task = queuedTaskFactory.createSendTask(event, encrypt) + return postTask(task) + } + + override fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable { + return postRedaction(redactionLocalEcho.eventId!!, redactionLocalEcho.redacts!!, redactionLocalEcho.roomId!!, reason) + } + + override fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable { + val task = queuedTaskFactory.createRedactTask(redactionLocalEchoId, eventToRedactId, roomId, reason) + return postTask(task) + } + + override fun postTask(task: QueuedTask): Cancelable { + markAsManaged(task) + val sequencer = sequencers.getOrPut(task.queueIdentifier) { + SemaphoreCoroutineSequencer() + } + Timber.v("## post $task") + return taskExecutor.executorScope + .launchWith(sequencer) { + executeTask(task) + }.toCancelable() + .also { + cancelableBag[task.taskIdentifier] = it + } + } + + override fun cancel(eventId: String, roomId: String) { + // eventId is most likely the taskIdentifier + cancelableBag[eventId]?.cancel() + } + + private fun CoroutineScope.launchWith(sequencer: CoroutineSequencer, block: suspend CoroutineScope.() -> Unit) = launch { + sequencer.post { + block() + } + } + + private suspend fun executeTask(task: QueuedTask) { + try { + if (task.isCancelled()) { + Timber.v("## $task has been cancelled, try next task") + return + } + task.waitForNetwork() + task.execute() + } catch (exception: Throwable) { + when { + exception is IOException || exception is Failure.NetworkConnection -> { + canReachServer.set(false) + task.markAsFailedOrRetry(exception, 0) + } + (exception is Failure.ServerError && exception.error.code == MatrixError.M_LIMIT_EXCEEDED) -> { + val delay = exception.error.retryAfterMillis?.plus(100) ?: 3_000 + task.markAsFailedOrRetry(exception, delay) + } + exception is CancellationException -> { + Timber.v("## $task has been cancelled, try next task") + } + else -> { + Timber.v("## un-retryable error for $task, try next task") + // this task is in error, check next one? + task.onTaskFailed() + } + } + } + markAsFinished(task) + } + + private suspend fun QueuedTask.markAsFailedOrRetry(failure: Throwable, retryDelay: Long) { + if (retryCount.incrementAndGet() >= MAX_RETRY_COUNT) { + onTaskFailed() + } else { + Timber.v("## retryable error for $this reason: ${failure.localizedMessage}") + // Wait if necessary + delay(retryDelay) + // And then retry + executeTask(this) + } + } + + private fun markAsManaged(task: QueuedTask) { + memento.track(task) + } + + private fun markAsFinished(task: QueuedTask) { + cancelableBag.remove(task.taskIdentifier) + memento.unTrack(task) + } + + private val canReachServer = AtomicBoolean(true) + + private suspend fun QueuedTask.waitForNetwork() = waitForNetworkSequencer.post { + while (!canReachServer.get()) { + Timber.v("## $this cannot reach server wait ts:${System.currentTimeMillis()}") + delay(RETRY_WAIT_TIME_MS) + withContext(Dispatchers.IO) { + val hostAvailable = HomeServerAvailabilityChecker(sessionParams).check() + canReachServer.set(hostAvailable) + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt new file mode 100644 index 0000000000000000000000000000000000000000..b79a86dd7e0ce8392171d1aa560112b5587b9bbc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt @@ -0,0 +1,234 @@ +/* + * 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.session.room.send.queue + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.auth.data.sessionId +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.failure.isTokenError +import org.matrix.android.sdk.api.session.crypto.CryptoService +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.task.TaskExecutor +import timber.log.Timber +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.LinkedBlockingQueue +import javax.inject.Inject +import kotlin.concurrent.schedule + +/** + * A simple ever running thread unique for that session responsible of sending events in order. + * Each send is retried 3 times, if there is no network (e.g if cannot ping home server) it will wait and + * periodically test reachability before resume (does not count as a retry) + * + * If the app is killed before all event were sent, on next wakeup the scheduled events will be re posted + */ +@Deprecated("You should know use EventSenderProcessorCoroutine instead") +@SessionScope +internal class EventSenderProcessorThread @Inject constructor( + private val cryptoService: CryptoService, + private val sessionParams: SessionParams, + private val queuedTaskFactory: QueuedTaskFactory, + private val taskExecutor: TaskExecutor, + private val memento: QueueMemento +) : Thread("SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor { + + private fun markAsManaged(task: QueuedTask) { + memento.track(task) + } + + private fun markAsFinished(task: QueuedTask) { + memento.unTrack(task) + } + + override fun onSessionStarted() { + start() + } + + override fun onSessionStopped() { + interrupt() + } + + override fun start() { + super.start() + // We should check for sending events not handled because app was killed + // But we should be careful of only took those that was submitted to us, because if it's + // for example it's a media event it is handled by some worker and he will handle it + // This is a bit fragile :/ + // also some events cannot be retried manually by users, e.g reactions + // they were previously relying on workers to do the work :/ and was expected to always finally succeed + // Also some echos are not to be resent like redaction echos (fake event created for aggregation) + + tryOrNull { + taskExecutor.executorScope.launch { + Timber.d("## Send relaunched pending events on restart") + memento.restoreTasks(this@EventSenderProcessorThread) + } + } + } + + // API + override fun postEvent(event: Event): Cancelable { + return postEvent(event, event.roomId?.let { cryptoService.isRoomEncrypted(it) } ?: false) + } + + override fun postEvent(event: Event, encrypt: Boolean): Cancelable { + val task = queuedTaskFactory.createSendTask(event, encrypt) + return postTask(task) + } + + override fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable { + return postRedaction(redactionLocalEcho.eventId!!, redactionLocalEcho.redacts!!, redactionLocalEcho.roomId!!, reason) + } + + override fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable { + val task = queuedTaskFactory.createRedactTask(redactionLocalEchoId, eventToRedactId, roomId, reason) + return postTask(task) + } + + override fun postTask(task: QueuedTask): Cancelable { + // non blocking add to queue + sendingQueue.add(task) + markAsManaged(task) + return task + } + + override fun cancel(eventId: String, roomId: String) { + (currentTask as? SendEventQueuedTask) + ?.takeIf { it -> it.event.eventId == eventId && it.event.roomId == roomId } + ?.cancel() + } + + companion object { + private const val RETRY_WAIT_TIME_MS = 10_000L + } + + private var currentTask: QueuedTask? = null + + private var sendingQueue = LinkedBlockingQueue<QueuedTask>() + + private var networkAvailableLock = Object() + private var canReachServer = true + private var retryNoNetworkTask: TimerTask? = null + + override fun run() { + Timber.v("## SendThread started ts:${System.currentTimeMillis()}") + try { + while (!isInterrupted) { + Timber.v("## SendThread wait for task to process") + val task = sendingQueue.take() + .also { currentTask = it } + Timber.v("## SendThread Found task to process $task") + + if (task.isCancelled()) { + Timber.v("## SendThread send cancelled for $task") + // we do not execute this one + continue + } + // we check for network connectivity + while (!canReachServer) { + Timber.v("## SendThread cannot reach server, wait ts:${System.currentTimeMillis()}") + // schedule to retry + waitForNetwork() + // if thread as been killed meanwhile +// if (state == State.KILLING) break + } + Timber.v("## Server is Reachable") + // so network is available + + runBlocking { + retryLoop@ while (task.retryCount.get() < 3) { + try { + // SendPerformanceProfiler.startStage(task.event.eventId!!, SendPerformanceProfiler.Stages.SEND_WORKER) + Timber.v("## SendThread retryLoop for $task retryCount ${task.retryCount}") + task.execute() + // sendEventTask.execute(SendEventTask.Params(task.event, task.encrypt, cryptoService)) + // SendPerformanceProfiler.stopStage(task.event.eventId, SendPerformanceProfiler.Stages.SEND_WORKER) + break@retryLoop + } catch (exception: Throwable) { + when { + exception is IOException || exception is Failure.NetworkConnection -> { + canReachServer = false + if (task.retryCount.getAndIncrement() >= 3) task.onTaskFailed() + while (!canReachServer) { + Timber.v("## SendThread retryLoop cannot reach server, wait ts:${System.currentTimeMillis()}") + // schedule to retry + waitForNetwork() + } + } + (exception is Failure.ServerError && exception.error.code == MatrixError.M_LIMIT_EXCEEDED) -> { + if (task.retryCount.getAndIncrement() >= 3) task.onTaskFailed() + Timber.v("## SendThread retryLoop retryable error for $task reason: ${exception.localizedMessage}") + // wait a bit + // Todo if its a quota exception can we get timout? + sleep(3_000) + continue@retryLoop + } + exception.isTokenError() -> { + Timber.v("## SendThread retryLoop retryable TOKEN error, interrupt") + // we can exit the loop + task.onTaskFailed() + throw InterruptedException() + } + exception is CancellationException -> { + Timber.v("## SendThread task has been cancelled") + break@retryLoop + } + else -> { + Timber.v("## SendThread retryLoop Un-Retryable error, try next task") + // this task is in error, check next one? + task.onTaskFailed() + break@retryLoop + } + } + } + } + } + markAsFinished(task) + } + } catch (interruptionException: InterruptedException) { + // will be thrown is thread is interrupted while seeping + interrupt() + Timber.v("## InterruptedException!! ${interruptionException.localizedMessage}") + } +// state = State.KILLED + // is this needed? + retryNoNetworkTask?.cancel() + Timber.w("## SendThread finished ${System.currentTimeMillis()}") + } + + private fun waitForNetwork() { + retryNoNetworkTask = Timer(SyncState.NoNetwork.toString(), false).schedule(RETRY_WAIT_TIME_MS) { + synchronized(networkAvailableLock) { + canReachServer = HomeServerAvailabilityChecker(sessionParams).check().also { + Timber.v("## SendThread checkHostAvailable $it") + } + networkAvailableLock.notify() + } + } + synchronized(networkAvailableLock) { networkAvailableLock.wait() } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/HomeServerAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/HomeServerAvailabilityChecker.kt new file mode 100644 index 0000000000000000000000000000000000000000..2d53699917b03c819fd0ac04882a61edf7fea8ca --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/HomeServerAvailabilityChecker.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.session.room.send.queue + +import org.matrix.android.sdk.api.auth.data.SessionParams +import timber.log.Timber +import java.io.IOException +import java.net.InetAddress +import java.net.InetSocketAddress +import java.net.Socket + +internal class HomeServerAvailabilityChecker(val sessionParams: SessionParams) { + + fun check(): Boolean { + val host = sessionParams.homeServerConnectionConfig.homeServerUri.host ?: return false + val port = sessionParams.homeServerConnectionConfig.homeServerUri.port.takeIf { it != -1 } ?: 80 + val timeout = 30_000 + try { + Socket().use { socket -> + val inetAddress: InetAddress = InetAddress.getByName(host) + val inetSocketAddress = InetSocketAddress(inetAddress, port) + socket.connect(inetSocketAddress, timeout) + return true + } + } catch (e: IOException) { + Timber.v("## EventSender isHostAvailable failure ${e.localizedMessage}") + return false + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt index dfbac347d94f5b5d638a58f91c33fa15d5ab40ec..116c8d5c6b6c8a0ddf6e2ffff9b2e02c09c7e019 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt @@ -32,6 +32,9 @@ import javax.inject.Inject * It is just used to remember what events/localEchos was managed by the event sender in order to * reschedule them (and only them) on next restart */ + +private const val PERSISTENCE_KEY = "ManagedBySender" + internal class QueueMemento @Inject constructor(context: Context, @SessionId sessionId: String, private val queuedTaskFactory: QueuedTaskFactory, @@ -39,50 +42,49 @@ internal class QueueMemento @Inject constructor(context: Context, private val cryptoService: CryptoService) { private val storage = context.getSharedPreferences("QueueMemento_$sessionId", Context.MODE_PRIVATE) - private val managedTaskInfos = mutableListOf<QueuedTask>() + private val trackedTasks = mutableListOf<QueuedTask>() - fun track(task: QueuedTask) { - synchronized(managedTaskInfos) { - managedTaskInfos.add(task) - persist() - } + fun track(task: QueuedTask) = synchronized(trackedTasks) { + trackedTasks.add(task) + persist() } - fun unTrack(task: QueuedTask) { - managedTaskInfos.remove(task) + fun unTrack(task: QueuedTask) = synchronized(trackedTasks) { + trackedTasks.remove(task) persist() } + fun trackedTasks() = synchronized(trackedTasks) { + } + private fun persist() { - managedTaskInfos.mapIndexedNotNull { index, queuedTask -> + trackedTasks.mapIndexedNotNull { index, queuedTask -> toTaskInfo(queuedTask, index)?.let { TaskInfo.map(it) } }.toSet().let { set -> storage.edit() - .putStringSet("ManagedBySender", set) + .putStringSet(PERSISTENCE_KEY, set) .apply() } } private fun toTaskInfo(task: QueuedTask, order: Int): TaskInfo? { - synchronized(managedTaskInfos) { - return when (task) { - is SendEventQueuedTask -> SendEventTaskInfo( - localEchoId = task.event.eventId ?: "", - encrypt = task.encrypt, - order = order - ) - is RedactQueuedTask -> RedactEventTaskInfo( - redactionLocalEcho = task.redactionLocalEchoId, - order = order - ) - else -> null - } + return when (task) { + is SendEventQueuedTask -> SendEventTaskInfo( + localEchoId = task.event.eventId ?: "", + encrypt = task.encrypt, + order = order + ) + is RedactQueuedTask -> RedactEventTaskInfo( + redactionLocalEcho = task.redactionLocalEchoId, + order = order + ) + else -> null } } suspend fun restoreTasks(eventProcessor: EventSenderProcessor) { // events should be restarted in correct order - storage.getStringSet("ManagedBySender", null)?.let { pending -> + storage.getStringSet(PERSISTENCE_KEY, null)?.let { pending -> Timber.d("## Send - Recovering unsent events $pending") pending.mapNotNull { tryOrNull { TaskInfo.map(it) } } } @@ -90,7 +92,7 @@ internal class QueueMemento @Inject constructor(context: Context, ?.forEach { info -> try { when (info) { - is SendEventTaskInfo -> { + is SendEventTaskInfo -> { localEchoRepository.getUpToDateEcho(info.localEchoId)?.let { if (it.sendState.isSending() && it.eventId != null && it.roomId != null) { localEchoRepository.updateSendState(it.eventId, it.roomId, SendState.UNSENT) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt index 9a7fcd8d9148ea6860542ddaf0dc356a75aac0dd..948786677d4803cb43a040b5c75f1ab77b7fdc1f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt @@ -17,15 +17,29 @@ package org.matrix.android.sdk.internal.session.room.send.queue import org.matrix.android.sdk.api.util.Cancelable +import timber.log.Timber +import java.util.concurrent.atomic.AtomicInteger -abstract class QueuedTask : Cancelable { - var retryCount = 0 +/** + * @param queueIdentifier String value to identify a unique Queue + * @param taskIdentifier String value to identify a unique Task. Should be different from queueIdentifier + */ +internal abstract class QueuedTask( + val queueIdentifier: String, + val taskIdentifier: String +) : Cancelable { + + override fun toString() = "${javaClass.simpleName} queueIdentifier: $queueIdentifier, taskIdentifier: $taskIdentifier)" + + var retryCount = AtomicInteger(0) private var hasBeenCancelled: Boolean = false suspend fun execute() { if (!isCancelled()) { + Timber.v("Execute: $this start") doExecute() + Timber.v("Execute: $this finish") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt index 8e7ba2f15525486f6bb44e3a67b723ea66ee63a3..0e3d88aa792f9ab1156bcfbf4e56f257bbf17069 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt @@ -29,9 +29,7 @@ internal class RedactQueuedTask( private val redactEventTask: RedactEventTask, private val localEchoRepository: LocalEchoRepository, private val cancelSendTracker: CancelSendTracker -) : QueuedTask() { - - override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]" +) : QueuedTask(queueIdentifier = roomId, taskIdentifier = redactionLocalEchoId) { override suspend fun doExecute() { redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason)) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt index ea097082c71abdecd0e1dab3819d9ff8c483bb6e..49492e79907dd44fc1735b760b7ccd5f63772403 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt @@ -31,9 +31,7 @@ internal class SendEventQueuedTask( val cryptoService: CryptoService, val localEchoRepository: LocalEchoRepository, val cancelSendTracker: CancelSendTracker -) : QueuedTask() { - - override fun toString() = "[SendEventQueuedTask ${event.eventId}]" +) : QueuedTask(queueIdentifier = event.roomId!!, taskIdentifier = event.eventId!!) { override suspend fun doExecute() { sendEventTask.execute(SendEventTask.Params(event, encrypt)) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/TaskInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/TaskInfo.kt index 87c6299c4d86f9f34d627232283c338dee4e816c..a03ab778f68ca6980454402350094244b8dd1e64 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/TaskInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/TaskInfo.kt @@ -36,7 +36,7 @@ internal interface TaskInfo { const val TYPE_SEND = "TYPE_SEND" const val TYPE_REDACT = "TYPE_REDACT" - val moshi = Moshi.Builder() + private val moshi = Moshi.Builder() .add(RuntimeJsonAdapterFactory.of(TaskInfo::class.java, "type", FallbackTaskInfo::class.java) .registerSubtype(SendEventTaskInfo::class.java, TYPE_SEND) .registerSubtype(RedactEventTaskInfo::class.java, TYPE_REDACT) @@ -71,6 +71,6 @@ internal data class RedactEventTaskInfo( @JsonClass(generateAdapter = true) internal data class FallbackTaskInfo( - @Json(name = "type") override val type: String = TaskInfo.TYPE_REDACT, + @Json(name = "type") override val type: String = TaskInfo.TYPE_UNKNOWN, @Json(name = "order") override val order: Int ) : TaskInfo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index d0f6f8050ec7ffb344a70c130ab0627e7fed3fd1..a25a362bfa5bbd08ec13ba9fb9bb2deb0637c129 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -80,7 +80,11 @@ internal class StateEventDataSource @Inject constructor(@SessionDatabase private ): RealmQuery<CurrentStateEventEntity> { return realm.where<CurrentStateEventEntity>() .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) - .`in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + .apply { + if (eventTypes.isNotEmpty()) { + `in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + } + } .process(CurrentStateEventEntityFields.STATE_KEY, stateKey) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index ae90282d52d2e49dad52cf82021c3b9c6bac8ea8..d0946abe28c1747ff705dc52d962ee169a1a31c1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -28,13 +28,7 @@ import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.RelationType -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary -import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary import org.matrix.android.sdk.api.session.room.model.ReadReceipt -import org.matrix.android.sdk.api.session.room.model.message.MessageContent -import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent 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 @@ -43,11 +37,9 @@ import org.matrix.android.sdk.api.util.CancelableBag import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.model.ChunkEntity -import org.matrix.android.sdk.internal.database.model.ChunkEntityFields import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields -import org.matrix.android.sdk.internal.database.query.filterEvents import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.whereRoomId @@ -84,7 +76,8 @@ internal class DefaultTimeline( private val loadRoomMembersTask: LoadRoomMembersTask ) : Timeline, TimelineHiddenReadReceipts.Delegate, - TimelineInput.Listener { + TimelineInput.Listener, + UIEchoManager.Listener { companion object { val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD") @@ -105,12 +98,12 @@ internal class DefaultTimeline( private var prevDisplayIndex: Int? = null private var nextDisplayIndex: Int? = null - private val uiEchoManager = UIEchoManager() + private val uiEchoManager = UIEchoManager(settings, this) private val builtEvents = Collections.synchronizedList<TimelineEvent>(ArrayList()) private val builtEventsIdMap = Collections.synchronizedMap(HashMap<String, Int>()) - private val backwardsState = AtomicReference(State()) - private val forwardsState = AtomicReference(State()) + private val backwardsState = AtomicReference(TimelineState()) + private val forwardsState = AtomicReference(TimelineState()) override val timelineID = UUID.randomUUID().toString() @@ -169,13 +162,13 @@ internal class DefaultTimeline( // are still used for ui echo (relation like reaction) sendingEvents = roomEntity.sendingTimelineEvents.where()/*.filterEventsWithSettings()*/.findAll() sendingEvents.addChangeListener { events -> - uiEchoManager.sentEventsUpdated(events) + uiEchoManager.onSentEventsInDatabase(events.map { it.eventId }) postSnapshot() } nonFilteredEvents = buildEventQuery(realm).sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING).findAll() filteredEvents = nonFilteredEvents.where() - .filterEventsWithSettings() + .filterEventsWithSettings(settings) .findAll() nonFilteredEvents.addChangeListener(eventsChangeListener) handleInitialLoad() @@ -261,7 +254,9 @@ internal class DefaultTimeline( .equalTo(TimelineEventEntityFields.EVENT_ID, eventId) .findFirst() - val filteredEvents = nonFilteredEvents.where().filterEventsWithSettings().findAll() + val filteredEvents = nonFilteredEvents.where() + .filterEventsWithSettings(settings) + .findAll() val isEventInDb = nonFilteredEvent != null val isHidden = isEventInDb && filteredEvents.where() @@ -327,20 +322,29 @@ internal class DefaultTimeline( } override fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent) { - if (uiEchoManager.onLocalEchoCreated(roomId, timelineEvent)) { + if (roomId != this.roomId || !isLive) return + + val postSnapShot = uiEchoManager.onLocalEchoCreated(timelineEvent) + + if (listOf(timelineEvent).filterEventsWithSettings(settings).isNotEmpty()) { + listeners.forEach { + it.onNewTimelineEvents(listOf(timelineEvent.eventId)) + } + } + + if (postSnapShot) { postSnapshot() } } override fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState) { - if (uiEchoManager.onLocalEchoUpdated(roomId, eventId, sendState)) { + if (roomId != this.roomId || !isLive) return + if (uiEchoManager.onSendStateUpdated(eventId, sendState)) { postSnapshot() } } -// Private methods ***************************************************************************** - - private fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean { + override fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean { return tryOrNull { builtEventsIdMap[eventId]?.let { builtIndex -> // Update the relation of existing event @@ -360,6 +364,8 @@ internal class DefaultTimeline( } ?: false } +// Private methods ***************************************************************************** + private fun hasMoreInCache(direction: Timeline.Direction) = getState(direction).hasMoreInCache private fun hasReachedEnd(direction: Timeline.Direction) = getState(direction).hasReachedEnd @@ -412,35 +418,41 @@ internal class DefaultTimeline( } private fun buildSendingEvents(): List<TimelineEvent> { - val builtSendingEvents = ArrayList<TimelineEvent>() + val builtSendingEvents = mutableListOf<TimelineEvent>() if (hasReachedEnd(Timeline.Direction.FORWARDS) && !hasMoreInCache(Timeline.Direction.FORWARDS)) { - builtSendingEvents.addAll(uiEchoManager.getInMemorySendingEvents().filterEventsWithSettings()) + uiEchoManager.getInMemorySendingEvents() + .filterSendingEventsTo(builtSendingEvents) sendingEvents - .map { timelineEventMapper.map(it) } - // Filter out sending event that are not displayable! - .filterEventsWithSettings() - .forEach { timelineEvent -> - if (builtSendingEvents.find { it.eventId == timelineEvent.eventId } == null) { - uiEchoManager.updateSentStateWithUiEcho(timelineEvent) - builtSendingEvents.add(timelineEvent) - } + .filter { timelineEvent -> + builtSendingEvents.none { it.eventId == timelineEvent.eventId } } + .map { timelineEventMapper.map(it) } + .filterSendingEventsTo(builtSendingEvents) } return builtSendingEvents } + private fun List<TimelineEvent>.filterSendingEventsTo(target: MutableList<TimelineEvent>) { + target.addAll( + // Filter out sending event that are not displayable! + filterEventsWithSettings(settings) + // Get most up to date send state (in memory) + .map { uiEchoManager.updateSentStateWithUiEcho(it) } + ) + } + private fun canPaginate(direction: Timeline.Direction): Boolean { return isReady.get() && !getState(direction).isPaginating && hasMoreToLoad(direction) } - private fun getState(direction: Timeline.Direction): State { + private fun getState(direction: Timeline.Direction): TimelineState { return when (direction) { Timeline.Direction.FORWARDS -> forwardsState.get() Timeline.Direction.BACKWARDS -> backwardsState.get() } } - private fun updateState(direction: Timeline.Direction, update: (State) -> State) { + private fun updateState(direction: Timeline.Direction, update: (TimelineState) -> TimelineState) { val stateReference = when (direction) { Timeline.Direction.FORWARDS -> forwardsState Timeline.Direction.BACKWARDS -> backwardsState @@ -512,7 +524,7 @@ internal class DefaultTimeline( eventEntity?.eventId?.let { eventId -> postSnapshot = rebuildEvent(eventId) { val builtEvent = buildTimelineEvent(eventEntity) - listOf(builtEvent).filterEventsWithSettings().firstOrNull() + listOf(builtEvent).filterEventsWithSettings(settings).firstOrNull() } || postSnapshot } } @@ -688,11 +700,11 @@ internal class DefaultTimeline( return if (initialEventId == null) { TimelineEventEntity .whereRoomId(realm, roomId = roomId) - .equalTo("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST_FORWARD}", true) + .equalTo(TimelineEventEntityFields.CHUNK.IS_LAST_FORWARD, true) } else { TimelineEventEntity .whereRoomId(realm, roomId = roomId) - .`in`("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID}", arrayOf(initialEventId)) + .`in`("${TimelineEventEntityFields.CHUNK.TIMELINE_EVENTS}.${TimelineEventEntityFields.EVENT_ID}", arrayOf(initialEventId)) } } @@ -745,8 +757,8 @@ internal class DefaultTimeline( nextDisplayIndex = null builtEvents.clear() builtEventsIdMap.clear() - backwardsState.set(State()) - forwardsState.set(State()) + backwardsState.set(TimelineState()) + forwardsState.set(TimelineState()) } private fun createPaginationCallback(limit: Int, direction: Timeline.Direction): MatrixCallback<TokenChunkEventPersistor.Result> { @@ -780,191 +792,4 @@ internal class DefaultTimeline( private fun Timeline.Direction.toPaginationDirection(): PaginationDirection { return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS } - - private fun RealmQuery<TimelineEventEntity>.filterEventsWithSettings(): RealmQuery<TimelineEventEntity> { - return filterEvents(settings.filters) - } - - private fun List<TimelineEvent>.filterEventsWithSettings(): List<TimelineEvent> { - return filter { event -> - val filterType = !settings.filters.filterTypes - || settings.filters.allowedTypes.any { it.eventType == event.root.type && (it.stateKey == null || it.stateKey == event.root.senderId) } - if (!filterType) return@filter false - - val filterEdits = if (settings.filters.filterEdits && event.root.getClearType() == EventType.MESSAGE) { - val messageContent = event.root.getClearContent().toModel<MessageContent>() - messageContent?.relatesTo?.type != RelationType.REPLACE && messageContent?.relatesTo?.type != RelationType.RESPONSE - } else { - true - } - if (!filterEdits) return@filter false - - val filterRedacted = settings.filters.filterRedacted && event.root.isRedacted() - !filterRedacted - } - } - - private data class State( - val hasReachedEnd: Boolean = false, - val hasMoreInCache: Boolean = true, - val isPaginating: Boolean = false, - val requestedPaginationCount: Int = 0 - ) - - private data class ReactionUiEchoData( - val localEchoId: String, - val reactedOnEventId: String, - val reaction: String - ) - - inner class UIEchoManager { - - private val inMemorySendingEvents = Collections.synchronizedList<TimelineEvent>(ArrayList()) - - fun getInMemorySendingEvents(): List<TimelineEvent> { - return inMemorySendingEvents.toList() - } - - /** - * Due to lag of DB updates, we keep some UI echo of some properties to update timeline faster - */ - private val inMemorySendingStates = Collections.synchronizedMap<String, SendState>(HashMap()) - - private val inMemoryReactions = Collections.synchronizedMap<String, MutableList<ReactionUiEchoData>>(HashMap()) - - fun sentEventsUpdated(events: RealmResults<TimelineEventEntity>) { - // Remove in memory as soon as they are known by database - events.forEach { te -> - inMemorySendingEvents.removeAll { te.eventId == it.eventId } - } - inMemorySendingStates.keys.removeAll { key -> - events.find { it.eventId == key } == null - } - - inMemoryReactions.forEach { (_, uiEchoData) -> - uiEchoData.removeAll { data -> - // I remove the uiEcho, when the related event is not anymore in the sending list - // (means that it is synced)! - events.find { it.eventId == data.localEchoId } == null - } - } - } - - fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState): Boolean { - if (isLive && roomId == this@DefaultTimeline.roomId) { - val existingState = inMemorySendingStates[eventId] - inMemorySendingStates[eventId] = sendState - if (existingState != sendState) { - return true - } - } - return false - } - - // return true if should update - fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent): Boolean { - var postSnapshot = false - if (isLive && roomId == this@DefaultTimeline.roomId) { - // Manage some ui echos (do it before filter because actual event could be filtered out) - when (timelineEvent.root.getClearType()) { - EventType.REDACTION -> { - } - EventType.REACTION -> { - val content = timelineEvent.root.content?.toModel<ReactionContent>() - if (RelationType.ANNOTATION == content?.relatesTo?.type) { - val reaction = content.relatesTo.key - val relatedEventID = content.relatesTo.eventId - inMemoryReactions.getOrPut(relatedEventID) { mutableListOf() } - .add( - ReactionUiEchoData( - localEchoId = timelineEvent.eventId, - reactedOnEventId = relatedEventID, - reaction = reaction - ) - ) - postSnapshot = rebuildEvent(relatedEventID) { - decorateEventWithReactionUiEcho(it) - } || postSnapshot - } - } - } - - // do not add events that would have been filtered - if (listOf(timelineEvent).filterEventsWithSettings().isNotEmpty()) { - listeners.forEach { - it.onNewTimelineEvents(listOf(timelineEvent.eventId)) - } - Timber.v("On local echo created: ${timelineEvent.eventId}") - inMemorySendingEvents.add(0, timelineEvent) - postSnapshot = true - } - } - return postSnapshot - } - - fun decorateEventWithReactionUiEcho(timelineEvent: TimelineEvent): TimelineEvent? { - val relatedEventID = timelineEvent.eventId - val contents = inMemoryReactions[relatedEventID] ?: return null - - var existingAnnotationSummary = timelineEvent.annotations ?: EventAnnotationsSummary( - relatedEventID - ) - val updateReactions = existingAnnotationSummary.reactionsSummary.toMutableList() - - contents.forEach { uiEchoReaction -> - val existing = updateReactions.firstOrNull { it.key == uiEchoReaction.reaction } - if (existing == null) { - // just add the new key - ReactionAggregatedSummary( - key = uiEchoReaction.reaction, - count = 1, - addedByMe = true, - firstTimestamp = System.currentTimeMillis(), - sourceEvents = emptyList(), - localEchoEvents = listOf(uiEchoReaction.localEchoId) - ).let { updateReactions.add(it) } - } else { - // update Existing Key - if (!existing.localEchoEvents.contains(uiEchoReaction.localEchoId)) { - updateReactions.remove(existing) - // only update if echo is not yet there - ReactionAggregatedSummary( - key = existing.key, - count = existing.count + 1, - addedByMe = true, - firstTimestamp = existing.firstTimestamp, - sourceEvents = existing.sourceEvents, - localEchoEvents = existing.localEchoEvents + uiEchoReaction.localEchoId - - ).let { updateReactions.add(it) } - } - } - } - - existingAnnotationSummary = existingAnnotationSummary.copy( - reactionsSummary = updateReactions - ) - return timelineEvent.copy( - annotations = existingAnnotationSummary - ) - } - - fun updateSentStateWithUiEcho(element: TimelineEvent) { - inMemorySendingStates[element.eventId]?.let { - // Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state ${it} from state ${element.root.sendState}") - element.root.sendState = element.root.sendState.takeIf { it == SendState.SENT } ?: it - } - } - - fun onSyncedEvent(transactionId: String?) { - val sendingEvent = inMemorySendingEvents.find { - it.eventId == transactionId - } - inMemorySendingEvents.remove(sendingEvent) - // Is it too early to clear it? will be done when removed from sending anyway? - inMemoryReactions.forEach { (_, u) -> - u.filterNot { it.localEchoId == transactionId } - } - } - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index ef890db79e75947032a4208da4096ff0a7d24ed0..d000bbeb5027b17fcf08512053fa03980caf49e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.room.timeline import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations import dagger.assisted.Assisted import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory @@ -31,7 +30,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineService import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper @@ -89,13 +87,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv } override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> { - val liveData = monarchy.findAllMappedWithChanges( - { TimelineEventEntity.where(it, roomId = roomId, eventId = eventId) }, - { timelineEventMapper.map(it) } - ) - return Transformations.map(liveData) { events -> - events.firstOrNull().toOptional() - } + return LiveTimelineEvent(timelineInput, monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId) } override fun getAttachmentMessages(): List<TimelineEvent> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/Extensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..b2c8021f3b49487f3d705369c0b18c4db9709bc6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/Extensions.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 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.session.room.timeline + +import io.realm.RealmQuery +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +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.internal.database.model.TimelineEventEntity +import org.matrix.android.sdk.internal.database.query.filterEvents + +internal fun RealmQuery<TimelineEventEntity>.filterEventsWithSettings(settings: TimelineSettings): RealmQuery<TimelineEventEntity> { + return filterEvents(settings.filters) +} + +internal fun List<TimelineEvent>.filterEventsWithSettings(settings: TimelineSettings): List<TimelineEvent> { + return filter { event -> + val filterType = !settings.filters.filterTypes + || settings.filters.allowedTypes.any { it.eventType == event.root.type && (it.stateKey == null || it.stateKey == event.root.senderId) } + if (!filterType) return@filter false + + val filterEdits = if (settings.filters.filterEdits && event.root.getClearType() == EventType.MESSAGE) { + val messageContent = event.root.getClearContent().toModel<MessageContent>() + messageContent?.relatesTo?.type != RelationType.REPLACE && messageContent?.relatesTo?.type != RelationType.RESPONSE + } else { + true + } + if (!filterEdits) return@filter false + + val filterRedacted = settings.filters.filterRedacted && event.root.isRedacted() + !filterRedacted + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt new file mode 100644 index 0000000000000000000000000000000000000000..3c0f101e11da4f0a3dd3c892f12a508c31b565fd --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt @@ -0,0 +1,94 @@ +/* + * 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.session.room.timeline + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.events.model.LocalEcho +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper +import org.matrix.android.sdk.internal.database.model.TimelineEventEntity +import org.matrix.android.sdk.internal.database.query.where +import java.util.concurrent.atomic.AtomicBoolean + +/** + * This class takes care of handling case where local echo is replaced by the synced event in the db. + */ +internal class LiveTimelineEvent(private val timelineInput: TimelineInput, + private val monarchy: Monarchy, + private val coroutineScope: CoroutineScope, + private val timelineEventMapper: TimelineEventMapper, + private val roomId: String, + private val eventId: String) + : TimelineInput.Listener, + MediatorLiveData<Optional<TimelineEvent>>() { + + private var queryLiveData: LiveData<Optional<TimelineEvent>>? = null + + // If we are listening to local echo, we want to be aware when event is synced + private var shouldObserveSync = AtomicBoolean(LocalEcho.isLocalEchoId(eventId)) + + init { + buildAndObserveQuery(eventId) + } + + // Makes sure it's made on the main thread + private fun buildAndObserveQuery(eventIdToObserve: String) = coroutineScope.launch(Dispatchers.Main) { + queryLiveData?.also { + removeSource(it) + } + val liveData = monarchy.findAllMappedWithChanges( + { TimelineEventEntity.where(it, roomId = roomId, eventId = eventIdToObserve) }, + { timelineEventMapper.map(it) } + ) + queryLiveData = Transformations.map(liveData) { events -> + events.firstOrNull().toOptional() + }.also { + addSource(it) { newValue -> value = newValue } + } + } + + override fun onLocalEchoSynced(roomId: String, localEchoEventId: String, syncedEventId: String) { + if (this.roomId == roomId && localEchoEventId == this.eventId) { + timelineInput.listeners.remove(this) + shouldObserveSync.set(false) + // rebuild the query with the new eventId + buildAndObserveQuery(syncedEventId) + } + } + + override fun onActive() { + super.onActive() + if (shouldObserveSync.get()) { + timelineInput.listeners.add(this) + } + } + + override fun onInactive() { + super.onInactive() + if (shouldObserveSync.get()) { + timelineInput.listeners.remove(this) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/ReactionUiEchoData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/ReactionUiEchoData.kt new file mode 100644 index 0000000000000000000000000000000000000000..521120c4157bd075d9bd4b2d8c44d6a8beba0a93 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/ReactionUiEchoData.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 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.session.room.timeline + +internal data class ReactionUiEchoData( + val localEchoId: String, + val reactedOnEventId: String, + val reaction: String +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt index fa517bebf2b1b3325f5f9ec1d7354d1c6ee55ac5..0ade8ad3b8e6f2a9d55f2f02541dde714b1e7e62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt @@ -24,6 +24,7 @@ import io.realm.RealmResults import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper +import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntityFields import org.matrix.android.sdk.internal.database.model.TimelineEventEntity @@ -121,7 +122,7 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu // We are looking for read receipts set on hidden events. // We only accept those with a timelineEvent (so coming from pagination/sync). this.hiddenReadReceipts = ReadReceiptsSummaryEntity.whereInRoom(realm, roomId) - .isNotEmpty(ReadReceiptsSummaryEntityFields.TIMELINE_EVENT) + .isNotEmpty(ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.`$`) .isNotEmpty(ReadReceiptsSummaryEntityFields.READ_RECEIPTS.`$`) .filterReceiptsWithSettings() .findAllAsync() @@ -157,12 +158,12 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu // Result: D, F, H, I settings.filters.allowedTypes.forEachIndexed { index, filter -> if (filter.stateKey == null) { - notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", filter.eventType) + notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.TYPE}", filter.eventType) } else { beginGroup() - notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", filter.eventType) + notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.TYPE}", filter.eventType) or() - notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.STATE_KEY}", filter.stateKey) + notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.STATE_KEY}", filter.stateKey) endGroup() } if (index != settings.filters.allowedTypes.size - 1) { @@ -174,19 +175,19 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu } if (settings.filters.filterUseless) { if (needOr) or() - equalTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.IS_USELESS}", true) + equalTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.IS_USELESS}", true) needOr = true } if (settings.filters.filterEdits) { if (needOr) or() - like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT) + like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.CONTENT}", TimelineEventFilter.Content.EDIT) or() - like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.RESPONSE) + like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.CONTENT}", TimelineEventFilter.Content.RESPONSE) needOr = true } if (settings.filters.filterRedacted) { if (needOr) or() - like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.UNSIGNED_DATA}", TimelineEventFilter.Unsigned.REDACTED) + like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT.ROOT}.${EventEntityFields.UNSIGNED_DATA}", TimelineEventFilter.Unsigned.REDACTED) } endGroup() return this diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt index 002ab1dd8a17fe41aec312fcf90c3a8f88725bea..8911f265d57367d56fdb1706981b57c9e53ee825 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt @@ -35,11 +35,16 @@ internal class TimelineInput @Inject constructor() { listeners.toSet().forEach { it.onNewTimelineEvents(roomId, eventIds) } } + fun onLocalEchoSynced(roomId: String, localEchoEventId: String, syncEventId: String) { + listeners.toSet().forEach { it.onLocalEchoSynced(roomId, localEchoEventId, syncEventId) } + } + val listeners = mutableSetOf<Listener>() internal interface Listener { - fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent) - fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState) - fun onNewTimelineEvents(roomId: String, eventIds: List<String>) + fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent) = Unit + fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState) = Unit + fun onNewTimelineEvents(roomId: String, eventIds: List<String>) = Unit + fun onLocalEchoSynced(roomId: String, localEchoEventId: String, syncedEventId: String) = Unit } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineState.kt new file mode 100644 index 0000000000000000000000000000000000000000..0143d9bab3d21498b03c9bb1f2f23d6f48609a6d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 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.session.room.timeline + +internal data class TimelineState( + val hasReachedEnd: Boolean = false, + val hasMoreInCache: Boolean = true, + val isPaginating: Boolean = false, + val requestedPaginationCount: Int = 0 +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 1a497b883555a75d24759a9a6a037d8753b3cf5c..c38dcd00a73387e3ec1fe0697a607ef2bd8141ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -22,16 +22,16 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.send.SendState -import org.matrix.android.sdk.internal.database.helper.addOrUpdate +import org.matrix.android.sdk.internal.database.helper.addIfNecessary import org.matrix.android.sdk.internal.database.helper.addStateEvent import org.matrix.android.sdk.internal.database.helper.addTimelineEvent -import org.matrix.android.sdk.internal.database.helper.deleteOnCascade import org.matrix.android.sdk.internal.database.helper.merge import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.find @@ -172,7 +172,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri val currentLastForwardChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) if (currentChunk != currentLastForwardChunk) { currentChunk.isLastForward = true - currentLastForwardChunk?.deleteOnCascade() + currentLastForwardChunk?.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false) RoomSummaryEntity.where(realm, roomId).findFirst()?.apply { latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) } @@ -235,7 +235,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri } } chunksToDelete.forEach { - it.deleteOnCascade() + it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false) } val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId) val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null @@ -244,7 +244,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) } if (currentChunk.isValid) { - RoomEntity.where(realm, roomId).findFirst()?.addOrUpdate(currentChunk) + RoomEntity.where(realm, roomId).findFirst()?.addIfNecessary(currentChunk) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..67d0d90d7705d31d43a3c247aa26d48c19c770c3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 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.session.room.timeline + +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary +import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary +import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import timber.log.Timber +import java.util.Collections + +internal class UIEchoManager( + private val settings: TimelineSettings, + private val listener: Listener +) { + + interface Listener { + fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean + } + + private val inMemorySendingEvents = Collections.synchronizedList<TimelineEvent>(ArrayList()) + + fun getInMemorySendingEvents(): List<TimelineEvent> { + return inMemorySendingEvents.toList() + } + + /** + * Due to lag of DB updates, we keep some UI echo of some properties to update timeline faster + */ + private val inMemorySendingStates = Collections.synchronizedMap<String, SendState>(HashMap()) + + private val inMemoryReactions = Collections.synchronizedMap<String, MutableList<ReactionUiEchoData>>(HashMap()) + + fun onSentEventsInDatabase(eventIds: List<String>) { + // Remove in memory as soon as they are known by database + eventIds.forEach { eventId -> + inMemorySendingEvents.removeAll { eventId == it.eventId } + } + inMemoryReactions.forEach { (_, uiEchoData) -> + uiEchoData.removeAll { data -> + // I remove the uiEcho, when the related event is not anymore in the sending list + // (means that it is synced)! + eventIds.find { it == data.localEchoId } == null + } + } + } + + fun onSendStateUpdated(eventId: String, sendState: SendState): Boolean { + val existingState = inMemorySendingStates[eventId] + inMemorySendingStates[eventId] = sendState + return existingState != sendState + } + + // return true if should update + fun onLocalEchoCreated(timelineEvent: TimelineEvent): Boolean { + var postSnapshot = false + + // Manage some ui echos (do it before filter because actual event could be filtered out) + when (timelineEvent.root.getClearType()) { + EventType.REDACTION -> { + } + EventType.REACTION -> { + val content = timelineEvent.root.content?.toModel<ReactionContent>() + if (RelationType.ANNOTATION == content?.relatesTo?.type) { + val reaction = content.relatesTo.key + val relatedEventID = content.relatesTo.eventId + inMemoryReactions.getOrPut(relatedEventID) { mutableListOf() } + .add( + ReactionUiEchoData( + localEchoId = timelineEvent.eventId, + reactedOnEventId = relatedEventID, + reaction = reaction + ) + ) + postSnapshot = listener.rebuildEvent(relatedEventID) { + decorateEventWithReactionUiEcho(it) + } || postSnapshot + } + } + } + + // do not add events that would have been filtered + if (listOf(timelineEvent).filterEventsWithSettings(settings).isNotEmpty()) { + Timber.v("On local echo created: ${timelineEvent.eventId}") + inMemorySendingEvents.add(0, timelineEvent) + postSnapshot = true + } + + return postSnapshot + } + + fun decorateEventWithReactionUiEcho(timelineEvent: TimelineEvent): TimelineEvent? { + val relatedEventID = timelineEvent.eventId + val contents = inMemoryReactions[relatedEventID] ?: return null + + var existingAnnotationSummary = timelineEvent.annotations ?: EventAnnotationsSummary( + relatedEventID + ) + val updateReactions = existingAnnotationSummary.reactionsSummary.toMutableList() + + contents.forEach { uiEchoReaction -> + val existing = updateReactions.firstOrNull { it.key == uiEchoReaction.reaction } + if (existing == null) { + // just add the new key + ReactionAggregatedSummary( + key = uiEchoReaction.reaction, + count = 1, + addedByMe = true, + firstTimestamp = System.currentTimeMillis(), + sourceEvents = emptyList(), + localEchoEvents = listOf(uiEchoReaction.localEchoId) + ).let { updateReactions.add(it) } + } else { + // update Existing Key + if (!existing.localEchoEvents.contains(uiEchoReaction.localEchoId)) { + updateReactions.remove(existing) + // only update if echo is not yet there + ReactionAggregatedSummary( + key = existing.key, + count = existing.count + 1, + addedByMe = true, + firstTimestamp = existing.firstTimestamp, + sourceEvents = existing.sourceEvents, + localEchoEvents = existing.localEchoEvents + uiEchoReaction.localEchoId + + ).let { updateReactions.add(it) } + } + } + } + + existingAnnotationSummary = existingAnnotationSummary.copy( + reactionsSummary = updateReactions + ) + return timelineEvent.copy( + annotations = existingAnnotationSummary + ) + } + + fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent { + if (timelineEvent.root.sendState.isSent()) return timelineEvent + val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent + // Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}") + return timelineEvent.copy( + root = timelineEvent.root.copyAll() + .also { it.sendState = inMemoryState } + ) + } + + fun onSyncedEvent(transactionId: String?) { + val sendingEvent = inMemorySendingEvents.find { + it.eventId == transactionId + } + inMemorySendingEvents.remove(sendingEvent) + // Is it too early to clear it? will be done when removed from sending anyway? + inMemoryReactions.forEach { (_, u) -> + u.filterNot { it.localEchoId == transactionId } + } + inMemorySendingStates.remove(transactionId) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt index fc476a3dd69699ece2a6c0709084367f90880fd9..ae60faf9057a9680c1575fd0dd31b68a7d15142f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt @@ -26,7 +26,7 @@ import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.model.event.OlmEventContent import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService -import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService +import org.matrix.android.sdk.internal.session.initsync.ProgressReporter import org.matrix.android.sdk.internal.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse import timber.log.Timber @@ -35,10 +35,10 @@ import javax.inject.Inject internal class CryptoSyncHandler @Inject constructor(private val cryptoService: DefaultCryptoService, private val verificationService: DefaultVerificationService) { - fun handleToDevice(toDevice: ToDeviceSyncResponse, initialSyncProgressService: DefaultInitialSyncProgressService? = null) { + fun handleToDevice(toDevice: ToDeviceSyncResponse, progressReporter: ProgressReporter? = null) { val total = toDevice.events?.size ?: 0 toDevice.events?.forEachIndexed { index, event -> - initialSyncProgressService?.reportProgress(((index / total.toFloat()) * 100).toInt()) + progressReporter?.reportProgress(index * 100F / total) // Decrypt event if necessary Timber.i("## CRYPTO | To device event from ${event.senderId} of type:${event.type}") decryptToDeviceEvent(event, null) @@ -75,7 +75,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: // try to find device id to ease log reading val deviceId = cryptoService.getCryptoDeviceInfo(event.senderId!!).firstOrNull { it.identityKey() == senderKey - }?.deviceId ?: senderKey + }?.deviceId ?: senderKey Timber.e("## CRYPTO | Failed to decrypt to device event from ${event.senderId}|$deviceId reason:<${event.mCryptoError ?: exception}>") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt index 135f711a6cca74a2833c752c9d5566de053b170a..02362bf050e5512af37b7716f072b93056446e5b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt @@ -16,17 +16,17 @@ package org.matrix.android.sdk.internal.session.sync -import org.matrix.android.sdk.R +import io.realm.Realm +import org.matrix.android.sdk.api.session.initsync.InitSyncStep import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.internal.database.model.GroupEntity import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where -import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService -import org.matrix.android.sdk.internal.session.mapWithProgress +import org.matrix.android.sdk.internal.session.initsync.ProgressReporter +import org.matrix.android.sdk.internal.session.initsync.mapWithProgress import org.matrix.android.sdk.internal.session.sync.model.GroupsSyncResponse import org.matrix.android.sdk.internal.session.sync.model.InvitedGroupSync -import io.realm.Realm import javax.inject.Inject internal class GroupSyncHandler @Inject constructor() { @@ -37,11 +37,9 @@ internal class GroupSyncHandler @Inject constructor() { data class LEFT(val data: Map<String, Any>) : HandlingStrategy() } - fun handle( - realm: Realm, - roomsSyncResponse: GroupsSyncResponse, - reporter: DefaultInitialSyncProgressService? = null - ) { + fun handle(realm: Realm, + roomsSyncResponse: GroupsSyncResponse, + reporter: ProgressReporter? = null) { handleGroupSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), reporter) handleGroupSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), reporter) handleGroupSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), reporter) @@ -49,20 +47,20 @@ internal class GroupSyncHandler @Inject constructor() { // PRIVATE METHODS ***************************************************************************** - private fun handleGroupSync(realm: Realm, handlingStrategy: HandlingStrategy, reporter: DefaultInitialSyncProgressService?) { + private fun handleGroupSync(realm: Realm, handlingStrategy: HandlingStrategy, reporter: ProgressReporter?) { val groups = when (handlingStrategy) { is HandlingStrategy.JOINED -> - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.6f) { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountGroups, 0.6f) { handleJoinedGroup(realm, it.key) } is HandlingStrategy.INVITED -> - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.3f) { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountGroups, 0.3f) { handleInvitedGroup(realm, it.key) } is HandlingStrategy.LEFT -> - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.1f) { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountGroups, 0.1f) { handleLeftGroup(realm, it.key) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..4b82ecc3e5878e450d47f2d7c63ea929ee287f63 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt @@ -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.session.sync + +import com.squareup.moshi.JsonClass +import okio.buffer +import okio.source +import org.matrix.android.sdk.internal.di.MoshiProvider +import timber.log.Timber +import java.io.File + +@JsonClass(generateAdapter = true) +internal data class InitialSyncStatus( + val step: Int = STEP_INIT, + val downloadedDate: Long = 0 +) { + companion object { + const val STEP_INIT = 0 + const val STEP_DOWNLOADING = 1 + const val STEP_DOWNLOADED = 2 + const val STEP_PARSED = 3 + const val STEP_SUCCESS = 4 + } +} + +internal interface InitialSyncStatusRepository { + fun getStep(): Int + + fun setStep(step: Int) +} + +/** + * This class handle the current status of an initial sync and persist it on the disk, to be robust against crash + */ +internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncStatusRepository { + + companion object { + // After 2 hours, we consider that the downloaded file is outdated: + // - if a problem occurs, it's for big accounts, and big accounts have lots of new events in 2 hours + // - For small accounts, there should be no problem, so 2 hours delay will never be used. + private const val INIT_SYNC_FILE_LIFETIME = 2 * 60 * 60 * 1_000L + } + + private val file = File(directory, "status.json") + private val jsonAdapter = MoshiProvider.providesMoshi().adapter(InitialSyncStatus::class.java) + + private var cache: InitialSyncStatus? = null + + override fun getStep(): Int { + ensureCache() + val state = cache?.step ?: InitialSyncStatus.STEP_INIT + return if (state >= InitialSyncStatus.STEP_DOWNLOADED + && System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) { + Timber.v("INIT_SYNC downloaded file is outdated, download it again") + // The downloaded file is outdated + setStep(InitialSyncStatus.STEP_INIT) + InitialSyncStatus.STEP_INIT + } else { + state + } + } + + override fun setStep(step: Int) { + var newStatus = cache?.copy(step = step) ?: InitialSyncStatus(step = step) + if (step == InitialSyncStatus.STEP_DOWNLOADED) { + // Also store the downloaded date + newStatus = newStatus.copy( + downloadedDate = System.currentTimeMillis() + ) + } + cache = newStatus + writeFile() + } + + private fun ensureCache() { + if (cache == null) readFile() + } + + /** + * File -> Cache + */ + private fun readFile() { + cache = file + .takeIf { it.exists() } + ?.let { jsonAdapter.fromJson(it.source().buffer()) } + } + + /** + * Cache -> File + */ + private fun writeFile() { + file.delete() + cache + ?.let { jsonAdapter.toJson(it) } + ?.let { file.writeText(it) } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt new file mode 100644 index 0000000000000000000000000000000000000000..297cc213ed704f6c76058d05d34901e40a074d32 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt @@ -0,0 +1,53 @@ +/* + * 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.session.sync + +var initialSyncStrategy: InitialSyncStrategy = InitialSyncStrategy.Optimized() + +sealed class InitialSyncStrategy { + /** + * Parse the result in its entirety + * Pros: + * - Faster to handle parsed data + * Cons: + * - Slower to download and parse data + * - big RAM usage + * - not robust to crash + */ + object Legacy : InitialSyncStrategy() + + /** + * Optimized. + * First store the request result in a file, to avoid doing it again in case of crash + */ + data class Optimized( + /** + * Limit to reach to decide to split the init sync response into smaller files + * Empiric value: 1 megabytes + */ + val minSizeToSplit: Long = 1024 * 1024, + /** + * Limit per room to reach to decide to store a join room ephemeral Events into a file + * Empiric value: 6 kilobytes + */ + val minSizeToStoreInFile: Long = 6 * 1024, + /** + * Max number of rooms to insert at a time in database (to avoid too much RAM usage) + */ + val maxRoomsToInsert: Int = 100 + ) : InitialSyncStrategy() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt index 456b0f9c2625224be559f61e3675967889e99347..336a83eaadd1d5a26b9f746f599c977c99a36b29 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt @@ -18,11 +18,11 @@ package org.matrix.android.sdk.internal.session.sync import io.realm.Realm import io.realm.kotlin.createObject -import org.matrix.android.sdk.R import org.matrix.android.sdk.api.session.crypto.MXCryptoError 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.toModel +import org.matrix.android.sdk.api.session.initsync.InitSyncStep import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent @@ -30,9 +30,8 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult -import org.matrix.android.sdk.internal.database.helper.addOrUpdate +import org.matrix.android.sdk.internal.database.helper.addIfNecessary import org.matrix.android.sdk.internal.database.helper.addTimelineEvent -import org.matrix.android.sdk.internal.database.helper.deleteOnCascade import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.ChunkEntity @@ -40,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.find import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom @@ -48,8 +48,10 @@ import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService -import org.matrix.android.sdk.internal.session.mapWithProgress +import org.matrix.android.sdk.internal.extensions.clearWith +import org.matrix.android.sdk.internal.session.initsync.ProgressReporter +import org.matrix.android.sdk.internal.session.initsync.mapWithProgress +import org.matrix.android.sdk.internal.session.initsync.reportSubtask import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler import org.matrix.android.sdk.internal.session.room.read.FullyReadContent @@ -60,10 +62,10 @@ import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync import org.matrix.android.sdk.internal.session.sync.model.RoomSync import org.matrix.android.sdk.internal.session.sync.model.RoomSyncAccountData -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncEphemeral import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse import timber.log.Timber import javax.inject.Inject +import kotlin.math.ceil internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler, private val roomSummaryUpdater: RoomSummaryUpdater, @@ -82,21 +84,32 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy() } - fun handle( - realm: Realm, - roomsSyncResponse: RoomsSyncResponse, - isInitialSync: Boolean, - reporter: DefaultInitialSyncProgressService? = null - ) { + fun handle(realm: Realm, + roomsSyncResponse: RoomsSyncResponse, + isInitialSync: Boolean, + reporter: ProgressReporter? = null) { Timber.v("Execute transaction from $this") handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, reporter) handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, reporter) handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, reporter) } + fun handleInitSyncEphemeral(realm: Realm, + roomsSyncResponse: RoomsSyncResponse) { + roomsSyncResponse.join.forEach { roomSync -> + val ephemeralResult = roomSync.value.ephemeral + ?.roomSyncEphemeral + ?.events + ?.takeIf { it.isNotEmpty() } + ?.let { events -> handleEphemeral(realm, roomSync.key, events, true) } + + roomTypingUsersHandler.handle(realm, roomSync.key, ephemeralResult) + } + } + // PRIVATE METHODS ***************************************************************************** - private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService?) { + private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy, isInitialSync: Boolean, reporter: ProgressReporter?) { val insertType = if (isInitialSync) { EventInsertType.INITIAL_SYNC } else { @@ -104,17 +117,24 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } val syncLocalTimeStampMillis = System.currentTimeMillis() val rooms = when (handlingStrategy) { - is HandlingStrategy.JOINED -> - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_joined_rooms, 0.6f) { - handleJoinedRoom(realm, it.key, it.value, isInitialSync, insertType, syncLocalTimeStampMillis) + is HandlingStrategy.JOINED -> { + if (isInitialSync && initialSyncStrategy is InitialSyncStrategy.Optimized) { + insertJoinRoomsFromInitSync(realm, handlingStrategy, syncLocalTimeStampMillis, reporter) + // Rooms are already inserted, return an empty list + emptyList() + } else { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountJoinedRooms, 0.6f) { + handleJoinedRoom(realm, it.key, it.value, true, insertType, syncLocalTimeStampMillis) + } } + } is HandlingStrategy.INVITED -> - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.1f) { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountInvitedRooms, 0.1f) { handleInvitedRoom(realm, it.key, it.value, insertType, syncLocalTimeStampMillis) } is HandlingStrategy.LEFT -> { - handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.3f) { + handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountLeftRooms, 0.3f) { handleLeftRoom(realm, it.key, it.value, insertType, syncLocalTimeStampMillis) } } @@ -122,17 +142,60 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle realm.insertOrUpdate(rooms) } + private fun insertJoinRoomsFromInitSync(realm: Realm, + handlingStrategy: HandlingStrategy.JOINED, + syncLocalTimeStampMillis: Long, + reporter: ProgressReporter?) { + val maxSize = (initialSyncStrategy as? InitialSyncStrategy.Optimized)?.maxRoomsToInsert ?: Int.MAX_VALUE + val listSize = handlingStrategy.data.keys.size + val numberOfChunks = ceil(listSize / maxSize.toDouble()).toInt() + + if (numberOfChunks > 1) { + reportSubtask(reporter, InitSyncStep.ImportingAccountJoinedRooms, numberOfChunks, 0.6f) { + val chunkSize = listSize / numberOfChunks + Timber.v("INIT_SYNC $listSize rooms to insert, split into $numberOfChunks sublists of $chunkSize items") + // I cannot find a better way to chunk a map, so chunk the keys and then create new maps + handlingStrategy.data.keys + .chunked(chunkSize) + .forEachIndexed { index, roomIds -> + val roomEntities = roomIds + .also { Timber.v("INIT_SYNC insert ${roomIds.size} rooms") } + .map { + handleJoinedRoom( + realm = realm, + roomId = it, + roomSync = handlingStrategy.data[it] ?: error("Should not happen"), + handleEphemeralEvents = false, + insertType = EventInsertType.INITIAL_SYNC, + syncLocalTimestampMillis = syncLocalTimeStampMillis + ) + } + realm.insertOrUpdate(roomEntities) + reporter?.reportProgress(index + 1F) + } + } + } else { + // No need to split + val rooms = handlingStrategy.data.mapWithProgress(reporter, InitSyncStep.ImportingAccountJoinedRooms, 0.6f) { + handleJoinedRoom(realm, it.key, it.value, false, EventInsertType.INITIAL_SYNC, syncLocalTimeStampMillis) + } + realm.insertOrUpdate(rooms) + } + } + private fun handleJoinedRoom(realm: Realm, roomId: String, roomSync: RoomSync, - isInitialSync: Boolean, + handleEphemeralEvents: Boolean, insertType: EventInsertType, syncLocalTimestampMillis: Long): RoomEntity { Timber.v("Handle join sync for room $roomId") var ephemeralResult: EphemeralResult? = null - if (roomSync.ephemeral?.events?.isNotEmpty() == true) { - ephemeralResult = handleEphemeral(realm, roomId, roomSync.ephemeral, isInitialSync) + if (handleEphemeralEvents) { + ephemeralResult = roomSync.ephemeral?.roomSyncEphemeral?.events + ?.takeIf { it.isNotEmpty() } + ?.let { handleEphemeral(realm, roomId, it, insertType == EventInsertType.INITIAL_SYNC) } } if (roomSync.accountData?.events?.isNotEmpty() == true) { @@ -149,7 +212,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle // State event if (roomSync.state?.events?.isNotEmpty() == true) { for (event in roomSync.state.events) { - if (event.eventId == null || event.stateKey == null) { + if (event.eventId == null || event.stateKey == null || event.type == null) { continue } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } @@ -172,10 +235,9 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle roomSync.timeline.prevToken, roomSync.timeline.limited, insertType, - syncLocalTimestampMillis, - isInitialSync + syncLocalTimestampMillis ) - roomEntity.addOrUpdate(chunkEntity) + roomEntity.addIfNecessary(chunkEntity) } val hasRoomMember = roomSync.state?.events?.firstOrNull { it.type == EventType.STATE_ROOM_MEMBER @@ -206,7 +268,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle roomEntity.membership = Membership.INVITE if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) { roomSync.inviteState.events.forEach { event -> - if (event.stateKey == null) { + if (event.stateKey == null || event.type == null) { return@forEach } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } @@ -233,7 +295,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle syncLocalTimestampMillis: Long): RoomEntity { val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) for (event in roomSync.state?.events.orEmpty()) { - if (event.eventId == null || event.stateKey == null) { + if (event.eventId == null || event.stateKey == null || event.type == null) { continue } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } @@ -245,7 +307,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle roomMemberEventHandler.handle(realm, roomId, event) } for (event in roomSync.timeline?.events.orEmpty()) { - if (event.eventId == null || event.senderId == null) { + if (event.eventId == null || event.senderId == null || event.type == null) { continue } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } @@ -263,7 +325,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle val leftMember = RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst() val membership = leftMember?.membership ?: Membership.LEAVE roomEntity.membership = membership - roomEntity.chunks.deleteAllFromRealm() + roomEntity.chunks.clearWith { it.deleteOnCascade(deleteStateEvents = true, canDeleteRoot = true) } roomTypingUsersHandler.handle(realm, roomId, null) roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE) roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications) @@ -277,8 +339,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle prevToken: String? = null, isLimited: Boolean = true, insertType: EventInsertType, - syncLocalTimestampMillis: Long, - isInitialSync: Boolean): ChunkEntity { + syncLocalTimestampMillis: Long): ChunkEntity { val lastChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomEntity.roomId) val chunkEntity = if (!isLimited && lastChunk != null) { lastChunk @@ -293,12 +354,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>() for (event in eventList) { - if (event.eventId == null || event.senderId == null) { + if (event.eventId == null || event.senderId == null || event.type == null) { continue } eventIds.add(event.eventId) - if (event.isEncrypted() && !isInitialSync) { + if (event.isEncrypted() && insertType != EventInsertType.INITIAL_SYNC) { decryptIfNeeded(event, roomId) } @@ -339,8 +400,9 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle event.mxDecryptionResult = adapter.fromJson(json) } } + timelineInput.onLocalEchoSynced(roomId, it, event.eventId) // Finally delete the local echo - sendingEventEntity.deleteOnCascade() + sendingEventEntity.deleteOnCascade(true) } else { Timber.v("Can't find corresponding local echo for tx:$it") } @@ -374,10 +436,10 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle private fun handleEphemeral(realm: Realm, roomId: String, - ephemeral: RoomSyncEphemeral, + ephemeralEvents: List<Event>, isInitialSync: Boolean): EphemeralResult { var result = EphemeralResult() - for (event in ephemeral.events) { + for (event in ephemeralEvents) { when (event.type) { EventType.RECEIPT -> { @Suppress("UNCHECKED_CAST") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt index 77289f04b43a21238b7e715948bfebf5bce6464a..8e3523bc5767ab44d773d1463770b8bb317a0e95 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.sync +import okhttp3.ResponseBody import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.TimeOutInterceptor import org.matrix.android.sdk.internal.session.sync.model.SyncResponse @@ -23,6 +24,7 @@ import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.QueryMap +import retrofit2.http.Streaming internal interface SyncAPI { /** @@ -34,4 +36,15 @@ internal interface SyncAPI { @Header(TimeOutInterceptor.READ_TIMEOUT) readTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT, @Header(TimeOutInterceptor.WRITE_TIMEOUT) writeTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT ): Call<SyncResponse> + + /** + * Set all the timeouts to 1 minute by default + */ + @Streaming + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync") + fun syncStream(@QueryMap params: Map<String, String>, + @Header(TimeOutInterceptor.CONNECT_TIMEOUT) connectTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT, + @Header(TimeOutInterceptor.READ_TIMEOUT) readTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT, + @Header(TimeOutInterceptor.WRITE_TIMEOUT) writeTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT + ): Call<ResponseBody> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt index a80b062427f78057d308bb3b2a16554ea6101b1f..d17a672485d7b4ceb077bc8f3bfe59bd4c8647eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt @@ -18,17 +18,17 @@ package org.matrix.android.sdk.internal.session.sync import androidx.work.ExistingPeriodicWorkPolicy import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.R import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.pushrules.RuleScope +import org.matrix.android.sdk.api.session.initsync.InitSyncStep import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.WorkManagerProvider -import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker +import org.matrix.android.sdk.internal.session.initsync.ProgressReporter +import org.matrix.android.sdk.internal.session.initsync.reportSubtask import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask -import org.matrix.android.sdk.internal.session.reportSubtask import org.matrix.android.sdk.internal.session.sync.model.GroupsSyncResponse import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.session.sync.model.SyncResponse @@ -51,13 +51,13 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private private val cryptoService: DefaultCryptoService, private val tokenStore: SyncTokenStore, private val processEventForPushTask: ProcessEventForPushTask, - private val pushRuleService: PushRuleService, - private val initialSyncProgressService: DefaultInitialSyncProgressService) { + private val pushRuleService: PushRuleService) { - suspend fun handleResponse(syncResponse: SyncResponse, fromToken: String?) { + suspend fun handleResponse(syncResponse: SyncResponse, + fromToken: String?, + reporter: ProgressReporter?) { val isInitialSync = fromToken == null Timber.v("Start handling sync, is InitialSync: $isInitialSync") - val reporter = initialSyncProgressService.takeIf { isInitialSync } measureTimeMillis { if (!cryptoService.isStarted()) { @@ -73,7 +73,7 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private // to ensure to decrypt them properly measureTimeMillis { Timber.v("Handle toDevice") - reportSubtask(reporter, R.string.initial_sync_start_importing_account_crypto, 100, 0.1f) { + reportSubtask(reporter, InitSyncStep.ImportingAccountCrypto, 100, 0.1f) { if (syncResponse.toDevice != null) { cryptoSyncHandler.handleToDevice(syncResponse.toDevice, reporter) } @@ -85,7 +85,7 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private monarchy.awaitTransaction { realm -> measureTimeMillis { Timber.v("Handle rooms") - reportSubtask(reporter, R.string.initial_sync_start_importing_account_rooms, 100, 0.7f) { + reportSubtask(reporter, InitSyncStep.ImportingAccountRoom, 1, 0.7f) { if (syncResponse.rooms != null) { roomSyncHandler.handle(realm, syncResponse.rooms, isInitialSync, reporter) } @@ -95,7 +95,7 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private } measureTimeMillis { - reportSubtask(reporter, R.string.initial_sync_start_importing_account_groups, 100, 0.1f) { + reportSubtask(reporter, InitSyncStep.ImportingAccountGroups, 1, 0.1f) { Timber.v("Handle groups") if (syncResponse.groups != null) { groupSyncHandler.handle(realm, syncResponse.groups, reporter) @@ -106,7 +106,7 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private } measureTimeMillis { - reportSubtask(reporter, R.string.initial_sync_start_importing_account_data, 100, 0.1f) { + reportSubtask(reporter, InitSyncStep.ImportingAccountData, 1, 0.1f) { Timber.v("Handle accountData") userAccountDataSyncHandler.handle(realm, syncResponse.accountData) } @@ -128,6 +128,15 @@ internal class SyncResponseHandler @Inject constructor(@SessionDatabase private cryptoSyncHandler.onSyncCompleted(syncResponse) } + suspend fun handleInitSyncSecondTransaction(syncResponse: SyncResponse) { + // Start another transaction to handle the ephemeral events + monarchy.awaitTransaction { realm -> + if (syncResponse.rooms != null) { + roomSyncHandler.handleInitSyncEphemeral(realm, syncResponse.rooms) + } + } + } + /** * At the moment we don't get any group data through the sync, so we poll where every hour. * You can also force to refetch group data using [Group] API. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index 7c38230065ff8bb5c6ff3e5001bb0e95b0a5a64e..00060a33b11db2fee0dbcf34228b59520e651147 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -16,18 +16,29 @@ package org.matrix.android.sdk.internal.session.sync -import org.matrix.android.sdk.R +import okhttp3.ResponseBody +import org.matrix.android.sdk.api.session.initsync.InitSyncStep +import org.matrix.android.sdk.internal.di.SessionFilesDirectory import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.TimeOutInterceptor import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService +import org.matrix.android.sdk.internal.network.toFailure import org.matrix.android.sdk.internal.session.filter.FilterRepository import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask +import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService +import org.matrix.android.sdk.internal.session.initsync.reportSubtask +import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral import org.matrix.android.sdk.internal.session.sync.model.SyncResponse +import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser import org.matrix.android.sdk.internal.session.user.UserStore import org.matrix.android.sdk.internal.task.Task +import org.matrix.android.sdk.internal.util.logDuration +import retrofit2.Response +import retrofit2.awaitResponse import timber.log.Timber +import java.io.File +import java.net.SocketTimeoutException import javax.inject.Inject internal interface SyncTask : Task<SyncTask.Params, Unit> { @@ -48,11 +59,19 @@ internal class DefaultSyncTask @Inject constructor( private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask, private val userStore: UserStore, private val syncTaskSequencer: SyncTaskSequencer, - private val globalErrorReceiver: GlobalErrorReceiver + private val globalErrorReceiver: GlobalErrorReceiver, + @SessionFilesDirectory + private val fileDirectory: File, + private val syncResponseParser: InitialSyncResponseParser ) : SyncTask { - override suspend fun execute(params: SyncTask.Params) = syncTaskSequencer.post { - doSync(params) + private val workingDir = File(fileDirectory, "is") + private val initialSyncStatusRepository: InitialSyncStatusRepository = FileInitialSyncStatusRepository(workingDir) + + override suspend fun execute(params: SyncTask.Params) { + syncTaskSequencer.post { + doSync(params) + } } private suspend fun doSync(params: SyncTask.Params) { @@ -73,28 +92,136 @@ internal class DefaultSyncTask @Inject constructor( if (isInitialSync) { // We might want to get the user information in parallel too userStore.createOrUpdate(userId) - initialSyncProgressService.endAll() - initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100) + initialSyncProgressService.startRoot(InitSyncStep.ImportingAccount, 100) } // Maybe refresh the home server capabilities data we know - getHomeServerCapabilitiesTask.execute(Unit) + getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false)) val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) - val syncResponse = executeRequest<SyncResponse>(globalErrorReceiver) { - apiCall = syncAPI.sync( - params = requestParams, - readTimeOut = readTimeOut - ) - } - syncResponseHandler.handleResponse(syncResponse, token) if (isInitialSync) { + Timber.v("INIT_SYNC with filter: ${requestParams["filter"]}") + val initSyncStrategy = initialSyncStrategy + var syncResp: SyncResponse? = null + logDuration("INIT_SYNC strategy: $initSyncStrategy") { + if (initSyncStrategy is InitialSyncStrategy.Optimized) { + val file = downloadInitSyncResponse(requestParams) + syncResp = reportSubtask(initialSyncProgressService, InitSyncStep.ImportingAccount, 1, 0.7F) { + handleSyncFile(file, initSyncStrategy) + } + } else { + val syncResponse = logDuration("INIT_SYNC Request") { + executeRequest<SyncResponse>(globalErrorReceiver) { + apiCall = syncAPI.sync( + params = requestParams, + readTimeOut = readTimeOut + ) + } + } + + logDuration("INIT_SYNC Database insertion") { + syncResponseHandler.handleResponse(syncResponse, token, initialSyncProgressService) + } + } + } initialSyncProgressService.endAll() + + if (initSyncStrategy is InitialSyncStrategy.Optimized) { + logDuration("INIT_SYNC Handle ephemeral") { + syncResponseHandler.handleInitSyncSecondTransaction(syncResp!!) + } + initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS) + // Delete all files + workingDir.deleteRecursively() + } + } else { + val syncResponse = executeRequest<SyncResponse>(globalErrorReceiver) { + apiCall = syncAPI.sync( + params = requestParams, + readTimeOut = readTimeOut + ) + } + syncResponseHandler.handleResponse(syncResponse, token, null) } Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}") } + private suspend fun downloadInitSyncResponse(requestParams: Map<String, String>): File { + workingDir.mkdirs() + val workingFile = File(workingDir, "initSync.json") + val status = initialSyncStatusRepository.getStep() + if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) { + Timber.v("INIT_SYNC file is already here") + reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) { + // Empty task + } + } else { + initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING) + val syncResponse = logDuration("INIT_SYNC Perform server request") { + reportSubtask(initialSyncProgressService, InitSyncStep.ServerComputing, 1, 0.2f) { + getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT) + } + } + + if (syncResponse.isSuccessful) { + logDuration("INIT_SYNC Download and save to file") { + reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.1f) { + syncResponse.body()?.byteStream()?.use { inputStream -> + workingFile.outputStream().use { outputStream -> + inputStream.copyTo(outputStream) + } + } + } + } + } else { + throw syncResponse.toFailure(globalErrorReceiver) + .also { Timber.w("INIT_SYNC request failure: $this") } + } + initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADED) + } + return workingFile + } + + private suspend fun getSyncResponse(requestParams: Map<String, String>, maxNumberOfRetries: Int): Response<ResponseBody> { + var retry = maxNumberOfRetries + while (true) { + retry-- + try { + return syncAPI.syncStream( + params = requestParams + ).awaitResponse() + } catch (throwable: Throwable) { + if (throwable is SocketTimeoutException && retry > 0) { + Timber.w("INIT_SYNC timeout retry left: $retry") + } else { + Timber.e(throwable, "INIT_SYNC timeout, no retry left, or other error") + throw throwable + } + } + } + } + + private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized): SyncResponse { + return logDuration("INIT_SYNC handleSyncFile()") { + val syncResponse = logDuration("INIT_SYNC Read file and parse") { + syncResponseParser.parse(initSyncStrategy, workingFile) + } + initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED) + // Log some stats + val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0 + val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored } + Timber.v("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files") + + logDuration("INIT_SYNC Database insertion") { + syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService) + } + syncResponse + } + } + companion object { + private const val MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT = 50 + private const val TIMEOUT_MARGIN: Long = 10_000 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt index 0e549172f30f275d08dfb7519568280f255bedf4..449d47abe5580670a8a245623b6210062fb3da81 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt @@ -16,8 +16,10 @@ package org.matrix.android.sdk.internal.session.sync -import com.squareup.moshi.Moshi import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import io.realm.RealmList +import io.realm.kotlin.where import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.pushrules.RuleSetKey import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse @@ -37,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields +import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.database.query.getDirectRooms import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where @@ -50,9 +53,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.IgnoredUse import org.matrix.android.sdk.internal.session.sync.model.accountdata.UserAccountDataSync import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask -import io.realm.Realm -import io.realm.RealmList -import io.realm.kotlin.where import timber.log.Timber import javax.inject.Inject @@ -60,7 +60,6 @@ internal class UserAccountDataSyncHandler @Inject constructor( @SessionDatabase private val monarchy: Monarchy, @UserId private val userId: String, private val directChatsHelper: DirectChatsHelper, - private val moshi: Moshi, private val updateUserAccountDataTask: UpdateUserAccountDataTask) { fun handle(realm: Realm, accountData: UserAccountDataSync?) { @@ -113,7 +112,7 @@ internal class UserAccountDataSyncHandler @Inject constructor( val pushRules = event.content.toModel<GetPushRulesResponse>() ?: return realm.where(PushRulesEntity::class.java) .findAll() - .deleteAllFromRealm() + .forEach { it.deleteOnCascade() } // Save only global rules for the moment val globalRules = pushRules.global diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt new file mode 100644 index 0000000000000000000000000000000000000000..938168b5f4bb368c0863c763f6813cca5a79e078 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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.session.sync.model + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonClass +import com.squareup.moshi.JsonReader +import okio.buffer +import okio.source +import java.io.File + +@JsonClass(generateAdapter = false) +internal sealed class LazyRoomSyncEphemeral { + data class Parsed(val _roomSyncEphemeral: RoomSyncEphemeral) : LazyRoomSyncEphemeral() + data class Stored(val roomSyncEphemeralAdapter: JsonAdapter<RoomSyncEphemeral>, val file: File) : LazyRoomSyncEphemeral() + + val roomSyncEphemeral: RoomSyncEphemeral + get() { + return when (this) { + is Parsed -> _roomSyncEphemeral + is Stored -> { + // Parse the file now + file.inputStream().use { pos -> + roomSyncEphemeralAdapter.fromJson(JsonReader.of(pos.source().buffer()))!! + } + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt index 212d319139601b09f76566d20d9e79535e20ad22..9aed0d37d688d6659fe649ead540323d18091759 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt @@ -34,7 +34,7 @@ internal data class RoomSync( /** * The ephemeral events in the room that aren't recorded in the timeline or state of the room (e.g. typing, receipts). */ - @Json(name = "ephemeral") val ephemeral: RoomSyncEphemeral? = null, + @Json(name = "ephemeral") val ephemeral: LazyRoomSyncEphemeral? = null, /** * The account data events for the room (e.g. tags). diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef56802a668c2206d0d73f24f1be0732c098ec3c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 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.session.sync.parsing + +import com.squareup.moshi.FromJson +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.ToJson +import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy +import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral +import org.matrix.android.sdk.internal.session.sync.model.RoomSyncEphemeral +import timber.log.Timber +import java.io.File +import java.util.concurrent.atomic.AtomicInteger + +internal class DefaultLazyRoomSyncEphemeralJsonAdapter { + + @FromJson + fun fromJson(reader: JsonReader, delegate: JsonAdapter<RoomSyncEphemeral>): LazyRoomSyncEphemeral? { + val roomSyncEphemeral = delegate.fromJson(reader) ?: return null + return LazyRoomSyncEphemeral.Parsed(roomSyncEphemeral) + } + + @ToJson + fun toJson(writer: JsonWriter, value: LazyRoomSyncEphemeral?) { + // This Adapter is not supposed to serialize object + Timber.v("To json $value with $writer") + throw UnsupportedOperationException() + } +} + +internal class SplitLazyRoomSyncJsonAdapter( + private val workingDirectory: File, + private val syncStrategy: InitialSyncStrategy.Optimized +) { + private val atomicInteger = AtomicInteger(0) + + private fun createFile(): File { + val index = atomicInteger.getAndIncrement() + return File(workingDirectory, "room_$index.json") + } + + @FromJson + fun fromJson(reader: JsonReader, delegate: JsonAdapter<RoomSyncEphemeral>): LazyRoomSyncEphemeral? { + val path = reader.path + val json = reader.nextSource().inputStream().bufferedReader().use { + it.readText() + } + val limit = syncStrategy.minSizeToStoreInFile + return if (json.length > limit) { + Timber.v("INIT_SYNC $path content length: ${json.length} copy to a file") + // Copy the source to a file + val file = createFile() + file.writeText(json) + LazyRoomSyncEphemeral.Stored(delegate, file) + } else { + Timber.v("INIT_SYNC $path content length: ${json.length} parse it now") + val roomSync = delegate.fromJson(json) ?: return null + LazyRoomSyncEphemeral.Parsed(roomSync) + } + } + + @ToJson + fun toJson(writer: JsonWriter, value: LazyRoomSyncEphemeral?) { + // This Adapter is not supposed to serialize object + Timber.v("To json $value with $writer") + throw UnsupportedOperationException() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt new file mode 100644 index 0000000000000000000000000000000000000000..ae7b2a44681f2369b5301f6cd9eeaf5b1d8487d8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 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.session.sync.parsing + +import com.squareup.moshi.Moshi +import okio.buffer +import okio.source +import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy +import org.matrix.android.sdk.internal.session.sync.model.SyncResponse +import timber.log.Timber +import java.io.File +import javax.inject.Inject + +internal class InitialSyncResponseParser @Inject constructor(private val moshi: Moshi) { + + fun parse(syncStrategy: InitialSyncStrategy.Optimized, workingFile: File): SyncResponse { + val syncResponseLength = workingFile.length().toInt() + Timber.v("INIT_SYNC Sync file size is $syncResponseLength bytes") + val shouldSplit = syncResponseLength >= syncStrategy.minSizeToSplit + Timber.v("INIT_SYNC should split in several files: $shouldSplit") + return getMoshi(syncStrategy, workingFile.parentFile!!, shouldSplit) + .adapter(SyncResponse::class.java) + .fromJson(workingFile.source().buffer())!! + } + + private fun getMoshi(syncStrategy: InitialSyncStrategy.Optimized, workingDirectory: File, shouldSplit: Boolean): Moshi { + // If we don't have to split we'll rely on the already default moshi + if (!shouldSplit) return moshi + // Otherwise, we create a new adapter for handling Map of Lazy sync + return moshi.newBuilder() + .add(SplitLazyRoomSyncJsonAdapter(workingDirectory, syncStrategy)) + .build() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt new file mode 100644 index 0000000000000000000000000000000000000000..13829c400a19e3126a42b54b4ea9098886c92656 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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.session.thirdparty + +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser +import javax.inject.Inject + +internal class DefaultThirdPartyService @Inject constructor(private val getThirdPartyProtocolTask: GetThirdPartyProtocolsTask, + private val getThirdPartyUserTask: GetThirdPartyUserTask) + : ThirdPartyService { + + override suspend fun getThirdPartyProtocols(): Map<String, ThirdPartyProtocol> { + return getThirdPartyProtocolTask.execute(Unit) + } + + override suspend fun getThirdPartyUser(protocol: String, fields: Map<String, String>): List<ThirdPartyUser> { + val taskParams = GetThirdPartyUserTask.Params( + protocol = protocol, + fields = fields + ) + return getThirdPartyUserTask.execute(taskParams) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyProtocolsTask.kt similarity index 86% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyProtocolsTask.kt index 3477aa671ea6ba1283a598e31685e6ca2070a72c..fd1ed741e991b3f80506f4571abcab2c908ad394 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyProtocolsTask.kt @@ -14,25 +14,24 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.room.directory +package org.matrix.android.sdk.internal.session.thirdparty import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject internal interface GetThirdPartyProtocolsTask : Task<Unit, Map<String, ThirdPartyProtocol>> internal class DefaultGetThirdPartyProtocolsTask @Inject constructor( - private val roomAPI: RoomAPI, + private val thirdPartyAPI: ThirdPartyAPI, private val globalErrorReceiver: GlobalErrorReceiver ) : GetThirdPartyProtocolsTask { override suspend fun execute(params: Unit): Map<String, ThirdPartyProtocol> { return executeRequest(globalErrorReceiver) { - apiCall = roomAPI.thirdPartyProtocols() + apiCall = thirdPartyAPI.thirdPartyProtocols() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt new file mode 100644 index 0000000000000000000000000000000000000000..01a8b576783c404f53409176ec48d6f5a48b6c6b --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt @@ -0,0 +1,43 @@ +/* + * 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.session.thirdparty + +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetThirdPartyUserTask : Task<GetThirdPartyUserTask.Params, List<ThirdPartyUser>> { + + data class Params( + val protocol: String, + val fields: Map<String, String> = emptyMap() + ) +} + +internal class DefaultGetThirdPartyUserTask @Inject constructor( + private val thirdPartyAPI: ThirdPartyAPI, + private val globalErrorReceiver: GlobalErrorReceiver +) : GetThirdPartyUserTask { + + override suspend fun execute(params: GetThirdPartyUserTask.Params): List<ThirdPartyUser> { + return executeRequest(globalErrorReceiver) { + apiCall = thirdPartyAPI.getThirdPartyUser(params.protocol, params.fields) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt new file mode 100644 index 0000000000000000000000000000000000000000..0c60a27341239c661054f993e2cfbe757a735557 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt @@ -0,0 +1,44 @@ +/* + * 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.session.thirdparty + +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser +import org.matrix.android.sdk.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface ThirdPartyAPI { + + /** + * Get the third party server protocols. + * + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1.html#get-matrix-client-r0-thirdparty-protocols + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols") + fun thirdPartyProtocols(): Call<Map<String, ThirdPartyProtocol>> + + /** + * Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. + * + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user-protocol + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols/user/{protocol}") + fun getThirdPartyUser(@Path("protocol") protocol: String, @QueryMap params: Map<String, String>?): Call<List<ThirdPartyUser>> +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..d3acd7a9f34267e806c511d64149bb4aa1450b08 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt @@ -0,0 +1,47 @@ +/* + * 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.session.thirdparty + +import dagger.Binds +import dagger.Module +import dagger.Provides +import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService +import org.matrix.android.sdk.internal.session.SessionScope +import retrofit2.Retrofit + +@Module +internal abstract class ThirdPartyModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesThirdPartyAPI(retrofit: Retrofit): ThirdPartyAPI { + return retrofit.create(ThirdPartyAPI::class.java) + } + } + + @Binds + abstract fun bindThirdPartyService(service: DefaultThirdPartyService): ThirdPartyService + + @Binds + abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask + + @Binds + abstract fun bindGetThirdPartyUserTask(task: DefaultGetThirdPartyUserTask): GetThirdPartyUserTask +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt index 3e4e430e3b81ea8377dacfb2de9aa631ee5c255b..9f5a9360ee9a54fed08196a7954b638e5302dcb4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt @@ -50,6 +50,10 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage return widgetManager.getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes) } + override fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? { + return widgetManager.getWidgetComputedUrl(widget, isLightTheme) + } + override fun getRoomWidgetsLive( roomId: String, widgetId: QueryStringValue, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt index 94dba75205c75ca109599b36194a7a042843cff5..0937f6d18be4255bd02d1dbcdf8ea04e36c847d4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt @@ -20,11 +20,12 @@ import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.widgets.WidgetURLFormatter +import org.matrix.android.sdk.api.util.appendParamToUrl +import org.matrix.android.sdk.api.util.appendParamsToUrl import org.matrix.android.sdk.internal.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.widgets.token.GetScalarTokenTask -import java.net.URLEncoder import javax.inject.Inject @SessionScope @@ -36,12 +37,12 @@ internal class DefaultWidgetURLFormatter @Inject constructor(private val integra private lateinit var currentConfig: IntegrationManagerConfig private var whiteListedUrls: List<String> = emptyList() - override fun onStart() { + override fun onSessionStarted() { setupWithConfiguration() integrationManager.addListener(this) } - override fun onStop() { + override fun onSessionStopped() { integrationManager.removeListener(this) } @@ -90,25 +91,4 @@ internal class DefaultWidgetURLFormatter @Inject constructor(private val integra } return false } - - private fun StringBuilder.appendParamsToUrl(params: Map<String, String>): StringBuilder { - params.forEach { (param, value) -> - appendParamToUrl(param, value) - } - return this - } - - private fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder { - if (contains("?")) { - append("&") - } else { - append("?") - } - - append(param) - append("=") - append(URLEncoder.encode(value, "utf-8")) - - return this - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt index 329903f15b5dbce1543720f9559ac6f5b43eb999..73a4cc697dc320bd03e401f2a943858fc3506144 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt @@ -62,12 +62,12 @@ internal class WidgetManager @Inject constructor(private val integrationManager: private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) - override fun onStart() { + override fun onSessionStarted() { lifecycleRegistry.currentState = Lifecycle.State.STARTED integrationManager.addListener(this) } - override fun onStop() { + override fun onSessionStopped() { integrationManager.removeListener(this) lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } @@ -104,6 +104,10 @@ internal class WidgetManager @Inject constructor(private val integrationManager: return widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes) } + fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? { + return widgetFactory.computeURL(widget, isLightTheme) + } + private fun List<Event>.mapEventsToWidgets(widgetTypes: Set<String>? = null, excludedTypes: Set<String>? = null): List<Widget> { val widgetEvents = this diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt index 000b9e38b9e8027774fb33329f48cffd4eafe5c5..a469a9fe976722907d8789558c0f22957e0c2d23 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.widgets.helper +import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.sender.SenderInfo @@ -31,6 +32,7 @@ import javax.inject.Inject internal class WidgetFactory @Inject constructor(private val userDataSource: UserDataSource, private val realmSessionProvider: RealmSessionProvider, + private val urlResolver: ContentUrlResolver, @UserId private val userId: String) { fun create(widgetEvent: Event): Widget? { @@ -53,35 +55,47 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use } } val isAddedByMe = widgetEvent.senderId == userId - val computedUrl = widgetContent.computeURL(widgetEvent.roomId, widgetId) return Widget( widgetContent = widgetContent, event = widgetEvent, widgetId = widgetId, senderInfo = senderInfo, isAddedByMe = isAddedByMe, - computedUrl = computedUrl, type = WidgetType.fromString(type) ) } - private fun WidgetContent.computeURL(roomId: String?, widgetId: String): String? { - var computedUrl = url ?: return null + // Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33 + fun computeURL(widget: Widget, isLightTheme: Boolean): String? { + var computedUrl = widget.widgetContent.url ?: return null val myUser = userDataSource.getUser(userId) - computedUrl = computedUrl - .replace("\$matrix_user_id", userId) - .replace("\$matrix_display_name", myUser?.displayName ?: userId) - .replace("\$matrix_avatar_url", myUser?.avatarUrl ?: "") - .replace("\$matrix_widget_id", widgetId) - if (roomId != null) { - computedUrl = computedUrl.replace("\$matrix_room_id", roomId) - } - for ((key, value) in data) { - if (value is String) { - computedUrl = computedUrl.replace("$$key", URLEncoder.encode(value, "utf-8")) - } + val keyValue = widget.widgetContent.data.mapKeys { "\$${it.key}" }.toMutableMap() + + keyValue[WIDGET_PATTERN_MATRIX_USER_ID] = userId + keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId + keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser?.avatarUrl) ?: "" + keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widget.widgetId + keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = widget.event.roomId ?: "" + keyValue[WIDGET_PATTERN_THEME] = getTheme(isLightTheme) + + for ((key, value) in keyValue) { + computedUrl = computedUrl.replace(key, URLEncoder.encode(value.toString(), "utf-8")) } return computedUrl } + + private fun getTheme(isLightTheme: Boolean): String { + return if (isLightTheme) "light" else "dark" + } + + companion object { + // Value to be replaced in URLS + const val WIDGET_PATTERN_MATRIX_USER_ID = "\$matrix_user_id" + const val WIDGET_PATTERN_MATRIX_DISPLAY_NAME = "\$matrix_display_name" + const val WIDGET_PATTERN_MATRIX_AVATAR_URL = "\$matrix_avatar_url" + const val WIDGET_PATTERN_MATRIX_WIDGET_ID = "\$matrix_widget_id" + const val WIDGET_PATTERN_MATRIX_ROOM_ID = "\$matrix_room_id" + const val WIDGET_PATTERN_THEME = "\$theme" + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt new file mode 100644 index 0000000000000000000000000000000000000000..329b100497e80a151e08508a58b664702dbf2c6d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2021 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.util + +import androidx.core.text.HtmlCompat + +internal fun String.unescapeHtml(): String { + return HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_LEGACY).toString() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe68b49a5c7712a6d85c7375f0e2d958cb71c4db --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt @@ -0,0 +1,51 @@ +/* + * 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.util + +import org.matrix.android.sdk.BuildConfig +import timber.log.Timber + +internal suspend fun <T> logDuration(message: String, + block: suspend () -> T): T { + Timber.v("$message -- BEGIN") + val start = System.currentTimeMillis() + val result = logRamUsage(message) { + block() + } + val duration = System.currentTimeMillis() - start + Timber.v("$message -- END duration: $duration ms") + + return result +} + +internal suspend fun <T> logRamUsage(message: String, block: suspend () -> T): T { + return if (BuildConfig.DEBUG) { + val runtime = Runtime.getRuntime() + runtime.gc() + val freeMemoryInMb = runtime.freeMemory() / 1048576L + val usedMemInMBStart = runtime.totalMemory() / 1048576L - freeMemoryInMb + Timber.v("$message -- BEGIN (free memory: $freeMemoryInMb MB)") + val result = block() + runtime.gc() + val usedMemInMBEnd = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L + val usedMemInMBDiff = usedMemInMBEnd - usedMemInMBStart + Timber.v("$message -- END RAM usage: $usedMemInMBDiff MB") + result + } else { + block() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringProvider.kt deleted file mode 100644 index 69d50680a71256a797c1fc82ea87607c6311d895..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringProvider.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.util - -import android.content.res.Resources -import androidx.annotation.NonNull -import androidx.annotation.PluralsRes -import androidx.annotation.StringRes -import dagger.Reusable -import javax.inject.Inject - -@Reusable -internal class StringProvider @Inject constructor(private val resources: Resources) { - - /** - * Returns a localized string from the application's package's - * default string table. - * - * @param resId Resource id for the string - * @return The string data associated with the resource, stripped of styled - * text information. - */ - @NonNull - fun getString(@StringRes resId: Int): String { - return resources.getString(resId) - } - - /** - * Returns a localized formatted string from the application's package's - * default string table, substituting the format arguments as defined in - * [java.util.Formatter] and [java.lang.String.format]. - * - * @param resId Resource id for the format string - * @param formatArgs The format arguments that will be used for - * substitution. - * @return The string data associated with the resource, formatted and - * stripped of styled text information. - */ - @NonNull - fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String { - return resources.getString(resId, *formatArgs) - } - - @NonNull - fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any?): String { - return resources.getQuantityString(resId, quantity, *formatArgs) - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt index ecfbe311f1d8d3742922b8ae2e9805c65bd521b9..2fabca4be86e5f7a66c237788d3992fde700452f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt @@ -71,3 +71,10 @@ fun String.caseInsensitiveFind(subString: String): Boolean { return false } + +internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex() + +/** + * Strip all the UTF-8 chars which are actually spaces + */ +internal fun String.replaceSpaceChars() = replace(spaceChars, "") diff --git a/matrix-sdk-android/src/main/res/values-ar/strings.xml b/matrix-sdk-android/src/main/res/values-ar/strings.xml deleted file mode 100644 index 0fc7bd1b49597ab4b3710c582b409d20fa4764d2..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-ar/strings.xml +++ /dev/null @@ -1,147 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_user_sent_image">أرسل â¨%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_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_answered_call">ردّ â¨%s⩠على المكالمة.</string> - <string name="notice_ended_call">أنهى â¨%s⩠المكالمة.</string> - <string name="notice_made_future_room_visibility">جعل â¨%1$s⩠تأريخ الغرÙØ© مستقبلًا ظاهرًا على â¨%2$sâ©</string> - <string name="notice_room_visibility_invited">كل أعضاء الغرÙØ© من Ù„Øظة دعوتهم.</string> - <string name="notice_room_visibility_joined">كل أعضاء الغرÙØ© من Ù„Øظة انضمامهم.</string> - <string name="notice_room_visibility_shared">كل أعضاء الغرÙØ©.</string> - <string name="notice_room_visibility_world_readable">الكل.</string> - <string name="notice_room_visibility_unknown">المجهول (â¨%sâ©).</string> - <string name="notice_end_to_end">Ùعّل â¨%1$s⩠تعمية الطرÙين (â¨%2$sâ©)</string> - - <string name="notice_requested_voip_conference">طلب â¨%1$s⩠اجتماع VoIP</string> - <string name="notice_voip_started">بدأ اجتماع VoIP</string> - <string name="notice_voip_finished">انتهى اجتماع VoIP</string> - - <string name="notice_room_name_removed">أزال â¨%1$s⩠اسم الغرÙØ©</string> - <string name="notice_room_topic_removed">أزال â¨%1$s⩠موضوع الغرÙØ©</string> - <string name="notice_profile_change_redacted">Øدّث â¨%1$s⩠اللاØØ© â¨%2$sâ©</string> - <string name="notice_room_third_party_invite">أرسل â¨%1$s⩠دعوة إلى â¨%2$s⩠للانضمام إلى الغرÙØ©</string> - <string name="notice_crypto_unable_to_decrypt">** تعذّر ÙÙƒ التعمية: â¨%sâ© **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">لم ÙŠÙرسل جهاز المرسل Ù…ÙØ§ØªÙŠØ Ù‡Ø°Ù‡ الرسالة.</string> - - <string name="unable_to_send_message">تعذّر إرسال الرسالة</string> - - <string name="message_failed_to_upload">Ùشل رÙع الصورة</string> - - <string name="network_error">خطأ ÙÙŠ الشبكة</string> - <string name="matrix_error">خطأ ÙÙŠ «ماترÙكس»</string> - - <string name="room_error_join_failed_empty_room">لا يمكنك Øاليًا الانضمام ثانيةً إلى غرÙØ© Ùارغة.</string> - - <string name="encrypted_message">رسالة معمّاة</string> - - <string name="medium_email">عنوان البريد الإلكتروني</string> - <string name="medium_phone_number">رقم الهاتÙ</string> - - <string name="summary_message">â€â€â¨%1$sâ©: â€â¨%2$sâ©</string> - <string name="notice_room_withdraw">انسØب â¨%1$s⩠من دعوة â¨%2$sâ©</string> - <string name="notice_placed_video_call">أجرى â¨%s⩠مكالمة مرئية.</string> - <string name="notice_placed_voice_call">أجرى â¨%s⩠مكالمة صوتية.</string> - <string name="notice_room_third_party_registered_invite">قَبÙÙ„ â¨%1$s⩠دعوة â¨%2$sâ©</string> - - <string name="could_not_redact">تعذر التهذيب</string> - <string name="summary_user_sent_sticker">أرسل â¨%1$s⩠ملصقًا.</string> - - <string name="notice_avatar_changed_too">(تغيّرت الصورة أيضا)</string> - - <string name="room_displayname_invite_from">دعوة من â¨%sâ©</string> - <string name="room_displayname_empty_room">غرÙØ© Ùارغة</string> - - <string name="room_displayname_two_members">â€â¨%1$sâ© Ùˆ â¨%2$sâ©</string> - <string name="room_displayname_room_invite">دعوة إلى غرÙØ©</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="zero"></item> - <item quantity="one"></item> - <item quantity="two"></item> - <item quantity="few"></item> - <item quantity="many"></item> - <item quantity="other"></item> - </plurals> - - <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">أنشأ â¨%1$s⩠الغرÙØ©</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">رقّى â¨%s⩠هذه الغرÙØ©.</string> - <string name="notice_room_update_by_you">رقّيت هذه الغرÙØ©.</string> - - <string name="notice_requested_voip_conference_by_you">طلبت اجتماع VoIP</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_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_by_you">أرسلت دعوة إلى â¨%1$s⩠للانضمام إلى الغرÙØ©</string> - <string name="notice_room_third_party_revoked_invite">سØب â¨%1$s⩠دعوة â¨%2$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_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="initial_sync_start_importing_account">المزامنة الأولية: -\nيستورد الØساب…</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-ar/strings_sas.xml b/matrix-sdk-android/src/main/res/values-ar/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..423a8332bfd1a45312da061b7d3e8498fc715637 --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-ar/strings_sas.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> + <string name="verification_emoji_dog">كَلب</string> + <string name="verification_emoji_cat">Ù‡Ùرَّة</string> + <string name="verification_emoji_lion">أَسَد</string> + <string name="verification_emoji_horse">ØÙصَان</string> + <string name="verification_emoji_unicorn">ØÙصَانٌ بÙقَرن</string> + <string name="verification_emoji_pig">Ø®ÙنزÙير</string> + <string name="verification_emoji_elephant">ÙÙيل</string> + <string name="verification_emoji_rabbit">أَرنَب</string> + <string name="verification_emoji_panda">باندَا</string> + <string name="verification_emoji_rooster">دÙيك</string> + <string name="verification_emoji_penguin">بÙطريق</string> + <string name="verification_emoji_turtle">سÙÙ„ØÙاة</string> + <string name="verification_emoji_fish">سَمَكَة</string> + <string name="verification_emoji_octopus">Ø£ÙخطÙبÙوط</string> + <string name="verification_emoji_butterfly">Ùَرَاشَة</string> + <string name="verification_emoji_flower">زَهرَة</string> + <string name="verification_emoji_tree">شَجَرَة</string> + <string name="verification_emoji_cactus">صبار</string> + <string name="verification_emoji_mushroom">ÙÙطر</string> + <string name="verification_emoji_globe">ÙƒÙرَةٌ أرضÙيَّة</string> + <string name="verification_emoji_moon">قَمَر</string> + <string name="verification_emoji_cloud">سَØابَة</string> + <string name="verification_emoji_fire">نار</string> + <string name="verification_emoji_banana">مَوزَة</string> + <string name="verification_emoji_apple">تÙÙَّاØÙŽØ©</string> + <string name="verification_emoji_strawberry">ÙَراوÙÙ„ÙŽØ©</string> + <string name="verification_emoji_corn">Ø°Ùرَة</string> + <string name="verification_emoji_pizza">بÙيتزا</string> + <string name="verification_emoji_cake">كَعكَة</string> + <string name="verification_emoji_heart">قَلب</string> + <string name="verification_emoji_smiley">اÙبتÙسَامَة</string> + <string name="verification_emoji_robot">رÙوبÙوت</string> + <string name="verification_emoji_hat">Ù‚Ùبَّعَة</string> + <string name="verification_emoji_glasses">نَظَّارَة</string> + <string name="verification_emoji_spanner">Ù…ÙÙتَاØ٠رَبط</string> + <string name="verification_emoji_santa">سانتا</string> + <string name="verification_emoji_thumbs_up">رَÙع٠إÙبهَام</string> + <string name="verification_emoji_umbrella">Ù…Ùظَلَّة</string> + <string name="verification_emoji_hourglass">سَاعَةٌ رَملÙيَّة</string> + <string name="verification_emoji_clock">سَاعَة</string> + <string name="verification_emoji_gift">هَدÙيَّة</string> + <string name="verification_emoji_light_bulb">Ù…ÙصبَاØ</string> + <string name="verification_emoji_book">ÙƒÙتَاب</string> + <string name="verification_emoji_pencil">قَلَم٠رَصاص</string> + <string name="verification_emoji_paperclip">Ù…Ùشبَك٠وَرَق</string> + <string name="verification_emoji_scissors">Ù…Ùقَصّ</string> + <string name="verification_emoji_lock">Ù‚ÙÙÙ„</string> + <string name="verification_emoji_key">Ù…ÙÙتَاØ</string> + <string name="verification_emoji_hammer">Ù…Ùطرَقَة</string> + <string name="verification_emoji_telephone">تÙÙ„ÙÙÙون</string> + <string name="verification_emoji_flag">عَلَم</string> + <string name="verification_emoji_train">Ù‚Ùطَار</string> + <string name="verification_emoji_bicycle">دَرّاجَة</string> + <string name="verification_emoji_aeroplane">طَائÙرة</string> + <string name="verification_emoji_rocket">صَارÙوخ</string> + <string name="verification_emoji_trophy">كَأس٠النَّصر</string> + <string name="verification_emoji_ball">ÙƒÙرَة</string> + <string name="verification_emoji_guitar">غيتار</string> + <string name="verification_emoji_trumpet">بÙوق</string> + <string name="verification_emoji_bell">جَرَس</string> + <string name="verification_emoji_anchor">Ù…Ùرسَاة</string> + <string name="verification_emoji_headphones">سَمّاعَة رَأس</string> + <string name="verification_emoji_folder">Ù…Ùجَلَّد</string> + <string name="verification_emoji_pin">دَبّÙوس</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-az/strings.xml b/matrix-sdk-android/src/main/res/values-az/strings.xml deleted file mode 100644 index 1f366c647f1632b8d9e8ae9d41abcd648f549a3a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-az/strings.xml +++ /dev/null @@ -1,116 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s ÅŸÉ™kil göndÉ™rdi.</string> - <string name="summary_user_sent_sticker">%1$s stiker göndÉ™rdi.</string> - - <string name="notice_room_invite_no_invitee">%s-nin dÉ™vÉ™ti</string> - <string name="notice_room_invite">%1$s dÉ™vÉ™t etdi %2$s</string> - <string name="notice_room_invite_you">%1$s sizi dÉ™vÉ™t etdi</string> - <string name="notice_room_join">%1$s qoÅŸuldu</string> - <string name="notice_room_leave">%1$s qalıb</string> - <string name="notice_room_reject">%1$s dÉ™vÉ™ti rÉ™dd etdi</string> - <string name="notice_room_kick">%1$s %2$s-i xaric etdi</string> - <string name="notice_room_unban">%1$s %2$s-i blokdan açdı</string> - <string name="notice_room_ban">%1$s %2$s-i blokladı</string> - <string name="notice_room_withdraw">%1$s %2$s-in dÉ™vÉ™tini geri götürdü</string> - <string name="notice_avatar_url_changed">%1$s avatarı dÉ™yiÅŸdi</string> - <string name="notice_display_name_set">%1$s ekran adını %2$s olaraq tÉ™yin etdi</string> - <string name="notice_display_name_changed_from">%1$s ekran adını %2$s-dan %3$s-ya dÉ™yiÅŸdi</string> - <string name="notice_display_name_removed">%1$s onların göstÉ™rilÉ™n adlarını sildi (%2$s)</string> - <string name="notice_room_topic_changed">%1$s mövzunu dÉ™yiÅŸdi: %2$s</string> - <string name="notice_room_name_changed">%1$s otaq adını dÉ™yiÅŸdirdi: %2$s</string> - <string name="notice_placed_video_call">%s video zÉ™ng etdi.</string> - <string name="notice_placed_voice_call">%s sÉ™sli zÉ™ng etdi.</string> - <string name="notice_answered_call">%s zÉ™ngÉ™ cavab verdi.</string> - <string name="notice_ended_call">%s zÉ™ng baÅŸa çatdı.</string> - <string name="notice_made_future_room_visibility">"%1$s gÉ™lÉ™cÉ™k otaq tarixçəsini %2$s-É™ görünÉ™n etdi"</string> - <string name="notice_room_visibility_invited">bütün otaq üzvlÉ™ri, dÉ™vÉ™t olunduÄŸu andan.</string> - <string name="notice_room_visibility_joined">bütün otaq üzvlÉ™ri, qoÅŸulduÄŸu andan.</string> - <string name="notice_room_visibility_shared">bütün otaq üzvlÉ™ri.</string> - <string name="notice_room_visibility_world_readable">hÉ™r kÉ™s.</string> - <string name="notice_room_visibility_unknown">namÉ™lum (%s).</string> - <string name="notice_end_to_end">%1$s sondan-sona ÅŸifrÉ™lÉ™mÉ™ açdı (%2$s)</string> - <string name="notice_room_update">%s bu otağı tÉ™kmilləşdirdi.</string> - - <string name="notice_requested_voip_conference">%1$s VoIP konfrans istÉ™di</string> - <string name="notice_voip_started">VoIP konfransı baÅŸladı</string> - <string name="notice_voip_finished">VoIP konfransı baÅŸa çatdı</string> - - <string name="notice_avatar_changed_too">(avatar da dÉ™yiÅŸdirilib)</string> - <string name="notice_room_name_removed">%1$s otaq adını sildi</string> - <string name="notice_room_topic_removed">%1$s otaq mövzusunu sildi</string> - <string name="notice_event_redacted">Mesaj silindi</string> - <string name="notice_event_redacted_by">Mesaj %1$s tÉ™rÉ™findÉ™n silindi</string> - <string name="notice_event_redacted_with_reason">Mesaj silindi [sÉ™bÉ™b: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Mesaj %1$s tÉ™rÉ™findÉ™n qaldırıldı [sÉ™bÉ™b: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s profilini %2$s yenilÉ™di</string> - <string name="notice_room_third_party_invite">%1$s otaÄŸa qoÅŸulmaq üçün %2$s dÉ™vÉ™tnamÉ™ göndÉ™rdi</string> - <string name="notice_room_third_party_revoked_invite">%1$s otaÄŸa qoÅŸulmaq üçün %2$s dÉ™vÉ™tini ləğv etdi</string> - <string name="notice_room_third_party_registered_invite">%1$s %2$s üçün dÉ™vÉ™ti qÉ™bul etdi</string> - - <string name="notice_crypto_unable_to_decrypt">** ÅžifrÉ™ni aça bilmir: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">GöndÉ™rÉ™nin cihazı bu mesaj üçün açarları bizÉ™ göndÉ™rmÉ™yib.</string> - - <string name="could_not_redact">RedaktÉ™ etmÉ™k olmur</string> - <string name="unable_to_send_message">Mesaj göndÉ™rmÉ™k olmur</string> - - <string name="message_failed_to_upload">Şəkil yüklÉ™mÉ™k olmur</string> - - <string name="network_error">ŞəbÉ™kÉ™ xÉ™tası</string> - <string name="matrix_error">Matris xÉ™tası</string> - - <string name="room_error_join_failed_empty_room">BoÅŸ bir otaÄŸa yenidÉ™n qoÅŸulmaq hazırda mümkün deyil.</string> - - <string name="encrypted_message">ÅžifrÉ™li mesaj</string> - - <string name="medium_email">Elektron poçt ünvanı</string> - <string name="medium_phone_number">Telefon nömrÉ™si</string> - - <string name="room_displayname_invite_from">%s-dÉ™n dÉ™vÉ™t</string> - <string name="room_displayname_room_invite">OtaÄŸa dÉ™vÉ™t</string> - - <string name="room_displayname_two_members">%1$s vÉ™ %2$s</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s vÉ™ 1 digÉ™r</item> - <item quantity="other">%1$s vÉ™ %2$d digÉ™rlÉ™ri</item> - </plurals> - - <string name="room_displayname_empty_room">BoÅŸ otaq</string> - - <string name="initial_sync_start_importing_account">Ä°lkin sinxronizasiya: -\nHesab idxal olunur…</string> - <string name="initial_sync_start_importing_account_crypto">Ä°lkin sinxronizasiya: -\nKriptografiyanın idxalı</string> - <string name="initial_sync_start_importing_account_rooms">Ä°lkin sinxronizasiya: -\nOtaqlar idxalı</string> - <string name="initial_sync_start_importing_account_joined_rooms">Ä°lkin sinxronizasiya: -\nOtaqlara daxil olmaq</string> - <string name="initial_sync_start_importing_account_invited_rooms">Ä°lkin sinxronizasiya: -\nDÉ™vÉ™t olunmuÅŸ otaqların idxalı</string> - <string name="initial_sync_start_importing_account_left_rooms">Ä°lkin sinxronizasiya: -\nTÉ™rk olunmuÅŸ otaqların idxalı</string> - <string name="initial_sync_start_importing_account_groups">Ä°lkin sinxronizasiya: -\nÄ°cmaların idxalı</string> - <string name="initial_sync_start_importing_account_data">Ä°lkin sinxronizasiya: -\nHesab mÉ™lumatlarının idxalı</string> - - <string name="event_status_sending_message">Mesaj göndÉ™rilir…</string> - <string name="clear_timeline_send_queue">GöndÉ™rmÉ™ növbÉ™sini tÉ™mizlÉ™yin</string> - - <string name="notice_room_invite_no_invitee_with_reason">%1$s-nin dÉ™vÉ™ti. SÉ™bÉ™b: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s dÉ™vÉ™t olunmuÅŸ %2$s. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s sizi dÉ™vÉ™t etdi. SÉ™bÉ™b: %2$s</string> - <string name="notice_room_join_with_reason">%1$s qoÅŸuldu. SÉ™bÉ™b: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s qalıb. SÉ™bÉ™b: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s dÉ™vÉ™ti rÉ™dd etdi. SÉ™bÉ™b: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s %2$s-i xaric etdi. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s blokdan açdı %2$s. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s blokladı %2$s. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s otaÄŸa qoÅŸulmaq üçün %2$s dÉ™vÉ™tnamÉ™ göndÉ™rdi. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s otaÄŸa qoÅŸulmaq üçün %2$s dÉ™vÉ™tini ləğv etdi. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s %2$s üçün dÉ™vÉ™ti qÉ™bul etdi. SÉ™bÉ™b: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s %2$s dÉ™vÉ™tini geri götürdü. SÉ™bÉ™b: %3$s</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-bg/strings.xml b/matrix-sdk-android/src/main/res/values-bg/strings.xml deleted file mode 100644 index c3a5f3be8291dcb9b0012ca849281f99ef31ba43..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-bg/strings.xml +++ /dev/null @@ -1,216 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%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> - <string name="notice_answered_call">%s отговори на обаждането.</string> - <string name="notice_ended_call">%s прекрати разговора.</string> - <string name="notice_made_future_room_visibility">%1$s направи бъдещата иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° ÑтаÑта видима за %2$s</string> - <string name="notice_room_visibility_invited">вÑички членове, от момента на поканването им в неÑ.</string> - <string name="notice_room_visibility_joined">вÑички членове, от момента на приÑъединÑването им в неÑ.</string> - <string name="notice_room_visibility_shared">вÑички членове в неÑ.</string> - <string name="notice_room_visibility_world_readable">вÑеки.</string> - <string name="notice_room_visibility_unknown">непозната (%s).</string> - <string name="notice_end_to_end">%1$s включи шифроване от край до край (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s заÑви VoIP групов разговор</string> - <string name="notice_voip_started">Започна VoIP групов разговор</string> - <string name="notice_voip_finished">ГруповиÑÑ‚ разговор приключи</string> - <string name="notice_avatar_changed_too">(профилната Ñнимка Ñъщо беше Ñменена)</string> - <string name="notice_room_name_removed">%1$s премахна името на ÑтаÑта</string> - <string name="notice_room_topic_removed">%1$s премахна темата на ÑтаÑта</string> - <string name="notice_profile_change_redacted">%1$s обнови ÑÐ²Ð¾Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð» %2$s</string> - <string name="notice_room_third_party_invite">%1$s изпрати покана на %2$s да Ñе приÑъедини към ÑтаÑта</string> - <string name="notice_room_third_party_registered_invite">%1$s прие поканата за %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** ÐеуÑпешно разшифроване: %s **</string> - <string name="could_not_redact">ÐеуÑпешно премахване</string> - <string name="unable_to_send_message">ÐеуÑпешно изпращане на Ñъобщението</string> - <string name="message_failed_to_upload">ÐеуÑпешно качване на Ñнимката</string> - <string name="network_error">Грешка в мрежата</string> - <string name="matrix_error">Matrix грешка</string> - <string name="room_error_join_failed_empty_room">Ð’ момента не е възможно да Ñе приÑъедините отново към празна ÑтаÑ.</string> - <string name="encrypted_message">Шифровано Ñъобщение</string> - <string name="medium_email">Имейл адреÑ</string> - <string name="medium_phone_number">Телефонен номер</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">УÑтройÑтвото на Ð¿Ð¾Ð´Ð°Ñ‚ÐµÐ»Ñ Ð½Ðµ изпрати ключовете за това Ñъобщение.</string> - <string name="summary_user_sent_sticker">%1$s изпрати Ñтикер.</string> - <string name="room_displayname_invite_from">Покана от %s</string> - <string name="room_displayname_room_invite">Покана за ÑтаÑ</string> - <string name="room_displayname_two_members">%1$s и %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s и 1 друг</item> - <item quantity="other">%1$s и %2$d други</item> - </plurals> - <string name="room_displayname_empty_room">Празна ÑтаÑ</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="initial_sync_start_importing_account">Ðачална ÑинхронизациÑ: -\nИмпортиране на профил…</string> - <string name="initial_sync_start_importing_account_crypto">Ðачална ÑинхронизациÑ: -\nИмпортиране на данни за шифроване</string> - <string name="initial_sync_start_importing_account_rooms">Ðачална ÑинхронизациÑ: -\nИмпортиране на Ñтаи</string> - <string name="initial_sync_start_importing_account_joined_rooms">Ðачална ÑинхронизациÑ: -\nИмпортиране на Ñтаи, от които Ñъм чаÑÑ‚</string> - <string name="initial_sync_start_importing_account_invited_rooms">Ðачална ÑинхронизациÑ: -\nИмпортиране на Ñтаи, към които Ñъм поканен</string> - <string name="initial_sync_start_importing_account_left_rooms">Ðачална ÑинхронизациÑ: -\nИмпортиране на Ñтаи, които Ñъм напуÑнал</string> - <string name="initial_sync_start_importing_account_groups">Ðачална ÑинхронизациÑ: -\nИмпортиране на общноÑти</string> - <string name="initial_sync_start_importing_account_data">Ðачална ÑинхронизациÑ: -\nИмпортиране на данни за профила</string> - <string name="notice_room_update">%s обнови тази ÑтаÑ.</string> - <string name="event_status_sending_message">Изпращане на Ñъобщение…</string> - <string name="clear_timeline_send_queue">ИзчиÑти опашката за изпращане</string> - <string name="notice_room_third_party_revoked_invite">%1$s оттегли поканата за приÑъединÑване на %2$s към ÑтаÑта</string> - <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_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> - <string name="notice_room_ban_with_reason">%1$s блокира %2$s. Причина: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s изпрати покана до %2$s да Ñе приÑъедини в ÑтаÑта. Причина: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s премахна поканата за приÑъединÑване на %2$s в ÑтаÑта. Причина: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s прие поканата за %2$s. Причина: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s оттегли поканата на %2$s. Причина: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s добави %2$s като Ð°Ð´Ñ€ÐµÑ Ð·Ð° тази ÑтаÑ.</item> - <item quantity="other">%1$s добави %2$s като адреÑи за тази ÑтаÑ.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s премахна %2$s като Ð°Ð´Ñ€ÐµÑ Ð·Ð° тази ÑтаÑ.</item> - <item quantity="other">%1$s премахна %2$s като адреÑи за тази ÑтаÑ.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s добави %2$s и премахна %3$s като адреÑи за тази ÑтаÑ.</string> - <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> - <string name="notice_room_created">%1$s Ñъздаде ÑтаÑта</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_end_to_end_unknown_algorithm_by_you">Включихте шифроване от-край-до-край (непознат алгоритъм: %1$s).</string> - <string name="notice_end_to_end_ok_by_you">Включихте шифроване от-край-до-край.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">СпрÑхте възможноÑтта гоÑти да Ñе приÑъединÑват в ÑтаÑта.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s ÑÐ¿Ñ€Ñ Ð²ÑŠÐ·Ð¼Ð¾Ð¶Ð½Ð¾Ñтта гоÑти да Ñе приÑъединÑват в ÑтаÑта.</string> - <string name="notice_room_guest_access_forbidden_by_you">СпрÑхте възможноÑтта гоÑти да Ñе приÑъединÑват в ÑтаÑта.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Позволихте на гоÑти да Ñе приÑъединÑват тук.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s позволи на гоÑти да Ñе приÑъединÑват тук.</string> - <string name="notice_room_guest_access_can_join_by_you">Позволихте на гоÑти да Ñе приÑъединÑват към ÑтаÑта.</string> - <string name="notice_room_canonical_alias_unset_by_you">Премахнахте оÑÐ½Ð¾Ð²Ð½Ð¸Ñ Ð°Ð´Ñ€ÐµÑ Ð½Ð° ÑтаÑта.</string> - <string name="notice_room_canonical_alias_set_by_you">Зададохте %1$s като оÑновен Ð°Ð´Ñ€ÐµÑ Ð½Ð° ÑтаÑта.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Добавихте %1$s и премахнахте %2$s от адреÑите за ÑтаÑта.</string> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Премахнахте %1$s от адреÑите на ÑтаÑта.</item> - <item quantity="other">Премахнахте %1$s от адреÑите на ÑтаÑта.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Добавихте %1$s като Ð°Ð´Ñ€ÐµÑ Ð·Ð° тази ÑтаÑ.</item> - <item quantity="other">Добавихте %1$s като адреÑи за тази ÑтаÑ.</item> - </plurals> - <string name="notice_room_withdraw_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_third_party_revoked_invite_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_ban_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_kick_with_reason_by_you">Изгонихте %1$s. Причина: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Отхвърлихте поканата. Причина: %1$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">ÐапуÑнахте. Причина: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s напуÑна. Причина: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">ÐапуÑнахте ÑтаÑта. Причина: %1$s</string> - <string name="notice_direct_room_join_with_reason_by_you">ПриÑъединихте Ñе. Причина: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s Ñе приÑъедини. Причина: %2$s</string> - <string name="notice_room_join_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_invite_no_invitee_with_reason_by_you">Ваша покана. Причина: %1$s</string> - <string name="notice_power_level_diff">%1$s от %2$s на %3$s</string> - <string name="notice_power_level_changed">%1$s промени нивото на доÑтъп на %2$s.</string> - <string name="notice_power_level_changed_by_you">Променихте нивото на доÑтъп на %1$s.</string> - <string name="power_level_custom_no_value">СобÑтвено ниво</string> - <string name="power_level_custom">СобÑтвено ниво (%1$d)</string> - <string name="power_level_default">По подразбиране</string> - <string name="power_level_moderator">Модератор</string> - <string name="power_level_admin">ÐдминиÑтратор</string> - <string name="notice_widget_modified_by_you">Променихте %1$s приÑпоÑоблението</string> - <string name="notice_widget_modified">%1$s промени %2$s приÑпоÑоблението</string> - <string name="notice_widget_removed_by_you">Премахнахте %1$s приÑпоÑоблението</string> - <string name="notice_widget_removed">%1$s премахна %2$s приÑпоÑоблението</string> - <string name="notice_widget_added_by_you">Добавихте %1$s приÑпоÑобление</string> - <string name="notice_widget_added">%1$s добави %2$s приÑпоÑобление</string> - <string name="notice_room_third_party_registered_invite_by_you">Приехте поканата за %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Оттеглихте поканата от %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s оттегли поканата от %2$s</string> - <string name="notice_room_third_party_revoked_invite_by_you">Оттеглихте поканата за приÑъединÑване в ÑтаÑта от %1$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Поканихте %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s покани %2$s</string> - <string name="notice_room_third_party_invite_by_you">Изпратихте покана към %1$s за приÑъединÑване в ÑтаÑта</string> - <string name="notice_profile_change_redacted_by_you">Обновихте профила Ñи %1$s</string> - <string name="notice_room_avatar_removed_by_you">Премахнахте Ñнимката на ÑтаÑта</string> - <string name="notice_room_avatar_removed">%1$s премахна Ñнимката на ÑтаÑта</string> - <string name="notice_room_topic_removed_by_you">Премахнахте темата на ÑтаÑта</string> - <string name="notice_room_name_removed_by_you">Премахнахте името на ÑтаÑта</string> - <string name="notice_requested_voip_conference_by_you">ЗаÑвихте VoIP конференциÑ</string> - <string name="notice_direct_room_update_by_you">Обновихте чата.</string> - <string name="notice_direct_room_update">%s обнови чата.</string> - <string name="notice_room_update_by_you">Обновихте ÑтаÑта.</string> - <string name="notice_end_to_end_by_you">Включихте шифроване от-край-до-край (%1$s)</string> - <string name="notice_made_future_direct_room_visibility_by_you">Ðаправихте бъдещите ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´Ð¸Ð¼Ð¸ за %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s направи бъдещите ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´Ð¸Ð¼Ð¸ за %2$s</string> - <string name="notice_made_future_room_visibility_by_you">Ðаправихте бъдещата иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° ÑтаÑта видима за %1$s</string> - <string name="notice_ended_call_by_you">Прекратихте разговора.</string> - <string name="notice_placed_video_call_by_you">Започнахте видео разговор.</string> - <string name="notice_answered_call_by_you">Отговорихте на обаждането.</string> - <string name="notice_call_candidates_by_you">Изпратихте данни за наÑтройка на разговора.</string> - <string name="notice_call_candidates">%s изпрати данни за наÑтройка на разговора.</string> - <string name="notice_placed_voice_call_by_you">Започнахте глаÑов разговор.</string> - <string name="notice_room_name_changed_by_you">Променихте името на ÑтаÑта на: %1$s</string> - <string name="notice_room_avatar_changed_by_you">Променихте Ñнимката на ÑтаÑта</string> - <string name="notice_room_avatar_changed">%1$s промени Ñнимката на ÑтаÑта</string> - <string name="notice_room_topic_changed_by_you">Променихте темата на: %1$s</string> - <string name="notice_display_name_removed_by_you">Премахнахте името Ñи (%1$s)</string> - <string name="notice_display_name_changed_from_by_you">Променихте името Ñи от %1$s на %2$s</string> - <string name="notice_display_name_set_by_you">Променихте името Ñи на %1$s</string> - <string name="notice_avatar_url_changed_by_you">Променихте Ñнимката Ñи</string> - <string name="notice_room_withdraw_by_you">Оттеглихте поканата от %1$s</string> - <string name="notice_direct_room_leave_by_you">ÐапуÑнахте ÑтаÑта</string> - <string name="notice_direct_room_leave">%1$s напуÑна ÑтаÑта</string> - <string name="notice_direct_room_join_by_you">ПриÑъединихте Ñе</string> - <string name="notice_direct_room_join">%1$s Ñе приÑъедини</string> - <string name="notice_direct_room_created_by_you">Създадохте диÑкуÑиÑта</string> - <string name="notice_direct_room_created">%1$s Ñъздаде диÑкуÑиÑта</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-bn-rIN/strings.xml b/matrix-sdk-android/src/main/res/values-bn-rIN/strings.xml deleted file mode 100644 index 35f8feaf0f36843de13131a25cbddb88d758d50a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-bn-rIN/strings.xml +++ /dev/null @@ -1,194 +0,0 @@ -<?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> - <string name="summary_message">%1$s: %2$s</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">%1$s ককà§à¦·à¦Ÿà¦¿ তৈরি করেছেন</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">%s কলটির উতà§à¦¤à¦° দিয়েছে।</string> - <string name="notice_answered_call_by_you">আপনি কলটি উতà§à¦¤à¦° দিয়েছেন।</string> - <string name="notice_ended_call">%s কলটি শেষ করেছেন।</string> - <string name="notice_ended_call_by_you">আপনি কলটি শেষ করেছেন।</string> - <string name="notice_made_future_room_visibility">%1$s à¦à¦¬à¦¿à¦·à§à¦¯à¦¤à§‡à¦° ঘরের ইতিহাস %2$s à¦à¦° কাছে দৃশà§à¦¯à¦®à¦¾à¦¨ করে তà§à¦²à§‡à¦›à§‡</string> - <string name="notice_made_future_room_visibility_by_you">আপনি à¦à¦¬à¦¿à¦·à§à¦¯à¦¤à§‡à¦° ককà§à¦· ইতিহাস %1$s à¦à¦° কাছে দৃশà§à¦¯à¦®à¦¾à¦¨ করেছেন</string> - <string name="notice_room_visibility_invited">ককà§à¦·à§‡à¦° সমসà§à¦¤ সদসà§à¦¯, যখন থেকে তারা আমনà§à¦¤à§à¦°à¦¿à¦¤à¥¤</string> - <string name="notice_room_visibility_joined">ককà§à¦·à§‡à¦° সমসà§à¦¤ সদসà§à¦¯, যখন থেকে তারা যোগদান করেছিল।</string> - <string name="notice_room_visibility_shared">সমসà§à¦¤ ককà§à¦·à§‡à¦° সদসà§à¦¯à¥¤</string> - <string name="notice_room_visibility_world_readable">যে কেউ।</string> - <string name="notice_room_visibility_unknown">অজানা (%s)।</string> - <string name="notice_end_to_end">%1$s à¦à¦¨à§à¦¡-টà§-à¦à¦¨à§à¦¡ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছে (%2$s)</string> - <string name="notice_end_to_end_by_you">আপনি শেষ-থেকে-শেষ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছেন (%1$s)</string> - <string name="notice_room_update">%s à¦à¦‡ ককà§à¦·à¦Ÿà¦¿à¦•à§‡ আপগà§à¦°à§‡à¦¡ করেছে।</string> - <string name="notice_room_update_by_you">আপনি à¦à¦‡ ককà§à¦·à¦Ÿà¦¿ আপগà§à¦°à§‡à¦¡ করেছেন।</string> - <string name="notice_requested_voip_conference">%1$s à¦à¦•à¦Ÿà¦¿ à¦à¦¿à¦“আইপি সমà§à¦®à§‡à¦²à¦¨à§‡à¦° জনà§à¦¯ অনà§à¦°à§‹à¦§ করেছে</string> - <string name="notice_requested_voip_conference_by_you">আপনি à¦à¦•à¦Ÿà¦¿ à¦à¦¿à¦“আইপি সমà§à¦®à§‡à¦²à¦¨à§‡à¦° অনà§à¦°à§‹à¦§ করেছেন</string> - <string name="notice_voip_started">à¦à¦¿à¦“আইপি সমà§à¦®à§‡à¦²à¦¨ শà§à¦°à§ হয়েছে</string> - <string name="notice_voip_finished">à¦à¦¿à¦“আইপি সমà§à¦®à§‡à¦²à¦¨ শেষ হয়েছে</string> - <string name="notice_avatar_changed_too">(আবতারটিও পরিবরà§à¦¤à¦¨ করা হয়েছিল)</string> - <string name="notice_room_name_removed">%1$s ককà§à¦·à§‡à¦° নাম সরিয়েছে</string> - <string name="notice_room_name_removed_by_you">আপনি ককà§à¦·à§‡à¦° নাম সরিয়েছেন</string> - <string name="notice_room_topic_removed">%1$s ককà§à¦·à§‡à¦° বিষয় মà§à¦›à§‡ ফেলেছে</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_event_redacted">বারà§à¦¤à¦¾ সরানো হয়েছে</string> - <string name="notice_event_redacted_by">%1$s দà§à¦¬à¦¾à¦°à¦¾ বারà§à¦¤à¦¾ সরানো হয়েছে</string> - <string name="notice_event_redacted_with_reason">বারà§à¦¤à¦¾ সরানো হয়েছে [কারণ:%1$s]</string> - <string name="notice_event_redacted_by_with_reason">%1$s দà§à¦¬à¦¾à¦°à¦¾ বারà§à¦¤à¦¾ সরানো হয়েছে [কারণ: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s তাদের পà§à¦°à§‹à¦«à¦¾à¦‡à¦² %2$s আপডেট করেছে</string> - <string name="notice_profile_change_redacted_by_you">আপনি আপনার পà§à¦°à§‹à¦«à¦¾à¦‡à¦² %1$s আপডেট করেছেন</string> - <string name="notice_room_third_party_invite">%1$s %2$s কে ঘরে যোগদানের জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ আমনà§à¦¤à§à¦°à¦£ পাঠিয়েছে</string> - <string name="notice_room_third_party_invite_by_you">আপনি %1$s কে ঘরে যোগদানের জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ আমনà§à¦¤à§à¦°à¦£ পà§à¦°à§‡à¦°à¦£ করেছেন</string> - <string name="notice_room_third_party_revoked_invite">%1$s %2$s à¦à¦° ককà§à¦·à§‡ যোগদানের আমনà§à¦¤à§à¦°à¦£ বাতিল করে দিয়েছিল</string> - <string name="notice_room_third_party_revoked_invite_by_you">আপনি %1$s à¦à¦° ককà§à¦·à§‡ যোগদানের জনà§à¦¯ আমনà§à¦¤à§à¦°à¦£à¦Ÿà¦¿ বাতিল করেছেন</string> - <string name="notice_room_third_party_registered_invite">%1$s %2$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_crypto_unable_to_decrypt">** ডিকà§à¦°à¦¿à¦ªà§à¦Ÿ করতে অকà§à¦·à¦®: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">পà§à¦°à§‡à¦°à¦•à§‡à¦° ডিà¦à¦¾à¦‡à¦¸ আমাদের à¦à¦‡ বারà§à¦¤à¦¾à¦° জনà§à¦¯ কীগà§à¦²à¦¿ পà§à¦°à§‡à¦°à¦£ করেনি।</string> - <string name="could_not_redact">পà§à¦¨à¦°à¦¾à¦¯à¦¼ পà§à¦°à¦¤à¦¿à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾ করতে পারেনি</string> - <string name="unable_to_send_message">বারà§à¦¤à¦¾ পাঠাতে অকà§à¦·à¦®</string> - <string name="message_failed_to_upload">চিতà§à¦° আপলোড করতে বà§à¦¯à¦°à§à¦¥</string> - <string name="network_error">নেটওয়ারà§à¦• তà§à¦°à§à¦Ÿà¦¿</string> - <string name="matrix_error">মà§à¦¯à¦¾à¦Ÿà§à¦°à¦¿à¦•à§à¦¸ তà§à¦°à§à¦Ÿà¦¿</string> - <string name="room_error_join_failed_empty_room">খালি ককà§à¦·à§‡ পà§à¦¨à¦°à¦¾à¦¯à¦¼ যোগদান করা বরà§à¦¤à¦®à¦¾à¦¨à§‡ সমà§à¦à¦¬ নয়।</string> - <string name="encrypted_message">à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ করা বারà§à¦¤à¦¾</string> - <string name="medium_email">ইমেল ঠিকানা</string> - <string name="medium_phone_number">ফোন নমà§à¦¬à¦°</string> - <string name="room_displayname_invite_from">%s থেকে আমনà§à¦¤à§à¦°à¦£ করà§à¦¨</string> - <string name="room_displayname_room_invite">ককà§à¦· আমনà§à¦¤à§à¦°à¦£</string> - <string name="room_displayname_two_members">%1$s à¦à¦¬à¦‚ %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s à¦à¦¬à¦‚ অনà§à¦¯ ১ জন</item> - <item quantity="other">%1$s à¦à¦¬à¦‚ অনà§à¦¯à¦¾à¦¨à§à¦¯ %2$d জন</item> - </plurals> - <string name="room_displayname_empty_room">খালি ককà§à¦·</string> - <string name="initial_sync_start_importing_account">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nঅà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ আমদানি করা হচà§à¦›à§‡â€¦</string> - <string name="initial_sync_start_importing_account_crypto">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nকà§à¦°à¦¿à¦ªà§à¦Ÿà§‹ আমদানি হচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_rooms">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nককà§à¦·à¦—à§à¦²à¦¿ আমদানি করা হচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_joined_rooms">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nযোগ করা ককà§à¦·à¦—à§à¦²à¦¿à¦¤à§‡ আমদানি করা হিচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_invited_rooms">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nআমনà§à¦¤à§à¦°à¦¿à¦¤ করা ককà§à¦·à¦—à§à¦²à¦¿à¦¤à§‡ আমদানি করা হিচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_left_rooms">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nছেড়ে দেওয়া ককà§à¦·à¦—à§à¦²à¦¿à¦¤à§‡ আমদানি করা হিচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_groups">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nসমà§à¦ªà§à¦°à¦¦à¦¾à¦¯à¦¼à¦—à§à¦²à¦¿ আমদানি করা হচà§à¦›à§‡</string> - <string name="initial_sync_start_importing_account_data">পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সিঙà§à¦•: -\nঅà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ ডেটা আমদানি করা হচà§à¦›à§‡</string> - <string name="event_status_sending_message">বারà§à¦¤à¦¾ পà§à¦°à§‡à¦°à¦£ করা হচà§à¦›à§‡ …</string> - <string name="clear_timeline_send_queue">পà§à¦°à§‡à¦°à¦£ সারি পরিষà§à¦•à¦¾à¦° করà§à¦¨</string> - <string name="notice_room_invite_no_invitee_with_reason">%1$s à¦à¦° আমনà§à¦¤à§à¦°à¦£à¥¤ কারণ: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">আপনার আমনà§à¦¤à§à¦°à¦£à¥¤ কারণ: %1$s</string> - <string name="notice_room_invite_with_reason">%1$s আমনà§à¦¤à§à¦°à¦¿à¦¤ করেছেন %2$s কে। কারণ: %3$s</string> - <string name="notice_room_invite_with_reason_by_you">আপনি %1$s কে আমনà§à¦¤à§à¦°à¦¿à¦¤ করেছেন। কারণ: %2$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_join_with_reason_by_you">আপনি ককà§à¦·à§‡ যোগ দিয়েছেন। কারণ: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s রà§à¦® ছেড়ে দিয়েছে। কারণ: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">আপনি ককà§à¦· ছেড়ে দিয়েছেন। কারণ: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s আমনà§à¦¤à§à¦°à¦£ বাতিল করেছেন। কারণ: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">আপনি আমনà§à¦¤à§à¦°à¦£à¦Ÿà¦¿ বাতিল করেছেন। কারণ: %1$s</string> - <string name="notice_room_kick_with_reason">%1$s %2$s কে কিক করেছে। কারণ: %3$s</string> - <string name="notice_room_kick_with_reason_by_you">আপনি %1$s কে কীক করেছেন। কারণ: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s %2$s কে নিষিদà§à¦§ তালিকা থেকে মà§à¦•à§à¦¤ করেছে। কারণ: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">আপনি %1$s কে নিষিদà§à¦§ মà§à¦•à§à¦¤ করেছেন। কারণ: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s %2$s কে নিষিদà§à¦§ করেছে। কারণ: %3$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">%1$s রà§à¦®à§‡à¦° সাথে যোগ দিতে %2$s কে à¦à¦•à¦Ÿà¦¿ আমনà§à¦¤à§à¦°à¦£ পাঠিয়েছেন। কারণ: %3$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">%1$s %2$s à¦à¦° ককà§à¦·à§‡ যোগদানের আমনà§à¦¤à§à¦°à¦£ বাতিল করে দিয়েছিল। কারণ: %3$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">%1$s %2$s à¦à¦° জনà§à¦¯ আমনà§à¦¤à§à¦°à¦£ গà§à¦°à¦¹à¦£ করেছেন। কারণ: %3$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">%1$s %2$s à¦à¦° আমনà§à¦¤à§à¦°à¦£ ফেরত নিয়েছে। কারণ: %3$s</string> - <string name="notice_room_withdraw_with_reason_by_you">আপনি %1$s à¦à¦° আমনà§à¦¤à§à¦°à¦£ পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করেছেন। কারণ: %2$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s à¦à¦‡ ঘরের ঠিকানা হিসাবে %2$s যà§à¦•à§à¦¤ করেছে।</item> - <item quantity="other">%1$s à¦à¦‡ ঘরের ঠিকানাগà§à¦²à¦¿ হিসাবে %2$s যà§à¦•à§à¦¤ করেছে।</item> - </plurals> - <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"> - <item quantity="one">%1$s à¦à¦‡ ঘরের ঠিকানা হিসাবে %2$s সরানো হয়েছে।</item> - <item quantity="other">%1$s %3$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">%1$s %2$s যোগ করেছে à¦à¦¬à¦‚ %3$s গà§à¦²à¦¿ à¦à¦‡ ঘরের ঠিকানা হিসাবে সরানো হয়েছে।</string> - <string name="notice_room_aliases_added_and_removed_by_you">আপনি %1$s যোগ করেছেন à¦à¦¬à¦‚ %2$s কে à¦à¦‡ ঘরের ঠিকানা হিসাবে সরিয়ে দিয়েছেন।</string> - <string name="notice_room_canonical_alias_set">%1$s à¦à¦‡ ঘরের মূল ঠিকানাটি %2$s তে সেট করে।</string> - <string name="notice_room_canonical_alias_set_by_you">আপনি à¦à¦‡ ঘরের মূল ঠিকানাটি %1$s তে সেট করেছেন।</string> - <string name="notice_room_canonical_alias_unset">%1$s à¦à¦‡ ঘরের মূল ঠিকানা সরিয়ে নিয়েছে।</string> - <string name="notice_room_canonical_alias_unset_by_you">আপনি à¦à¦‡ ঘরের মূল ঠিকানা সরিয়েছেন।</string> - <string name="notice_room_guest_access_can_join">%1$s অতিথিদের ঘরে যোগদানের অনà§à¦®à¦¤à¦¿ দিয়েছে।</string> - <string name="notice_room_guest_access_can_join_by_you">আপনি অতিথিদের ঘরে যোগদানের অনà§à¦®à¦¤à¦¿ দিয়েছেন।</string> - <string name="notice_room_guest_access_forbidden">%1$s অতিথিদের ঘরে যোগদান করতে বাধা দিয়েছে।</string> - <string name="notice_room_guest_access_forbidden_by_you">আপনি অতিথিদের ঘরে যোগদান করতে বাধা দিয়েছেন।</string> - <string name="notice_end_to_end_ok">%1$s à¦à¦¨à§à¦¡-টà§-à¦à¦¨à§à¦¡ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছে।</string> - <string name="notice_end_to_end_ok_by_you">আপনি শেষ থেকে শেষ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছেন।</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s à¦à¦¨à§à¦¡-টà§-à¦à¦¨à§à¦¡ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছে (অজানা অà§à¦¯à¦¾à¦²à¦—রিদম %2$s)।</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">আপনি শেষ-থেকে-শেষ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ চালৠকরেছেন (অজানা অà§à¦¯à¦¾à¦²à¦—রিদম %1$s )।</string> - <string name="key_verification_request_fallback_message">%s আপনার কীটি যাচাই করার জনà§à¦¯ অনà§à¦°à§‹à¦§ করছে, তবে আপনার কà§à¦²à¦¾à¦¯à¦¼à§‡à¦¨à§à¦Ÿ ইন-চà§à¦¯à¦¾à¦Ÿ কী যাচাইকরণ সমরà§à¦¥à¦¨ করে না। কীগà§à¦²à¦¿ যাচাই করতে আপনাকে লিগà§à¦¯à¦¾à¦¸à¦¿ কী যাচাইকরণ বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে হবে।</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-bs/strings.xml b/matrix-sdk-android/src/main/res/values-bs/strings.xml deleted file mode 100644 index 6a6ee46d3243da5b9328d65c84feb0d7658a0c2d..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-bs/strings.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="room_displayname_invite_from">Pozovite iz %s</string> - <string name="room_displayname_room_invite">Poziv u Sobu</string> - <string name="room_displayname_two_members">%1$s i %2$s</string> - <string name="room_displayname_empty_room">Prazna soba</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-ca/strings.xml b/matrix-sdk-android/src/main/res/values-ca/strings.xml deleted file mode 100644 index 8ba8c9acfd33b94484fdb765981611c1bd917dc4..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-ca/strings.xml +++ /dev/null @@ -1,217 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s ha enviat una imatge.</string> - <string name="notice_room_leave">%1$s ha marxat de la sala</string> - <string name="notice_room_join">%1$s s\'ha unit a la sala</string> - <string name="medium_phone_number">Número de telèfon</string> - <string name="medium_email">Correu electrònic</string> - <string name="encrypted_message">Missatge xifrat</string> - <string name="notice_room_invite_no_invitee">invitació de %s</string> - <string name="notice_room_invite">%1$s ha convidat a %2$s</string> - <string name="notice_room_invite_you">%1$s t\'ha convidat</string> - <string name="notice_room_reject">%1$s ha rebutjat la invitació</string> - <string name="notice_room_kick">%1$s ha expulsat %2$s</string> - <string name="notice_display_name_changed_from">%1$s ha canviat el seu nom de visualització de %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s ha eliminat el seu nom de visualització (era %2$s)</string> - <string name="notice_room_topic_changed">%1$s ha canviat el tema a: %2$s</string> - <string name="notice_room_name_changed">%1$s ha canviat el nom de la sala a: %2$s</string> - <string name="notice_answered_call">%s ha respost a la trucada.</string> - <string name="notice_ended_call">%s ha finalitzat la trucada.</string> - <string name="notice_room_visibility_invited">tots el participants de la sala, des de que són convidats.</string> - <string name="notice_room_visibility_shared">tots els participants de la sala.</string> - <string name="notice_room_visibility_unknown">desconegut (%s).</string> - <string name="notice_end_to_end">%1$s ha activat el xifrat d\'extrem a extrem (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s ha sol·licitat una conferència VoIP</string> - <string name="notice_room_unban">%1$s ha tret el veto a %2$s</string> - <string name="notice_room_ban">%1$s ha vetat %2$s</string> - <string name="notice_room_withdraw">%1$s ha retirat la invitació de %2$s</string> - <string name="notice_avatar_url_changed">%1$s ha canviat el seu avatar</string> - <string name="notice_made_future_room_visibility">%1$s ha establert la visibilitat de l\'historial futur de la sala a %2$s</string> - <string name="notice_room_visibility_joined">tots els participants de la sala, des de que s\'hi uneixen.</string> - <string name="notice_room_visibility_world_readable">qualsevol.</string> - <string name="notice_voip_started">S\'ha iniciat la conferència VoIP</string> - <string name="notice_voip_finished">Ha finalitzat la conferència VoIP</string> - <string name="notice_avatar_changed_too">(també ha canviat l\'avatar)</string> - <string name="notice_room_name_removed">%1$s ha eliminat el nom de la sala</string> - <string name="notice_room_topic_removed">%1$s ha eliminat el tema de la sala</string> - <string name="notice_profile_change_redacted">%1$s ha actualitzat el seu perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s ha enviat una invitació a %2$s perquè s\'uneixi a la sala</string> - <string name="notice_room_third_party_registered_invite">%1$s ha acceptat la invitació de %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** No s\'ha pogut desxifrar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">El dispositiu del remitent no ens ha enviat les claus per aquest missatge.</string> - <string name="could_not_redact">No s\'ha pogut redactar</string> - <string name="unable_to_send_message">No s\'ha pogut enviar el missatge</string> - <string name="message_failed_to_upload">No s\'ha pogut pujar la imatge</string> - <string name="network_error">Error de xarxa</string> - <string name="matrix_error">Error de Matrix</string> - <string name="room_error_join_failed_empty_room">Ara per ara no és possible tornar a unir-se a una sala buida.</string> - <string name="notice_display_name_set">%1$s a canviat el seu nom de visualització a %2$s</string> - <string name="notice_placed_video_call">%s ha realitzat una videotrucada.</string> - <string name="notice_placed_voice_call">%s ha realitzat una trucada de veu.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Invitació de %s</string> - <string name="room_displayname_room_invite">Convida a la sala</string> - <string name="room_displayname_two_members">%1$s i %2$s</string> - <string name="room_displayname_empty_room">Sala buida</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s i 1 altre</item> - <item quantity="other">%1$s i %2$d altres</item> - </plurals> - <string name="summary_user_sent_sticker">%1$s ha enviat un adhesiu.</string> - <string name="notice_direct_room_update">%s s\'ha actualitzat aquÃ.</string> - <string name="notice_direct_room_update_by_you">Ho has actualitzat aquÃ.</string> - <string name="key_verification_request_fallback_message">%s està sol·licitant la verificació de la teva clau, però el teu client no admet la verificació de clau des del xat. Haurà s d\'utilitzar la verificació de claus heretada per fer la verificació.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Has activat el xifrat d\'extrem a extrem (algorisme %1$s no reconegut).</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s ha activat el xifrat d\'extrem a extrem (algorisme %2$s no reconegut).</string> - <string name="notice_end_to_end_ok_by_you">Has activat el xifrat d\'extrem a extrem.</string> - <string name="notice_end_to_end_ok">%1$s ha activat el xifrat d\'extrem a extrem.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Has impedit que els convidats es puguin unir a la sala.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s ha impedit que els convidats es puguin unir a la sala.</string> - <string name="notice_room_guest_access_forbidden_by_you">Has impedit que els convidats es puguin unir a la sala.</string> - <string name="notice_room_guest_access_forbidden">%1$s ha impedit que els convidats es puguin unir a la sala.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Has permès que els convidats s\'uneixin aquÃ.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s ha permès que els convidats s\'uneixin aquÃ.</string> - <string name="notice_room_guest_access_can_join_by_you">Has permès que els convidats s\'uneixin a la sala.</string> - <string name="notice_room_guest_access_can_join">%1$s ha permès que els convidats s\'uneixin a la sala.</string> - <string name="notice_room_canonical_alias_unset_by_you">Has eliminat l\'adreça principal d\'aquesta sala.</string> - <string name="notice_room_canonical_alias_unset">%1$s ha eliminat l\'adreça principal d\'aquesta sala.</string> - <string name="notice_room_canonical_alias_set_by_you">Has establert l\'adreça principal d\'aquesta sala a %1$s.</string> - <string name="notice_room_canonical_alias_set">%1$s ha establert l\'adreça principal d\'aquesta sala a %2$s.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Has afegit %1$s i has eliminat %2$s d\'aquesta sala (adreces).</string> - <string name="notice_room_aliases_added_and_removed">%1$s ha afegit %2$s i ha eliminat %3$s d\'aquesta sala (adreces).</string> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Has eliminat l\'adreça %1$s d\'aquesta sala.</item> - <item quantity="other">Has eliminat les adreces %1$s d\'aquesta sala.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s ha eliminat l\'adreça %2$s d\'aquesta sala.</item> - <item quantity="other">%1$s ha eliminat les adreces %3$s d\'aquesta sala.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Has afegit l\'adreça %1$s a aquesta sala.</item> - <item quantity="other">Has afegit les adreces %1$s a aquesta sala.</item> - </plurals> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s ha afegit l\'adreça %2$s a aquesta sala.</item> - <item quantity="other">%1$s ha afegit les adreces %2$s a aquesta sala.</item> - </plurals> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Has revocat la invitació de %1$s perquè s\'uneixi a la sala. Motiu: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s ha revocat la invitació de %2$s perquè s\'uneixi a la sala. Motiu: %3$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Has revocat la invitació de %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s ha revocat la invitació de %2$s</string> - <string name="notice_room_third_party_revoked_invite_by_you">Has revocat la invitació de %1$s perquè s\'uneixi a la sala</string> - <string name="notice_room_third_party_revoked_invite">%1$s ha revocat la invitació de %2$s perquè s\'uneixi a la sala</string> - <string name="notice_room_withdraw_with_reason_by_you">Has retirat la invitació de %1$s. Motiu: %2$s</string> - <string name="notice_room_withdraw_with_reason">%1$s ha retirat la invitació de %2$s. Motiu: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Has acceptat la invitació de %1$s. Motiu: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s ha acceptat la invitació de %2$s. Motiu: %3$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Has enviat una invitació a %1$s perquè s\'uneixi a la sala. Motiu: %2$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s ha enviat una invitació a %2$s perquè s\'uneixi a la sala. Motiu: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">Has vetat %1$s. Motiu: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s ha vetat %2$s. Motiu: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">Has tret el veto a %1$s. Motiu: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s ha tret el veto a %2$s. Motiu: %3$s</string> - <string name="notice_room_ban_by_you">Has vetat %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Has marxat de la sala. Motiu: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s ha marxat de la sala. Motiu: %2$s</string> - <string name="notice_direct_room_leave_by_you">Has marxat de la sala</string> - <string name="notice_direct_room_leave">%1$s ha marxat de la sala</string> - <string name="notice_room_leave_by_you">Has marxat de la sala</string> - <string name="notice_room_kick_by_you">Has expulsat %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Has expulsat %1$s. Motiu: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s ha expulsat %2$s. Motiu: %3$s</string> - <string name="notice_room_reject_with_reason_by_you">Has rebutjat la invitació. Motiu: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s ha rebutjat la invitació. Motiu: %2$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">Has marxat. Motiu: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s ha marxat. Motiu: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">T\'has unit. Motiu: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s s\'ha unit. Motiu: %2$s</string> - <string name="notice_room_join_with_reason_by_you">T\'has unit a la sala. Motiu: %1$s</string> - <string name="notice_room_join_with_reason">%1$s s\'ha unit a la sala. Motiu: %2$s</string> - <string name="notice_room_invite_you_with_reason">%1$s t\'ha convidat. Motiu: %2$s</string> - <string name="notice_room_invite_with_reason_by_you">Has convidat %1$s. Motiu: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s ha convidat %2$s. Motiu: %3$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">La teva invitació. Motiu: %1$s</string> - <string name="notice_room_invite_no_invitee_with_reason">la invitació de %1$s. Motiu: %2$s</string> - <string name="clear_timeline_send_queue">Esborra la cua d\'enviament</string> - <string name="event_status_sending_message">Enviant missatge…</string> - <string name="initial_sync_start_importing_account_data">Sincronització inicial: -\nImportant dades del compte</string> - <string name="initial_sync_start_importing_account_groups">Sincronització inicial: -\nImportant comunitats</string> - <string name="initial_sync_start_importing_account_left_rooms">Sincronització inicial: -\nImportant sales que deixat</string> - <string name="initial_sync_start_importing_account">Sincronització inicial: -\nImportant compte…</string> - <string name="initial_sync_start_importing_account_crypto">Sincronització inicial: -\nImportant xifrat</string> - <string name="initial_sync_start_importing_account_rooms">Sincronització inicial: -\nImportant sales</string> - <string name="initial_sync_start_importing_account_invited_rooms">Sincronització inicial: -\nImportant sales on hi està s convidat</string> - <string name="initial_sync_start_importing_account_joined_rooms">Sincronització inicial: -\nImportant sales on hi està s unit</string> - <string name="notice_power_level_diff">%1$s de %2$s a %3$s</string> - <string name="notice_power_level_changed">%1$s ha canviat el nivell d\'autoritat de %2$s.</string> - <string name="notice_power_level_changed_by_you">Has canviat el nivell d\'autoritat de %1$s.</string> - <string name="power_level_custom_no_value">Personalitzat</string> - <string name="power_level_custom">Personalitzat (%1$d)</string> - <string name="power_level_default">Predeterminat</string> - <string name="power_level_moderator">Moderador</string> - <string name="power_level_admin">Administrador</string> - <string name="notice_widget_modified_by_you">Has modificat el giny %1$s</string> - <string name="notice_widget_modified">%1$s ha modificat el giny %2$s</string> - <string name="notice_widget_removed_by_you">Has eliminat el giny %1$s</string> - <string name="notice_widget_removed">%1$s ha eliminat el giny %2$s</string> - <string name="notice_widget_added_by_you">Has afegit el giny %1$s</string> - <string name="notice_widget_added">%1$s ha afegit el giny %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">Has acceptat la invitació de %1$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Has convidat a %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s ha convidat a %2$s</string> - <string name="notice_room_third_party_invite_by_you">Has enviat una invitació a %1$s perquè s\'uneixi a la sala</string> - <string name="notice_profile_change_redacted_by_you">Has actualitzat el teu perfil %1$s</string> - <string name="notice_event_redacted_by_with_reason">Missatge eliminat per %1$s [motiu: %2$s]</string> - <string name="notice_event_redacted_with_reason">Missatge eliminat [motiu: %1$s]</string> - <string name="notice_event_redacted_by">Missatge eliminat per %1$s</string> - <string name="notice_event_redacted">Missatge eliminat</string> - <string name="notice_room_avatar_removed_by_you">Has eliminat l\'avatar de la sala</string> - <string name="notice_room_avatar_removed">%1$s ha eliminat l\'avatar de la sala</string> - <string name="notice_room_topic_removed_by_you">Has eliminat el tema de la sala</string> - <string name="notice_room_name_removed_by_you">Has eliminat el nom de la sala</string> - <string name="notice_requested_voip_conference_by_you">Has sol·licitat una conferència VoIP</string> - <string name="notice_room_update_by_you">Has actualitzat aquesta sala.</string> - <string name="notice_room_update">%s ha actualitzat aquesta sala.</string> - <string name="notice_end_to_end_by_you">Has activat el xifrat d\'extrem a extrem (%1$s)</string> - <string name="notice_made_future_direct_room_visibility_by_you">Has establert la visibilitat dels missatges futurs a %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s ha establert la visibilitat dels missatges futurs a %2$s</string> - <string name="notice_made_future_room_visibility_by_you">Has establert la visibilitat de l\'historial futur de la sala a %1$s</string> - <string name="notice_ended_call_by_you">Has finalitzat la trucada.</string> - <string name="notice_answered_call_by_you">Has respost a la trucada.</string> - <string name="notice_call_candidates_by_you">Has enviat dades per configurar la trucada.</string> - <string name="notice_call_candidates">%s ha enviat dades per configurar la trucada.</string> - <string name="notice_placed_voice_call_by_you">Has realitzat una trucada de veu.</string> - <string name="notice_placed_video_call_by_you">Has realitzat una videotrucada.</string> - <string name="notice_room_name_changed_by_you">Has canviat el nom de la sala a: %1$s</string> - <string name="notice_room_avatar_changed_by_you">Has canviat l\'avatar de la sala</string> - <string name="notice_room_avatar_changed">%1$s ha canviat l\'avatar de la sala</string> - <string name="notice_room_topic_changed_by_you">Has canviat el tema a: %1$s</string> - <string name="notice_display_name_removed_by_you">Has eliminat el teu nom de visualització (era %1$s)</string> - <string name="notice_display_name_changed_from_by_you">Has canviat el teu nom de visualització de %1$s a %2$s</string> - <string name="notice_display_name_set_by_you">Has canviat el teu nom de visualització a %1$s</string> - <string name="notice_avatar_url_changed_by_you">Has canviat el teu avatar</string> - <string name="notice_room_withdraw_by_you">Has retirat la invitació de %1$s</string> - <string name="notice_room_unban_by_you">Has tret el veto a %1$s</string> - <string name="notice_room_reject_by_you">Has rebutjat la invitació</string> - <string name="notice_direct_room_created_by_you">Has creat la discussió</string> - <string name="notice_direct_room_created">%1$s ha creat la discussió</string> - <string name="notice_direct_room_join_by_you">T\'has unit</string> - <string name="notice_direct_room_join">%1$s s\'ha unit</string> - <string name="notice_room_join_by_you">T\'has unit a la sala</string> - <string name="notice_room_invite_by_you">Has convidat a %1$s</string> - <string name="notice_room_created_by_you">Has creat la sala</string> - <string name="notice_room_created">%1$s ha creat la sala</string> - <string name="notice_room_invite_no_invitee_by_you">La teva invitació</string> - <string name="summary_you_sent_sticker">Has enviat un adhesiu.</string> - <string name="summary_you_sent_image">Has enviat una imatge.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-cs/strings.xml b/matrix-sdk-android/src/main/res/values-cs/strings.xml deleted file mode 100644 index adb35179279190c52c2b3c74d90b5591b0b414d5..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-cs/strings.xml +++ /dev/null @@ -1,271 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <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á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">%1$s vstoupil do mÃstnosti</string> - <string name="notice_room_leave">Uživatel %1$s odeÅ¡el</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">%1$s zapnuli end-to-end Å¡ifrovánà (%2$s)</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">%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à 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> - <string name="message_failed_to_upload">Obrázek nelze nahrát</string> - <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é znovu vstoupit do prázdné mÃstnosti.</string> - <string name="encrypted_message">Å ifrovaná zpráva</string> - <string name="medium_email">E-mailová adresa</string> - <string name="medium_phone_number">Telefonnà ÄÃslo</string> - <string name="room_displayname_invite_from">Pozvánà od %s</string> - <string name="room_displayname_room_invite">Pozvánà do mÃstnosti</string> - <string name="room_displayname_two_members">%1$s a %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s a jeden dalÅ¡Ã</item> - <item quantity="few">%1$s a %2$d dalÅ¡Ã</item> - <item quantity="other">%1$s a %2$d dalÅ¡Ãch</item> - </plurals> - <string name="room_displayname_empty_room">Prázdná 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">%1$s zruÅ¡ili pozvánku do mÃstnosti pro %2$s</string> - <string name="initial_sync_start_importing_account">Úvodnà synchronizace: -\nImportuji úÄet…</string> - <string name="initial_sync_start_importing_account_crypto">Úvodnà synchronizace: -\nImportuji klÃÄe</string> - <string name="initial_sync_start_importing_account_rooms">Úvodnà synchronizace: -\nImportuji mÃstnosti</string> - <string name="initial_sync_start_importing_account_joined_rooms">Úvodnà synchronizace: -\nImportuji mÃstnostÃ, jichž jste Äleny</string> - <string name="initial_sync_start_importing_account_left_rooms">Úvodnà synchronizace: -\nImportuji mÃstnost, jež jste opustili</string> - <string name="initial_sync_start_importing_account_groups">Úvodnà synchronizace: -\nImportuji komunity</string> - <string name="initial_sync_start_importing_account_data">Úvodnà synchronizace: -\nImportuji data úÄtu</string> - <string name="event_status_sending_message">OdesÃlám zprávu…</string> - <string name="initial_sync_start_importing_account_invited_rooms">Úvodnà synchronizace: -\nImportuji pozvánÃ</string> - <string name="clear_timeline_send_queue">Vymazat frontu neodeslaných zpráv</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 %1$s jako adresu pro tuto mÃstnost.</item> - <item quantity="few">Odstranili jste %1$s jako adresuy pro tuto mÃstnost.</item> - <item quantity="other">Odstranili jste %1$s jako adresy pro tuto mÃstnost.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s pÅ™idali %2$s 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 %1$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> - <string name="notice_direct_room_guest_access_forbidden_by_you">Zamezili jste hostům vstoupit do této mÃstnosti.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s zamezil hostům vstoupit do této mÃstnosti.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Povolili jste hostům vstoupit.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s povolil hostům vstoupit.</string> - <string name="notice_direct_room_leave_with_reason_by_you">OdeÅ¡li jste. Důvod: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s odeÅ¡li. Důvod: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Vstoupili jste. Důvod: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s vstoupili. Důvod: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Vy jste zruÅ¡ili pozvánà pro %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s zruÅ¡ili pozvánà pro %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Vy jste pozvali %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s pozvali %2$s</string> - <string name="notice_direct_room_update_by_you">Vy jste tady provedli upgrade.</string> - <string name="notice_direct_room_update">%s tady provedli upgrade.</string> - <string name="notice_made_future_direct_room_visibility_by_you">UÄinili jste budoucà zprávy viditelné pro %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s uÄinili budoucà zprávy viditelné pro %2$s</string> - <string name="notice_direct_room_leave_by_you">OdeÅ¡li jste z mÃstnosti</string> - <string name="notice_direct_room_leave">%1$s odeÅ¡li z mÃstnosti</string> - <string name="notice_direct_room_join_by_you">Vstoupili jste</string> - <string name="notice_direct_room_join">%1$s vstoupili</string> - <string name="notice_direct_room_created_by_you">Založili jste diskusi</string> - <string name="notice_direct_room_created">%1$s založil diskusi</string> - <string name="room_displayname_empty_room_was">Prázdná mÃstnost (byla %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s a %4$d dalÅ¡Ã</item> - <item quantity="few">%1$s, %2$s, %3$s a %4$d dalÅ¡Ã</item> - <item quantity="other">%1$s, %2$s, %3$s a %4$d dalÅ¡Ãch</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s a %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s a %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 ÚÄast vÅ¡ech serverů je zakázána! Tuto mÃstnost již nelze použÃt.</string> - <string name="notice_room_server_acl_updated_no_change">Beze zmÄ›ny.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Servery shodujÃcà se doslovnÄ› s IP jsou nynà zakázány.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Servery shodujÃcà se doslovnÄ› s IP jsou nynà povoleny.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Servery shodujÃcà se s %s byly odstranÄ›ny ze seznamu povolených.</string> - <string name="notice_room_server_acl_updated_allowed">• Servery shodujÃcà se s %s jsou nynà povoleny.</string> - <string name="notice_room_server_acl_updated_was_banned">• Servery shodujÃcà se s %s byly odstranÄ›ny ze seznamu zakázaných.</string> - <string name="notice_room_server_acl_updated_banned">• Servery shodujÃcà se s %s jsou nynà zakázány.</string> - <string name="notice_room_server_acl_updated_title_by_you">ZmÄ›nili jste ACL serveru pro tuto mÃstnost.</string> - <string name="notice_room_server_acl_updated_title">%s zmÄ›nili ACL serveru pro tuto mÃstnost.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Server shodujÃcà se doslovnÄ› s IP je povolen.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Server shodujÃcà se doslovnÄ› s IP je zakázán.</string> - <string name="notice_room_server_acl_set_allowed">• Server shodujÃcà se s %s je povolen.</string> - <string name="notice_room_server_acl_set_banned">• Server shodujÃcà se s %s je zakázán.</string> - <string name="notice_room_server_acl_set_title_by_you">Nastavili jste ACL serveru pro tuto mÃstnost.</string> - <string name="notice_room_server_acl_set_title">%s nastavili ACL serveru pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_no_change_by_you">ZmÄ›nili jste adresy pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_no_change">%1$s zmÄ›nili adresy pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">ZmÄ›nili jste hlavnà a alternativnà adresu pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s zmÄ›nili hlavnà a alternativnà adresu pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">ZmÄ›nili jste alternativnà adresu pro tuto mÃstnost.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s zmÄ›nili alternativnà adresu pro tuto mÃstnost.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Odstranili jste alternativnà adresu %1$s pro tuto mÃstnost.</item> - <item quantity="few">Odstranili jste alternativnà adresy %1$s pro tuto mÃstnost.</item> - <item quantity="other">Odstranili jste alternativnà adresy %1$s pro tuto mÃstnost.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s odstranili alternativnà adresu %2$s pro tuto mÃstnost.</item> - <item quantity="few">%1$s odstranili alternativnà adresy %2$s pro tuto mÃstnost.</item> - <item quantity="other">%1$s odstranili alternativnà adresy %2$s pro tuto mÃstnost.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">PÅ™idali jste alternativnà adresu %1$s pro tuto mÃstnost.</item> - <item quantity="few">PÅ™idali jste alternativnà adresy %1$s pro tuto mÃstnost.</item> - <item quantity="other">PÅ™idali jste alternativnà adresy %1$s pro tuto mÃstnost.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s pÅ™idali alternativnà adresu %2$s pro tuto mÃstnost.</item> - <item quantity="few">%1$s pÅ™idali alternativnà adresy %2$s pro tuto mÃstnost.</item> - <item quantity="other">%1$s pÅ™idali alternativnà adresy %2$s pro tuto mÃstnost.</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-cs/strings_sas.xml b/matrix-sdk-android/src/main/res/values-cs/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..1ef9d56f609a9c2456d6ae519f05af2b367fc61a --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-cs/strings_sas.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> + <string name="verification_emoji_dog">Pes</string> + <string name="verification_emoji_cat">KoÄka</string> + <string name="verification_emoji_lion">Lev</string> + <string name="verification_emoji_horse">Kůň</string> + <string name="verification_emoji_unicorn">Jednorožec</string> + <string name="verification_emoji_pig">Prase</string> + <string name="verification_emoji_elephant">Slon</string> + <string name="verification_emoji_rabbit">KrálÃk</string> + <string name="verification_emoji_panda">Panda</string> + <string name="verification_emoji_rooster">Kohout</string> + <string name="verification_emoji_penguin">TuÄňák</string> + <string name="verification_emoji_turtle">Želva</string> + <string name="verification_emoji_fish">Ryba</string> + <string name="verification_emoji_octopus">Chobotnice</string> + <string name="verification_emoji_butterfly">Motýl</string> + <string name="verification_emoji_flower">KvÄ›tina</string> + <string name="verification_emoji_tree">Strom</string> + <string name="verification_emoji_cactus">Kaktus</string> + <string name="verification_emoji_mushroom">Houba</string> + <string name="verification_emoji_globe">ZemÄ›koule</string> + <string name="verification_emoji_moon">MÄ›sÃc</string> + <string name="verification_emoji_cloud">Mrak</string> + <string name="verification_emoji_fire">Oheň</string> + <string name="verification_emoji_banana">Banán</string> + <string name="verification_emoji_apple">Jablko</string> + <string name="verification_emoji_strawberry">Jahoda</string> + <string name="verification_emoji_corn">KukuÅ™ice</string> + <string name="verification_emoji_pizza">Pizza</string> + <string name="verification_emoji_cake">Dort</string> + <string name="verification_emoji_heart">Srdce</string> + <string name="verification_emoji_smiley">SmajlÃk</string> + <string name="verification_emoji_robot">Robot</string> + <string name="verification_emoji_hat">Klobouk</string> + <string name="verification_emoji_glasses">Brýle</string> + <string name="verification_emoji_spanner">KlÃÄ</string> + <string name="verification_emoji_santa">Mikuláš</string> + <string name="verification_emoji_thumbs_up">Palec nahoru</string> + <string name="verification_emoji_umbrella">DeÅ¡tnÃk</string> + <string name="verification_emoji_hourglass">PÅ™esýpacà hodiny</string> + <string name="verification_emoji_clock">Hodiny</string> + <string name="verification_emoji_gift">Dárek</string> + <string name="verification_emoji_light_bulb">Žárovka</string> + <string name="verification_emoji_book">Kniha</string> + <string name="verification_emoji_pencil">Tužka</string> + <string name="verification_emoji_paperclip">Sponka</string> + <string name="verification_emoji_scissors">Nůžky</string> + <string name="verification_emoji_lock">Zámek</string> + <string name="verification_emoji_key">KlÃÄ</string> + <string name="verification_emoji_hammer">Kladivo</string> + <string name="verification_emoji_telephone">Telefon</string> + <string name="verification_emoji_flag">Vlajka</string> + <string name="verification_emoji_train">Vlak</string> + <string name="verification_emoji_bicycle">Kolo</string> + <string name="verification_emoji_aeroplane">Letadlo</string> + <string name="verification_emoji_rocket">Raketa</string> + <string name="verification_emoji_trophy">Pohár</string> + <string name="verification_emoji_ball">MÃÄ</string> + <string name="verification_emoji_guitar">Kytara</string> + <string name="verification_emoji_trumpet">Trumpeta</string> + <string name="verification_emoji_bell">Zvonek</string> + <string name="verification_emoji_anchor">Kotva</string> + <string name="verification_emoji_headphones">Sluchátka</string> + <string name="verification_emoji_folder">Složka</string> + <string name="verification_emoji_pin">Å pendlÃk</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-da/strings.xml b/matrix-sdk-android/src/main/res/values-da/strings.xml deleted file mode 100644 index 510fa231afe3660cd953d0a4ed85f98a242480bb..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-da/strings.xml +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s sendte et billede.</string> - - <string name="notice_room_invite_no_invitee">%ss invitation</string> - <string name="notice_room_invite">%1$s inviterede %2$s</string> - <string name="notice_room_invite_you">%1$s inviterede dig</string> - <string name="notice_room_join">%1$s forbandt</string> - <string name="notice_room_leave">%1$s forlod rummet</string> - <string name="notice_room_reject">%1$s afviste invitationen</string> - <string name="notice_room_kick">%1$s kickede %2$s</string> - <string name="notice_room_unban">%1$s unbannede %2$s</string> - <string name="notice_room_ban">%1$s bannede %2$s</string> - <string name="notice_room_withdraw">%1$s trak %2$ss invitation tilbage</string> - <string name="notice_avatar_url_changed">%1$s skiftede sin avatar</string> - <string name="notice_display_name_set">%1$s satte sit viste navn til %2$s</string> - <string name="notice_display_name_changed_from">%1$s ændrede sit viste navn fra %2$s til %3$s</string> - <string name="notice_display_name_removed">%1$s fjernede sit viste navn (%2$s)</string> - <string name="notice_room_topic_changed">%1$s ændrede emnet til: %2$s</string> - <string name="notice_room_name_changed">%1$s ændrede rumnavnet til: %2$s</string> - <string name="notice_placed_video_call">%s startede et videoopkald.</string> - <string name="notice_placed_voice_call">%s startede et stemmeopkald.</string> - <string name="notice_answered_call">%s svarede opkaldet.</string> - <string name="notice_ended_call">%s stoppede opkaldet.</string> - <string name="notice_made_future_room_visibility">%1$s gjorde den fremtidige rum historik synlig for %2$s</string> - <string name="notice_room_visibility_invited">alle medlemmer af rummet, fra det tidspunkt de er inviteret.</string> - <string name="notice_room_visibility_joined">alle medlemmer af rummet, fra det tidspunkt de er forbundede.</string> - <string name="notice_room_visibility_shared">Alle medlemmer af rummet.</string> - <string name="notice_room_visibility_world_readable">alle.</string> - <string name="notice_room_visibility_unknown">ukendt (%s).</string> - <string name="notice_end_to_end">%1$s slog ende-til-ende kryptering til (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s forespurgte en VoIP konference</string> - <string name="notice_voip_started">VoIP konference startet</string> - <string name="notice_voip_finished">VoIP konference afsluttet</string> - - <string name="notice_avatar_changed_too">(avatar blev ogsÃ¥ ændret)</string> - <string name="notice_room_name_removed">%1$s fjernede navnet pÃ¥ rummet</string> - <string name="notice_room_topic_removed">%1$s fjernede emnet for rummet</string> - <string name="notice_profile_change_redacted">%1$s opdaterede sin profil %2$s</string> - <string name="notice_room_third_party_invite">%1$s inviterede %2$s til rummet</string> - <string name="notice_room_third_party_registered_invite">%1$s accepterede invitationen til %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** Kunne ikke dekryptere: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Afsenderens enhed har ikke sendt os nøglerne til denne besked.</string> - - <string name="could_not_redact">Kunne ikke hemmeligholde</string> - <string name="unable_to_send_message">Kunne ikke sende besked</string> - - <string name="message_failed_to_upload">Kunne ikke uploade billede</string> - - <string name="network_error">Netværks fejl</string> - <string name="matrix_error">Matrix fejl</string> - - <string name="room_error_join_failed_empty_room">Det er i øjeblikket ikke muligt at genforbinde til et tomt rum.</string> - - <string name="encrypted_message">Krypteret besked</string> - - <string name="medium_email">mailadresse</string> - <string name="medium_phone_number">Telefonnummer</string> - - <string name="room_displayname_invite_from">Invitation fra %s</string> - <string name="room_displayname_room_invite">Invitation til rum</string> - <string name="room_displayname_two_members">%1$s og %2$s</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s og 1 anden</item> - <item quantity="other">%1$s og %2$d andre</item> - </plurals> - - <string name="room_displayname_empty_room">Tomt rum</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-de/strings.xml b/matrix-sdk-android/src/main/res/values-de/strings.xml deleted file mode 100644 index bdeeafccb6825e90ff655e9c6be63d47ee3a7ea0..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-de/strings.xml +++ /dev/null @@ -1,224 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s hat ein Bild gesendet.</string> - <string name="notice_room_invite_no_invitee">Einladung von %s</string> - <string name="notice_room_invite">%1$s hat %2$s eingeladen</string> - <string name="notice_room_invite_you">%1$s hat dich eingeladen</string> - <string name="notice_room_join">%1$s hat den Raum betreten</string> - <string name="notice_room_leave">%1$s hat den Raum verlassen</string> - <string name="notice_room_reject">%1$s hat die Einladung abgelehnt</string> - <string name="notice_room_kick">%1$s hat %2$s gekickt</string> - <string name="notice_room_unban">%1$s hat die Sperre von %2$s aufgehoben</string> - <string name="notice_room_ban">%1$s hat %2$s verbannt</string> - <string name="notice_room_withdraw">%1$s hat die Einladung für %2$s zurückgezogen</string> - <string name="notice_avatar_url_changed">%1$s hat das Profilbild geändert</string> - <string name="notice_display_name_set">%1$s hat den Anzeigenamen geändert in %2$s</string> - <string name="notice_display_name_changed_from">%1$s hat den Anzeigenamen von %2$s auf %3$s geändert</string> - <string name="notice_display_name_removed">%1$s hat den Anzeigenamen gelöscht (%2$s)</string> - <string name="notice_room_topic_changed">%1$s hat das Raumthema geändert auf: %2$s</string> - <string name="notice_room_name_changed">%1$s hat den Raumnamen geändert in: %2$s</string> - <string name="notice_placed_video_call">%s hat einen Videoanruf durchgeführt.</string> - <string name="notice_placed_voice_call">%s hat einen Sprachanruf getätigt.</string> - <string name="notice_answered_call">%s hat den Anruf angenommen.</string> - <string name="notice_ended_call">%s hat den Anruf beendet.</string> - <string name="notice_made_future_room_visibility">%1$s hat den zukünftigen Chatverlauf sichtbar gemacht für %2$s</string> - <string name="notice_room_visibility_invited">Alle Mitglieder (ab dem Zeitpunkt, an dem sie eingeladen wurden).</string> - <string name="notice_room_visibility_joined">Alle Mitglieder (ab dem Zeitpunkt, an dem sie den Raum betreten haben).</string> - <string name="notice_room_visibility_shared">alle Raum-Mitglieder.</string> - <string name="notice_room_visibility_world_readable">Jeder.</string> - <string name="notice_room_visibility_unknown">Unbekannt (%s).</string> - <string name="notice_end_to_end">%1$s hat die Ende-zu-Ende-Verschlüsselung aktiviert (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s möchte eine VoIP-Konferenz beginnen</string> - <string name="notice_voip_started">VoIP-Konferenz gestartet</string> - <string name="notice_voip_finished">VoIP-Konferenz beendet</string> - <string name="notice_avatar_changed_too">(Profilbild wurde ebenfalls geändert)</string> - <string name="notice_room_name_removed">%1$s hat den Raumnamen entfernt</string> - <string name="notice_room_topic_removed">%1$s hat das Raum-Thema entfernt</string> - <string name="notice_profile_change_redacted">%1$s hat das Benutzerprofil aktualisiert %2$s</string> - <string name="notice_room_third_party_invite">%1$s hat eine Einladung an %2$s gesendet</string> - <string name="notice_room_third_party_registered_invite">%1$s hat die Einladung in %2$s akzeptiert</string> - <string name="notice_crypto_unable_to_decrypt">** Nicht entschlüsselbar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Das absendende Gerät hat uns keine Schlüssel für diese Nachricht übermittelt.</string> - <!-- Room Screen --> - <string name="could_not_redact">Entfernen nicht möglich</string> - <string name="unable_to_send_message">Nachricht kann nicht gesendet werden</string> - <string name="message_failed_to_upload">Bild konnte nicht hochgeladen werden</string> - <!-- general errors --> - <string name="network_error">Netzwerk-Fehler</string> - <string name="matrix_error">Matrix-Fehler</string> - <!-- Home Screen --> - <!-- Last seen time --> - <!-- call events --> - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Es ist aktuell nicht möglich, einen leeren Raum erneut zu betreten.</string> - <string name="encrypted_message">Verschlüsselte Nachricht</string> - <!-- medium friendly name --> - <string name="medium_email">E-Mail-Adresse</string> - <string name="medium_phone_number">Telefonnummer</string> - <string name="summary_user_sent_sticker">%1$s hat einen Sticker gesendet.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Einladung von %s</string> - <string name="room_displayname_room_invite">Raumeinladung</string> - <string name="room_displayname_two_members">%1$s und %2$s</string> - <string name="room_displayname_empty_room">Leerer Raum</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s und 1 andere(r)</item> - <item quantity="other">%1$s und %2$d andere</item> - </plurals> - <string name="notice_event_redacted">Nachricht entfernt</string> - <string name="notice_event_redacted_by">Nachricht entfernt von %1$s</string> - <string name="notice_event_redacted_with_reason">Nachricht entfernt [Grund: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Nachricht entfernt von %1$s [Grund: %2$s]</string> - <string name="notice_room_update">%s hat diesen Raum aufgewertet.</string> - <string name="event_status_sending_message">Sende eine Nachricht…</string> - <string name="clear_timeline_send_queue">Sendewarteschlange leeren</string> - <string name="initial_sync_start_importing_account">Erste Synchronisation: -\nImportiere Benutzerkonto…</string> - <string name="initial_sync_start_importing_account_crypto">Erste Synchronisation: -\nImportiere Cryptoschlüssel</string> - <string name="initial_sync_start_importing_account_rooms">Erste Synchronisation: -\nImportiere Räume</string> - <string name="initial_sync_start_importing_account_joined_rooms">Erste Synchronisation: -\nImportiere betretene Räume</string> - <string name="initial_sync_start_importing_account_invited_rooms">Erste Synchronisation: -\nImportiere eingeladene Räume</string> - <string name="initial_sync_start_importing_account_left_rooms">Erste Synchronisation: -\nImportiere verlassene Räume</string> - <string name="initial_sync_start_importing_account_groups">Erste Synchronisation: -\nImportiere Communities</string> - <string name="initial_sync_start_importing_account_data">Erste Synchronisation: -\nImportiere Benutzerdaten</string> - <string name="notice_room_third_party_revoked_invite">%1$s hat die Einladung an %2$s, den Raum zu betreten, zurückgezogen</string> - <string name="notice_room_invite_no_invitee_with_reason">%1$s\'s Einladung. Grund: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s hat %2$s eingeladen. Grund: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s hat dich eingeladen. Grund: %2$s</string> - <string name="notice_room_join_with_reason">%1$s ist dem Raum beigetreten. Grund: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s hat den Raum verlassen. Grund: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s hat die Einladung abgelehnt. Grund: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s hat %2$s gekickt. Grund: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s hat Sperre von %2$s aufgehoben. Grund: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s hat %2$s verbannt. Grund: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s hat eine Einladung an %2$s gesandt um diesem Raum beizutreten. Grund: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s hat Einladung an %2$s zu Betreten dieses Raumes zurückgezogen. Grund: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s hat die Einladung für %2$s angenommen. Grund: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s hat Einladung für %2$s verworfen. Grund: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s fügt %2$s als eine Adresse für diesen Raum hinzu.</item> - <item quantity="other">%1$s fügt %2$s als Adressen für diesen Raum hinzu.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s entfernt %2$s als eine Adresse für diesen Raum.</item> - <item quantity="other">%1$s entfernt %2$s als Adressen für diesen Raum.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s fügt %2$s als Adresse für diesen Raum hinzu und entfernt %3$s.</string> - <string name="notice_room_canonical_alias_set">%1$s legt die Hauptadresse fest für diesen Raum als %2$s fest.</string> - <string name="notice_room_canonical_alias_unset">%1$s entfernt die Hauptadresse für diesen Raum.</string> - <string name="notice_room_guest_access_can_join">%1$s hat Gästen erlaubt den Raum zu betreten.</string> - <string name="notice_room_guest_access_forbidden">%1$s hat Gästen untersagt den Raum zu betreten.</string> - <string name="notice_end_to_end_ok">%1$s aktivierte Ende-zu-Ende-Verschlüsselung.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s aktivierte Ende-zu-Ende-Verschlüsselung (unbekannter Algorithmus %2$s).</string> - <string name="key_verification_request_fallback_message">%s fordert zur Ãœberprüfung deines Schlüssels auf, jedoch unterstützt dein Client nicht die Schlüsselüberprüfung im Chat. Du musst die herkömmliche Schlüsselüberprüfung verwenden, um die Schlüssel zu überprüfen.</string> - <string name="summary_you_sent_image">Du hast ein Bild gesendet.</string> - <string name="summary_you_sent_sticker">Du hast einen Sticker gesendet.</string> - <string name="notice_room_invite_no_invitee_by_you">Deine Einladung</string> - <string name="notice_room_created">%1$s hat den Raum erstellt</string> - <string name="notice_room_created_by_you">Du hast den Raum erstellt</string> - <string name="notice_room_invite_by_you">Du hast %1$s eingeladen</string> - <string name="notice_room_join_by_you">Du bist dem Raum beigetreten</string> - <string name="notice_room_leave_by_you">Du hast den Raum verlassen</string> - <string name="notice_room_reject_by_you">Du hast die Einladung abgelehnt</string> - <string name="notice_room_kick_by_you">Du hast %1$s aus dem Raum entfernt</string> - <string name="notice_room_unban_by_you">Du hast den Bann von %1$s aufgehoben</string> - <string name="notice_room_ban_by_you">Du hast %1$s gebannt</string> - <string name="notice_room_withdraw_by_you">Du hast die Einladung von %1$s zurückgenommen</string> - <string name="notice_avatar_url_changed_by_you">Du hast dein Profilbild geändert</string> - <string name="notice_display_name_set_by_you">Du hast deinen Anzeigenamen zu %1$s geändert</string> - <string name="notice_display_name_changed_from_by_you">Du hast deinen Anzeigenamen von %1$s zu %2$s geändert</string> - <string name="notice_display_name_removed_by_you">Du hast deinen Anzeigenamen entfernt (er war %1$s)</string> - <string name="notice_room_topic_changed_by_you">Du hast das Thema geändert auf: %1$s</string> - <string name="notice_room_avatar_changed">%1$s hat das Bild des Raumes geändert</string> - <string name="notice_room_avatar_changed_by_you">Du hast das Bild des Raumes geändert</string> - <string name="notice_room_name_changed_by_you">Du hast den Raumnamen zu %1$s geändert</string> - <string name="notice_placed_video_call_by_you">Du hast einen Videoanruf gestartet.</string> - <string name="notice_placed_voice_call_by_you">Du hast einen Audioanruf gestartet.</string> - <string name="notice_answered_call_by_you">Du hast den Anruf angenommen.</string> - <string name="notice_ended_call_by_you">Du hast den Anruf beendet.</string> - <string name="notice_made_future_room_visibility_by_you">Du hast den zukünftigen Nachrichtenverlauf für %1$s sichtbar gemacht</string> - <string name="notice_end_to_end_by_you">Du hast Ende-zu-Ende-Verschlüsselung aktiviert (%1$s)</string> - <string name="notice_room_update_by_you">Du hast den Raum aufgwertet.</string> - <string name="notice_requested_voip_conference_by_you">Du hast eine VoIP-Konferenz angefordert</string> - <string name="notice_room_name_removed_by_you">Du hast den Raumnamen entfernt</string> - <string name="notice_room_topic_removed_by_you">Du hast das Raumthema entfernt</string> - <string name="notice_room_avatar_removed">%1$s hat das Bild des Raumes entfernt</string> - <string name="notice_room_avatar_removed_by_you">Du hast das Bild des Raumes entfernt</string> - <string name="notice_profile_change_redacted_by_you">Du hast dein Profil %1$s aktualisiert</string> - <string name="notice_room_third_party_invite_by_you">Du hast %1$s in den Raum eingeladen</string> - <string name="notice_room_third_party_revoked_invite_by_you">Du hast die Einladung für %1$s zurückgenommen</string> - <string name="notice_room_third_party_registered_invite_by_you">Du hast die Einladung für %1$s akzeptiert</string> - <string name="notice_widget_added">%1$s hat das %2$s Widget hinzugefügt</string> - <string name="notice_widget_added_by_you">Du hast das %1$s Widget hinzugefügt</string> - <string name="notice_widget_removed">%1$s hat das %2$s Widget entfernt</string> - <string name="notice_widget_removed_by_you">Du hast das %1$s Widget entfernt</string> - <string name="notice_widget_modified">%1$s hat das %2$s Widget modifiziert</string> - <string name="notice_widget_modified_by_you">Du hast das %1$s Widget modifiziert</string> - <string name="power_level_admin">Administrator</string> - <string name="power_level_moderator">Moderator</string> - <string name="power_level_default">Standard</string> - <string name="power_level_custom">Benutzerdefiniert (%1$d)</string> - <string name="power_level_custom_no_value">Benutzerdefiniert</string> - <string name="notice_power_level_changed_by_you">Du hast die Berechtigungsstufe von %1$s geändert.</string> - <string name="notice_power_level_changed">%1$s hat die Berechtigungsstufe von %2$s geändert.</string> - <string name="notice_power_level_diff">%1$s von %2$s zu %3$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Deine Einladung. Grund: %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Du hast %1$s eingeladen. Grund: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Du bist dem Raum beigetreten. Grund: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Du hast den Raum verlassen. Grund: %1$s</string> - <string name="notice_room_reject_with_reason_by_you">Du hast die Einladung abgelehnt. Grund: %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Du hast %1$s aus dem Raum entfernt. Grund %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Du hast den Bann von %1$s aufgehoben. Grund: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Du hast %1$s gebannt. Grund: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Du hast %1$s in den Raum eingeladen. Grund: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Du hast die Einladung für %1$s zurückgenommen. Grund: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Du hast die Einladung von %1$s angenommen. Grund: %2$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Du hast die Einladung von %1$s abgelehnt. Grund: %2$s</string> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Du hast die Raumaddresse %1$s hinzugefügt.</item> - <item quantity="other">Du hast die Raumaddressen %1$s hinzugefügt.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Du hast die Raum-Adresse %1$s vom Raum entfernt.</item> - <item quantity="other">Du hast die Raum-Adressen %1$s vom Raum entfernt.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed_by_you">Du hast den Raumaddressen %1$s hinzugefügt und %2$s entfernt.</string> - <string name="notice_room_canonical_alias_set_by_you">Du hast die Hauptaddresse für diesen Raum auf %1$s gesetzt.</string> - <string name="notice_room_canonical_alias_unset_by_you">Du hast die Hauptaddresse des Raums entfernt.</string> - <string name="notice_room_guest_access_can_join_by_you">Du hast Gästen erlaubt dem Raum beizutreten.</string> - <string name="notice_room_guest_access_forbidden_by_you">Du hast Gästen untersagt dem Raum beizutreten.</string> - <string name="notice_end_to_end_ok_by_you">Du hast Ende-zu-Ende-Verschlüsselung aktiviert.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Du hast Ende-zu-Ende-Verschlüsselung aktiviert (unbekannter Algorithmus %1$s).</string> - <string name="notice_call_candidates">%s hat Daten gesendet, um einen Anruf zu starten.</string> - <string name="notice_call_candidates_by_you">Du hast Daten geschickt, um eine Anruf zu starten.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Du hast Gästen erlaubt hier beizutreten.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s hat Gästen erlaubt hier beizutreten.</string> - <string name="notice_direct_room_join_with_reason_by_you">Du bist beigetreten. Grund: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s ist beigetreten. Grund: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Du hast die Einladung für %1$s zurückgezogen</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s hat die Einladung für %2$s zurückgezogen</string> - <string name="notice_direct_room_third_party_invite_by_you">Du hast %1$s eingeladen</string> - <string name="notice_direct_room_third_party_invite">%1$s hat %2$s eingeladen</string> - <string name="notice_made_future_direct_room_visibility_by_you">Du hast zukünftige Nachrichten für %1$s sichtbar gemacht</string> - <string name="notice_made_future_direct_room_visibility">%1$s hat zukünftige Nachrichten für %2$s sichtbar gemacht</string> - <string name="notice_direct_room_leave_by_you">Du hast den Raum verlassen</string> - <string name="notice_direct_room_leave">%1$s hat den Raum verlassen</string> - <string name="notice_direct_room_join_by_you">Du bist beigetreten</string> - <string name="notice_direct_room_join">%1$s ist beigetreten</string> - <string name="notice_direct_room_created_by_you">Du hast eine Diskussion erstellt</string> - <string name="notice_direct_room_created">%1$s hat eine Diskussion erstellt</string> - <string name="notice_direct_room_update">%s hat hier ein Upgrade durchgeführt.</string> - <string name="notice_direct_room_update_by_you">Du hast hier ein Upgrade durchgeführt.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Du hast Gästen untersagt den Raum zu betreten.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s hat Gästen untersagt den Raum zu betreten.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Du bist gegangen. Grund: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s ist gegangen. Grund: %2$s</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-de/strings_sas.xml b/matrix-sdk-android/src/main/res/values-de/strings_sas.xml index 108dedd1a593dfc37cbdd61c0c1e36443544773a..be75f797f93cbbb88130a5271a16d3efc550c861 100644 --- a/matrix-sdk-android/src/main/res/values-de/strings_sas.xml +++ b/matrix-sdk-android/src/main/res/values-de/strings_sas.xml @@ -27,20 +27,20 @@ <string name="verification_emoji_banana">Banane</string> <string name="verification_emoji_apple">Apfel</string> <string name="verification_emoji_strawberry">Erdbeere</string> - <string name="verification_emoji_corn">Korn</string> + <string name="verification_emoji_corn">Mais</string> <string name="verification_emoji_pizza">Pizza</string> <string name="verification_emoji_cake">Kuchen</string> <string name="verification_emoji_heart">Herz</string> - <string name="verification_emoji_smiley">Smiley</string> + <string name="verification_emoji_smiley">Lächeln</string> <string name="verification_emoji_robot">Roboter</string> <string name="verification_emoji_hat">Hut</string> <string name="verification_emoji_glasses">Brille</string> <string name="verification_emoji_spanner">Schraubenschlüssel</string> - <string name="verification_emoji_santa">Nikolaus</string> + <string name="verification_emoji_santa">Weihnachtsmann</string> <string name="verification_emoji_thumbs_up">Daumen Hoch</string> <string name="verification_emoji_umbrella">Regenschirm</string> <string name="verification_emoji_hourglass">Sanduhr</string> - <string name="verification_emoji_clock">Wecker</string> + <string name="verification_emoji_clock">Uhr</string> <string name="verification_emoji_gift">Geschenk</string> <string name="verification_emoji_light_bulb">Glühbirne</string> <string name="verification_emoji_book">Buch</string> @@ -56,7 +56,7 @@ <string name="verification_emoji_bicycle">Fahrrad</string> <string name="verification_emoji_aeroplane">Flugzeug</string> <string name="verification_emoji_rocket">Rakete</string> - <string name="verification_emoji_trophy">Trophäe</string> + <string name="verification_emoji_trophy">Pokal</string> <string name="verification_emoji_ball">Ball</string> <string name="verification_emoji_guitar">Gitarre</string> <string name="verification_emoji_trumpet">Trompete</string> diff --git a/matrix-sdk-android/src/main/res/values-el/strings.xml b/matrix-sdk-android/src/main/res/values-el/strings.xml deleted file mode 100644 index 9db4e918496520ddd054472b4d881b178101781e..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-el/strings.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="medium_email">ΗλεκτÏονική διεÏθυνση</string> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">Ο/Η %1$s Îστειλε μια εικόνα.</string> - <string name="summary_user_sent_sticker">Ο/Η %1$s Îστειλε Îνα αυτοκόλλητο.</string> - - <string name="notice_room_invite_you">Ο/Η %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_invite">Ο/Η %1$s Ï€Ïοσκάλεσε τον/την %2$s</string> - <string name="notice_room_invite_no_invitee">Η Ï€Ïόσκληση του/της %s</string> - <string name="medium_phone_number">ΑÏιθμός τηλεφώνου</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_answered_call">Ο/Η %s απάντησε στην κλήση.</string> - <string name="notice_ended_call">Ο/Η %s τεÏμάτισε την κλήση.</string> - - <string name="notice_placed_video_call">Ο/Η %s Ï€Ïαγματοποίησε μια κλήση βίντεο.</string> - <string name="notice_placed_voice_call">Ο/Η %s Ï€Ïαγματοποίησε μια κλήση ήχου.</string> - <string name="notice_made_future_room_visibility">Ο/Η %1$s κατÎστησε το μελλοντικό ιστοÏικό του δωματίου οÏατό στον/στην %2$s</string> - <string name="notice_room_visibility_invited">όλα τα μÎλη του δωματίου, από την στιγμή που Ï€Ïοσκλήθηκαν.</string> - <string name="notice_room_visibility_shared">όλα τα μÎλη του δωματίου.</string> - <string name="notice_room_visibility_world_readable">οποιοσδήποτε.</string> - <string name="notice_room_visibility_unknown">άγνωστος/η (%s).</string> - <string name="notice_avatar_changed_too">(Îγινε αλλαγή και του εικονιδίου χÏήστη)</string> - <string name="notice_room_name_removed">Ο/Η %1$s αφαίÏεσε το όνομα του δωματίου</string> - <string name="notice_room_topic_removed">Ο/Η %1$s αφαίÏεσε το θÎμα του δωματίου</string> - <string name="notice_profile_change_redacted">Ο/Η %1$s ανανÎωσε το Ï€Ïοφίλ του/της %2$s</string> - <string name="notice_room_third_party_registered_invite">Ο/Η %1$s δÎχτηκε την Ï€Ïόσκληση για το %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** Αδυναμία αποκÏυπτογÏάφησης: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Η συσκευή του/της αποστολÎα δεν μας Îχει στείλει τα κλειδιά για αυτό το μήνυμα.</string> - - <string name="unable_to_send_message">Αποτυχία αποστολής μηνÏματος</string> - - <string name="message_failed_to_upload">Αποτυχία αναφόÏτωσης εικόνας</string> - - <string name="network_error">Σφάλμα δικτÏου</string> - <string name="matrix_error">Σφάλμα του Matrix</string> - - <string name="encrypted_message">ΚÏυπτογÏαφημÎνο μήνυμα</string> - - <string name="notice_requested_voip_conference">Ο/Η %1$s ζήτησε μια VoIP διάσκεψη</string> - <string name="notice_voip_started">Η VoIP διάσκεψη ξεκίνησε</string> - <string name="notice_voip_finished">Η VoIP διάσκεψη Îληξε</string> - - <string name="notice_room_join">Ο/Η %1$s εισήλθε στο δωμάτιο</string> - - <string name="room_displayname_invite_from">Î Ïόσκληση από %s</string> - <string name="room_displayname_room_invite">Î Ïόσκληση στο δωμάτιο</string> - - <string name="room_displayname_two_members">%1$s και %2$s</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s και 1 ακόμα</item> - <item quantity="other">%1$s και %2$d ακόμα</item> - </plurals> - - <string name="room_displayname_empty_room">Άδειο δωμάτιο</string> - - <string name="notice_room_visibility_joined">όλα τα μÎλη του δωματίου από την στιγμή που εισήλθαν.</string> - <string name="notice_end_to_end">Ο/Η %1$s ενεÏγοποίησε την κÏυπτογÏάφηση απ\'άκÏη σ\'άκÏη (%2$s)</string> - - <string name="notice_room_third_party_invite">Ο/Η %1$s Îστειλε μία Ï€Ïόσκληση στον/στην %2$s για να εισÎλθει στο δωμάτιο</string> - <string name="room_error_join_failed_empty_room">Δεν είναι δυνατή ακόμα η επανείσοδος σε Îνα άδειο δωμάτιο.</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-eo/strings.xml b/matrix-sdk-android/src/main/res/values-eo/strings.xml deleted file mode 100644 index 10be2103cf1551ef5d34d4ee15ad16c8adcbb0d1..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-eo/strings.xml +++ /dev/null @@ -1,216 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_user_sent_image">%1$s sendis bildon.</string> - <string name="summary_user_sent_sticker">%1$s sendis glumarkon.</string> - <string name="notice_room_invite_no_invitee">Invito de %s</string> - <string name="notice_room_invite">%1$s invitis uzanton %2$s</string> - <string name="notice_room_invite_you">%1$s invitis vin</string> - <string name="notice_room_join">%1$s envenis</string> - <string name="notice_room_leave">%1$s foriris de la ĉambro</string> - <string name="notice_room_reject">%1$s rifuzis la inviton</string> - <string name="notice_room_kick">%1$s forpelis uzanton %2$s</string> - <string name="notice_room_unban">%1$s malforbaris uzanton %2$s</string> - <string name="notice_room_ban">%1$s forbaris uzanton %2$s</string> - <string name="notice_room_withdraw">%1$s nuligis inviton por %2$s</string> - <string name="notice_avatar_url_changed">%1$s ÅanÄis sian profilbildon</string> - <string name="notice_crypto_unable_to_decrypt">** Ne eblas malĉifri: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">La aparato de la sendinto ne sendis al ni la Ålosilojn por tiu mesaÄo.</string> - <string name="summary_message">%1$s: %2$s</string> - <string name="notice_display_name_set">%1$s ÅanÄis sian prezentan nomon al %2$s</string> - <string name="notice_display_name_changed_from">%1$s ÅanÄis sian prezentan nomon de %2$s al %3$s</string> - <string name="notice_display_name_removed">%1$s forigis sian prezentan nomon (%2$s)</string> - <string name="notice_room_topic_changed">%1$s ÅanÄis la temon al: %2$s</string> - <string name="notice_room_name_changed">%1$s ÅanÄis nomon de la ĉambro al: %2$s</string> - <string name="notice_placed_video_call">%s vidvokis.</string> - <string name="notice_placed_voice_call">%s voĉvokis.</string> - <string name="notice_answered_call">%s respondis la vokon.</string> - <string name="notice_ended_call">%s finis la vokon.</string> - <string name="notice_made_future_room_visibility">%1$s videbligis estontan historion de ĉambro al %2$s</string> - <string name="notice_room_visibility_invited">ĉiuj ĉambranoj, ekde siaj invitoj.</string> - <string name="notice_room_visibility_joined">ĉiuj ĉambranoj, ekde siaj aliÄoj.</string> - <string name="notice_room_visibility_shared">ĉiuj ĉambranoj.</string> - <string name="notice_room_visibility_world_readable">ĉiu ajn.</string> - <string name="notice_room_visibility_unknown">nekonata (%s).</string> - <string name="notice_end_to_end">%1$s Åaltis tutvojan ĉifradon (%2$s)</string> - <string name="notice_room_update">%s gradaltigis la ĉambron.</string> - <string name="notice_event_redacted">MesaÄo foriÄis</string> - <string name="notice_event_redacted_by">MesaÄon forigis %1$s</string> - <string name="notice_event_redacted_with_reason">MesaÄo foriÄis [kialo: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">MesaÄon forigis %1$s [kialo: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s Äisdatigis sian profilon %2$s</string> - <string name="notice_room_third_party_invite">%1$s sendis aliÄan inviton al %2$s</string> - <string name="notice_room_third_party_revoked_invite">%1$s nuligis la aliÄan inviton por %2$s</string> - <string name="notice_room_third_party_registered_invite">%1$s akceptis la inviton por %2$s</string> - <string name="could_not_redact">Ne povis redakti</string> - <string name="unable_to_send_message">Ne povas sendi mesaÄon</string> - <string name="message_failed_to_upload">Malsukcesis alÅuti bildon</string> - <string name="network_error">Reta eraro</string> - <string name="matrix_error">Matrix-eraro</string> - <string name="room_error_join_failed_empty_room">Nun ne eblas re-aliÄi al malplena ĉambro.</string> - <string name="encrypted_message">Ĉifrita mesaÄo</string> - <string name="medium_email">RetpoÅtadreso</string> - <string name="medium_phone_number">Telefonnumero</string> - <string name="room_displayname_invite_from">Invito de %s</string> - <string name="room_displayname_room_invite">Invito al ĉambro</string> - <string name="room_displayname_two_members">%1$s kaj %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s kaj 1 alia</item> - <item quantity="other">%1$s kaj %2$d aliaj</item> - </plurals> - <string name="room_displayname_empty_room">Malplena ĉambro</string> - <string name="initial_sync_start_importing_account">Komenca spegulado: -\nEnportante konton…</string> - <string name="initial_sync_start_importing_account_crypto">Komenca spegulado: -\nEnportante ĉifrilojn</string> - <string name="initial_sync_start_importing_account_rooms">Komenca spegulado: -\nEnportante ĉambrojn</string> - <string name="initial_sync_start_importing_account_joined_rooms">Komenca spegulado: -\nEnportante aliÄitajn ĉambrojn</string> - <string name="initial_sync_start_importing_account_invited_rooms">Komenca spegulado: -\nEnportante ĉambrojn de invitoj</string> - <string name="initial_sync_start_importing_account_left_rooms">Komenca spegulado: -\nEnportante forlasitajn ĉambrojn</string> - <string name="initial_sync_start_importing_account_groups">Komenca spegulado: -\nEnportante komunumojn</string> - <string name="initial_sync_start_importing_account_data">Komenca spegulado: -\nEnportante datumojn de konto</string> - <string name="event_status_sending_message">Sendante mesaÄon…</string> - <string name="clear_timeline_send_queue">Vakigi sendan atendovicon</string> - <string name="notice_requested_voip_conference">%1$s petis grupan vokon</string> - <string name="notice_voip_started">Grupa voko komenciÄis</string> - <string name="notice_voip_finished">Grupa voko finiÄis</string> - <string name="notice_avatar_changed_too">(ankaÅ profilbildo ÅanÄiÄis)</string> - <string name="notice_room_name_removed">%1$s forigis nomon de la ĉambro</string> - <string name="notice_room_topic_removed">%1$s forigis temon de la ĉambro</string> - <string name="notice_room_invite_no_invitee_with_reason">Invito de %1$s. Kialo: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s invitis uzanton %2$s. Kialo: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s invitis vin. Kialo: %2$s</string> - <string name="notice_room_join_with_reason">%1$s envenis. Kialo: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s foriris de la ĉambro. Kialo: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s rifuzis la inviton. Kialo: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s forpelis uzanton %2$s. Kialo: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s malforbaris uzanton %2$s. Kialo: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s forbaris uzanton %2$s. Kialo: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s sendis al %2$s inviton al la ĉambro. Kialo: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s nuligis la inviton al la ĉambro por %2$s. Kialo: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s akceptis la inviton por %2$s. Kialo: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s nuligis la inviton por %2$s. Kialo: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s aldonis %2$s kiel adreson por ĉi tiu ĉambro.</item> - <item quantity="other">%1$s aldonis %2$s kiel adresojn por ĉi tiu ĉambro.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s forigis %2$s kiel adreson por ĉi tiu ĉambro.</item> - <item quantity="other">%1$s forigis %2$s kiel adresojn por ĉi tiu ĉambro.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s aldonis %2$s kaj forigis %3$s kiel adresojn por ĉi tiu ĉambro.</string> - <string name="notice_room_canonical_alias_set">%1$s agordis la ĉefadreson de ĉi tiu ĉambro al %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s forigis la ĉefadreson de ĉi tiu ĉambro.</string> - <string name="notice_room_guest_access_can_join">%1$s permesis al gastoj enveni.</string> - <string name="notice_room_guest_access_forbidden">%1$s malpermesis al gastoj enveni.</string> - <string name="notice_end_to_end_ok">%1$s Åaltis tutvojan ĉifradon.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s Åaltis tutvojan ĉifradon (kun nerekonita algoritmo %2$s).</string> - <string name="key_verification_request_fallback_message">%s petas kontrolon de via Ålosilo, sed via kliento ne subtenas kontrolon de Ålosiloj en la babilujo. Vi devos uzi malnovan kontrolmanieron de Ålosiloj.</string> - <string name="notice_power_level_changed_by_you">Vi ÅanÄis la povnivelon de %1$s.</string> - <string name="notice_power_level_changed">%1$s sanÄis la povnivelon de %2$s.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Vi Åaltis tutvojan ĉifradon (kun nerekonita algoritmo %1$s).</string> - <string name="notice_end_to_end_ok_by_you">Vi Åaltis tutvojan ĉifradon.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Vi malpermesis al gastoj aliÄi.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s malpermesis al gastoj aliÄi.</string> - <string name="notice_room_guest_access_forbidden_by_you">Vi malpermesis al gastoj enveni.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Vi permesis al gastoj aliÄi.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s permesis al gastoj aliÄi.</string> - <string name="notice_room_guest_access_can_join_by_you">Vi permesis al gastoj enveni.</string> - <string name="notice_room_canonical_alias_unset_by_you">Vi forigis la ĉefadreson de ĉi tiu ĉambro.</string> - <string name="notice_room_canonical_alias_set_by_you">Vi agordis al ĉefadreson de ĉi tiu ĉambro al %1$s.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Vi aldonis %1$s kaj forigis %2$s kiel adresojn por ĉi tiu ĉambro.</string> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Vi forigis %1$s kiel adreson por ĉi tiu ĉambro.</item> - <item quantity="other">Vi forigis %1$s kiel adresojn por ĉi tiu ĉambro.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Vi aldonis %1$s kiel adreson por ĉi tiu ĉambro.</item> - <item quantity="other">Vi aldonis %1$s kiel adresojn por ĉi tiu ĉambro.</item> - </plurals> - <string name="notice_room_withdraw_with_reason_by_you">Vi nuligis la inviton por %1$s. Kialo: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Vi akceptis la inviton por %1$s. Kialo: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Vi nuligis inviton al la ĉambro por %1$s. Kialo: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Vi sendis al %1$s inviton al la ĉambro. Kialo: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Vi forbaris uzanton %1$s. Kialo: %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Vi malforbaris uzanton %1$s. Kialo: %2$s</string> - <string name="notice_room_kick_with_reason_by_you">Vi forpelis uzanton %1$s. Kialo: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Vi rifuzis la inviton. Kialo: %1$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">Vi foriris. Kialo: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s foriris. Kialo: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Vi foriris de la ĉambro. Kialo: %1$s</string> - <string name="notice_room_join_with_reason_by_you">Vi envenis. Kialo: %1$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Vi aliÄis. Kialo: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s aliÄis. Kialo: %2$s</string> - <string name="notice_room_invite_with_reason_by_you">Vi invitis uzanton %1$s. Kialo: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Via invito. Kialo: %1$s</string> - <string name="notice_power_level_diff">%1$s de %2$s al %3$s</string> - <string name="power_level_custom_no_value">Propra</string> - <string name="power_level_default">Ordinara</string> - <string name="power_level_custom">Propra (%1$d)</string> - <string name="power_level_moderator">Reguligisto</string> - <string name="power_level_admin">Administranto</string> - <string name="notice_widget_modified_by_you">Vi ÅanÄis la fenestraĵon %1$s</string> - <string name="notice_widget_modified">%1$s ÅanÄis la fenestraĵon %2$s</string> - <string name="notice_widget_removed_by_you">Vi forigis la fenestraĵon %1$s</string> - <string name="notice_widget_removed">%1$s forigis la fenestraĵon %2$s</string> - <string name="notice_widget_added_by_you">Vi aldonis la fenestraĵon %1$s</string> - <string name="notice_widget_added">%1$s aldonis la fenestraĵon %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">Vi akceptis la inviton por %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Vi nuligis la inviton por %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s nuligis la inviton por %2$s</string> - <string name="notice_room_third_party_revoked_invite_by_you">Vi nuligis la aliÄan inviton por %1$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Vi invitis uzanton %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s invitis uzanton %2$s</string> - <string name="notice_room_third_party_invite_by_you">Vi sendis aliÄan inviton al %1$s</string> - <string name="notice_profile_change_redacted_by_you">Vi Äisdatigis vian profilon %1$s</string> - <string name="notice_room_avatar_removed_by_you">Vi forigis bildon de la ĉambro</string> - <string name="notice_room_avatar_removed">%1$s forigis bildon de la ĉambro</string> - <string name="notice_room_topic_removed_by_you">Vi forigis temon de la ĉambro</string> - <string name="notice_room_name_removed_by_you">Vi forigis nomon de la ĉambro</string> - <string name="notice_requested_voip_conference_by_you">Vi petis grupan vokon</string> - <string name="notice_direct_room_update_by_you">Vi gradaltigis la interparolon.</string> - <string name="notice_direct_room_update">%s gradaltigis la interparolon.</string> - <string name="notice_room_update_by_you">Vi gradaltigis la ĉambron.</string> - <string name="notice_end_to_end_by_you">Vi Åaltis tutvojan ĉifradon (%1$s)</string> - <string name="notice_made_future_direct_room_visibility">%1$s videbligis al %2$s estontajn mesaÄojn</string> - <string name="notice_made_future_direct_room_visibility_by_you">Vi videbligis al %1$s estontajn mesaÄojn</string> - <string name="notice_made_future_room_visibility_by_you">Vi videbligis estontan historion de ĉambro al %1$s</string> - <string name="notice_ended_call_by_you">Vi finis la vokon.</string> - <string name="notice_answered_call_by_you">Vi respondis la vokon.</string> - <string name="notice_call_candidates_by_you">Vi sendis datumojn por prepari la vokon.</string> - <string name="notice_call_candidates">%s sendis datumojn por prepari la vokon.</string> - <string name="notice_placed_voice_call_by_you">Vi voĉvokis.</string> - <string name="notice_placed_video_call_by_you">Vi vidvokis.</string> - <string name="notice_room_name_changed_by_you">Vi ÅanÄis la nomon de la ĉambro al: %1$s</string> - <string name="notice_room_avatar_changed_by_you">Vi ÅanÄis la bildon de la ĉambro</string> - <string name="notice_room_avatar_changed">%1$s ÅanÄis la bildon de la ĉambro</string> - <string name="notice_room_topic_changed_by_you">Vi ÅanÄis la temon al: %1$s</string> - <string name="notice_display_name_removed_by_you">Vi forigis vian prezentan nomon (%1$s)</string> - <string name="notice_display_name_changed_from_by_you">Vi ÅanÄis vian prezentan nomon de %1$s al %2$s</string> - <string name="notice_display_name_set_by_you">Vi ÅanÄis vian prezentan nomon al %1$s</string> - <string name="notice_avatar_url_changed_by_you">Vi ÅanÄis vian profilbildon</string> - <string name="notice_room_withdraw_by_you">Vi nuligis inviton por %1$s</string> - <string name="notice_room_ban_by_you">Vi forbaris uzanton %1$s</string> - <string name="notice_room_unban_by_you">Vi malforbaris uzanton %1$s</string> - <string name="notice_room_kick_by_you">Vi forpelis uzanton %1$s</string> - <string name="notice_room_reject_by_you">Vi rifuzis la inviton</string> - <string name="notice_direct_room_leave_by_you">Vi foriris de la ĉambro</string> - <string name="notice_direct_room_leave">%1$s foriris de la ĉambro</string> - <string name="notice_room_leave_by_you">Vi foriris de la ĉambro</string> - <string name="notice_direct_room_join_by_you">Vi envenis</string> - <string name="notice_direct_room_join">%1$s envenis</string> - <string name="notice_room_join_by_you">Vi envenis</string> - <string name="notice_room_invite_by_you">Vi invitis uzanton %1$s</string> - <string name="notice_direct_room_created_by_you">Vi kreis la diskuton</string> - <string name="notice_direct_room_created">%1$s kreis la diskuton</string> - <string name="notice_room_created_by_you">Vi kreis la ĉambron</string> - <string name="notice_room_created">%1$s kreis la ĉambron</string> - <string name="notice_room_invite_no_invitee_by_you">Via invito</string> - <string name="summary_you_sent_sticker">Vi sendis glumarkon.</string> - <string name="summary_you_sent_image">Vi sendis bildon.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-es-rMX/strings.xml b/matrix-sdk-android/src/main/res/values-es-rMX/strings.xml deleted file mode 100644 index a8e8477005195e49021d3217de53866d1a54d8c4..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-es-rMX/strings.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s envió una imagen.</string> - - <string name="notice_room_invite_no_invitee">la invitación de %s</string> - <string name="notice_room_invite">%1$s invitó a %2$s</string> - <string name="notice_room_invite_you">%1$s te invitó</string> - <string name="notice_room_join">%1$s se unió</string> - <string name="notice_room_leave">%1$s salió</string> - <string name="notice_room_reject">%1$s rechazó la invitación</string> - <string name="notice_room_kick">%1$s quitó a %2$s</string> - <string name="notice_room_unban">%1$s desprohibió a %2$s</string> - <string name="notice_room_ban">%1$s prohibió %2$s</string> - <string name="notice_room_withdraw">%1$s retiró la invitación de %2$s</string> - <string name="notice_avatar_url_changed">%1$s cambió su foto de perfil</string> - <string name="notice_display_name_set">%1$s estableció %2$s como su nombre visible</string> - <string name="notice_display_name_changed_from">%1$s cambió su nombre visible de %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s eliminó su nombre visible (%2$s)</string> - <string name="notice_room_topic_changed">%1$s cambió el tema a: %2$s</string> - <string name="notice_room_name_changed">%1$s cambió el nombre de la sala a: %2$s</string> - <string name="notice_placed_video_call">%s comenzó una llamada de video.</string> - <string name="notice_placed_voice_call">%s comenzó una llamada de voz.</string> - <string name="notice_answered_call">%s recibió la llamada.</string> - <string name="notice_ended_call">%s terminó la llamada.</string> - <string name="notice_made_future_room_visibility">%1$s dejó que %2$s vea el historial del futuro</string> - <string name="notice_room_visibility_invited">todos los miembros de la sala, desde su invitación.</string> - <string name="notice_room_visibility_joined">todos los miembros de la sala, desde cuando entraron.</string> - <string name="notice_room_visibility_shared">todos los miembros de la sala.</string> - <string name="notice_room_visibility_world_readable">todos.</string> - <string name="notice_room_visibility_unknown">desconocido (%s).</string> - <string name="notice_end_to_end">%1$s encendió el cifrado de extremo a extremo (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s solicitó una conferencia VoIP</string> - <string name="notice_voip_started">conferencia VoIP comenzó</string> - <string name="notice_voip_finished">conferencia VoIP finalizó</string> - - <string name="notice_avatar_changed_too">(foto de perfil también se cambió)</string> - <string name="notice_room_name_removed">%1$s eliminó el nombre de la sala</string> - <string name="notice_room_topic_removed">%1$s retiró el tema de la sala</string> - <string name="notice_profile_change_redacted">%1$s actualizó su perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s envió una invitación a %2$s para entrar a la sala</string> - <string name="notice_room_third_party_registered_invite">%1$s aceptó la invitación de %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** No se puede descifrar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">El dispositivo del remitente no nos ha enviado las claves de este mensaje.</string> - - <!-- Room Screen --> - <string name="could_not_redact">No se pudo redactar</string> - <string name="unable_to_send_message">No se puede enviar el mensaje</string> - - <string name="message_failed_to_upload">La subida de la imagen falló</string> - - <!-- general errors --> - <string name="network_error">Error de la red</string> - <string name="matrix_error">Error de Matrix</string> - - <!-- Home Screen --> - - <!-- Last seen time --> - - <!-- call events --> - - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">No es posible volver a unirse a una sala vacÃa.</string> - - <string name="encrypted_message">Mensaje cifrado</string> - - <!-- medium friendly name --> - <string name="medium_email">Correo electrónico</string> - <string name="medium_phone_number">Número telefónico</string> - - <string name="summary_user_sent_sticker">%1$s envió una calcomanÃa.</string> - - <!-- Room display name --> - <string name="room_displayname_invite_from">Invitación de %s</string> - <string name="room_displayname_room_invite">Invitación de Sala</string> - <string name="room_displayname_two_members">%1$s y %2$s</string> - <string name="room_displayname_empty_room">Sala vacÃa</string> - - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s y otro</item> - <item quantity="other">%1$s y %2$d otros</item> - </plurals> - - <string name="notice_event_redacted">Mensaje eliminado</string> - <string name="notice_event_redacted_by">Mensaje eliminado por %1$s</string> - <string name="notice_event_redacted_with_reason">Mensaje eliminado [motivo: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Mensaje eliminado por %1$s [motivo: %2$s]</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-es/strings.xml b/matrix-sdk-android/src/main/res/values-es/strings.xml deleted file mode 100644 index 3648ca3a72d8f7b396dfdc7e93ee01b06080e59a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-es/strings.xml +++ /dev/null @@ -1,224 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s envió una imagen.</string> - <string name="notice_room_invite_no_invitee">la invitación de %s</string> - <string name="notice_room_invite">%1$s invitó a %2$s</string> - <string name="notice_room_invite_you">%1$s te ha invitado</string> - <string name="notice_room_join">%1$s se ha unido</string> - <string name="notice_room_leave">%1$s salió</string> - <string name="notice_room_reject">%1$s rechazó la invitación</string> - <string name="notice_room_kick">%1$s expulsó a %2$s</string> - <string name="notice_room_unban">%1$s le quitó el veto a %2$s</string> - <string name="notice_room_ban">%1$s vetó a %2$s</string> - <string name="notice_room_withdraw">%1$s retiró la invitación de %2$s</string> - <string name="notice_avatar_url_changed">%1$s cambió su avatar</string> - <string name="notice_display_name_set">%1$s estableció %2$s como su nombre público</string> - <string name="notice_display_name_changed_from">%1$s cambió su nombre público de %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s eliminó su nombre público (%2$s)</string> - <string name="notice_room_topic_changed">%1$s cambió el tema a: %2$s</string> - <string name="notice_room_name_changed">%1$s cambió el nombre de la sala a: %2$s</string> - <string name="notice_placed_video_call">%s realizó una llamada de vÃdeo.</string> - <string name="notice_placed_voice_call">%s realizó una llamada de voz.</string> - <string name="notice_answered_call">%s contestó la llamada.</string> - <string name="notice_ended_call">%s finalizó la llamada.</string> - <string name="notice_made_future_room_visibility">%1$s hizo visible el historial futuro de la sala para %2$s</string> - <string name="notice_room_visibility_invited">todos los miembros de la sala, desde su invitación.</string> - <string name="notice_room_visibility_joined">todos los miembros de la sala, desde el momento en que se unieron.</string> - <string name="notice_room_visibility_shared">todos los miembros de la sala.</string> - <string name="notice_room_visibility_world_readable">todos.</string> - <string name="notice_room_visibility_unknown">desconocido (%s).</string> - <string name="notice_end_to_end">%1$s ha activado la encriptación de Extremo-a-Extremo (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s solicitó una conferencia de vozIP</string> - <string name="notice_voip_started">conferencia de vozIP iniciada</string> - <string name="notice_voip_finished">conferencia de vozIP finalizada</string> - <string name="notice_avatar_changed_too">(el avatar también se cambió)</string> - <string name="notice_room_name_removed">%1$s eliminó el nombre de la sala</string> - <string name="notice_room_topic_removed">%1$s eliminó el tema de la sala</string> - <string name="notice_profile_change_redacted">%1$s actualizó su perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s invitó a %2$s a unirse a la sala</string> - <string name="notice_room_third_party_registered_invite">%1$s aceptó la invitación para %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** No es posible descifrar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">El dispositivo emisor no nos ha enviado las claves para este mensaje.</string> - <!-- Room Screen --> - <string name="could_not_redact">No se pudo redactar</string> - <string name="unable_to_send_message">No es posible enviar el mensaje</string> - <string name="message_failed_to_upload">No se pudo cargar la imagen</string> - <!-- general errors --> - <string name="network_error">Error de red</string> - <string name="matrix_error">Error de Matrix</string> - <!-- Home Screen --> - <!-- Last seen time --> - <!-- call events --> - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Actualmente no es posible volver a unirse a una sala vacÃa.</string> - <string name="encrypted_message">Mensaje encriptado</string> - <!-- medium friendly name --> - <string name="medium_email">Dirección de correo electrónico</string> - <string name="medium_phone_number">Número telefónico</string> - <string name="summary_user_sent_sticker">%1$s envió una pegatina.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Invitación de %s</string> - <string name="room_displayname_room_invite">Invitación a Sala</string> - <string name="room_displayname_two_members">%1$s y %2$s</string> - <string name="room_displayname_empty_room">Sala vacÃa</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s y 1 otro</item> - <item quantity="other">%1$s y %2$d otros</item> - </plurals> - <string name="notice_event_redacted">Mensaje eliminado</string> - <string name="notice_event_redacted_by">Mensaje eliminado por %1$s</string> - <string name="notice_event_redacted_with_reason">Mensaje eliminado [motivo: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Mensaje eliminado por %1$s [motivo: %2$s]</string> - <string name="notice_room_third_party_revoked_invite">%1$s ha revocado la invitación a unirse a la sala para %2$s</string> - <string name="initial_sync_start_importing_account">Sincronización Inicial -\nImportando cuenta…</string> - <string name="initial_sync_start_importing_account_rooms">Sincronización Inicial: -\nImportando Salas</string> - <string name="initial_sync_start_importing_account_groups">Sincronización Inicial: -\nImportando Comunidades</string> - <string name="initial_sync_start_importing_account_data">Sincronización Inicial: -\nImportando Datos de la Cuenta</string> - <string name="event_status_sending_message">Enviando mensaje…</string> - <string name="clear_timeline_send_queue">Borrar cola de envÃo</string> - <string name="notice_room_invite_with_reason">%1$s ha invitado a %2$s. Razón: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s te ha invitado. Razón: %2$s</string> - <string name="notice_room_join_with_reason">%1$s se ha unido. Razón: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s se ha ido. Razón: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s ha rechadazo la invitación. Razón: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s expulsó a %2$s. Razón: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s ha baneado a %2$s. Razón: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s ha aceptado la invitación para %2$s. Razón: %3$s</string> - <string name="notice_room_canonical_alias_unset">%1$s ha eliminado la dirección principal para esta sala.</string> - <string name="notice_room_update">%s ha actualizado la sala.</string> - <string name="initial_sync_start_importing_account_crypto">Sincronización Inicial: -\nImportando criptografÃa</string> - <string name="initial_sync_start_importing_account_joined_rooms">Sincronización Inicial: -\nImportando Salas a las que te has unido</string> - <string name="initial_sync_start_importing_account_invited_rooms">Sincronización Inicial: -\nImportando Salas a las que has sido invitada</string> - <string name="initial_sync_start_importing_account_left_rooms">Sincronización Inicial: -\nImportando Salas Abandonadas</string> - <string name="notice_room_invite_no_invitee_with_reason">Invitación de %1$s. Razón: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s ha desbaneado a %2$s. Razón: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s envió una invitación a %2$s para que se una a la sala. Razón: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s revocó la invitación de %2$s para unirse a la sala. Razón: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s ha retirado la invitación de %2$s. Razón: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s ha añadido %2$s como alias de esta sala.</item> - <item quantity="other">%1$s ha añadido %2$s como alias de esta sala.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s ha quitado %2$s como alias de esta sala.</item> - <item quantity="other">%1$s ha quitado %2$s como alias de esta sala.</item> - </plurals> - <string name="notice_room_canonical_alias_set">%1$s ha establecido la dirección principal de esta sala a %2$s.</string> - <string name="notice_room_guest_access_can_join">%1$s ha permitido que los invitados se unan a la sala.</string> - <string name="notice_room_guest_access_forbidden">%1$s ha impedido que los invitados se unan a la sala.</string> - <string name="notice_end_to_end_ok">%1$s ha activado la encriptación extremo a extremo.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s ha activado la encriptación de extremo a extremo (algoritmo no reconocido %2$s).</string> - <string name="key_verification_request_fallback_message">%s solicita verificar su clave, pero su cliente no soporta la verificación de la clave en chat. Necesitará usar la verificación de claves clásica para poder verificar las claves.</string> - <string name="summary_you_sent_image">Enviaste una imagen.</string> - <string name="summary_you_sent_sticker">Enviaste un sticker.</string> - <string name="notice_room_invite_no_invitee_by_you">Tu invitación</string> - <string name="notice_room_created">%1$s creó la sala</string> - <string name="notice_room_created_by_you">Creaste la sala</string> - <string name="notice_room_invite_by_you">Invitaste a %1$s</string> - <string name="notice_room_join_by_you">Te uniste a la Sala</string> - <string name="notice_room_leave_by_you">Dejaste la Sala</string> - <string name="notice_room_reject_by_you">Rechazaste la invitación</string> - <string name="notice_room_kick_by_you">Tu pateaste a %1$s</string> - <string name="notice_room_unban_by_you">Tu desbanaste a %1$s</string> - <string name="notice_room_ban_by_you">Usted prohibió a %1$s</string> - <string name="notice_room_withdraw_by_you">Retiró la invitación de %1$s\'s</string> - <string name="notice_avatar_url_changed_by_you">Cambiaste tu avatar</string> - <string name="notice_display_name_set_by_you">Establece su nombre de visualización en %1$s</string> - <string name="notice_display_name_changed_from_by_you">Cambiaste tu nombre para mostrar de %1$s a %2$s</string> - <string name="notice_display_name_removed_by_you">Quitaste tu nombre para mostrar (era %1$s)</string> - <string name="notice_room_topic_changed_by_you">Cambiaste el tema a: %1$s</string> - <string name="notice_room_avatar_changed">%1$s cambió el avatar de la sala</string> - <string name="notice_room_avatar_changed_by_you">Cambiaste el avatar de la sala</string> - <string name="notice_room_name_changed_by_you">Cambiaste el nombre de la sala a: %1$s</string> - <string name="notice_placed_video_call_by_you">Hiciste una videollamada.</string> - <string name="notice_placed_voice_call_by_you">Hiciste una llamada de voz.</string> - <string name="notice_call_candidates">%s envió datos para configurar la llamada.</string> - <string name="notice_call_candidates_by_you">Enviaste datos para configurar la llamada.</string> - <string name="notice_answered_call_by_you">Respondiste la llamada.</string> - <string name="notice_ended_call_by_you">Terminaste la llamada.</string> - <string name="notice_made_future_room_visibility_by_you">Hiciste visible el futuro historial de la %1$s</string> - <string name="notice_end_to_end_by_you">Has activado la encriptación de Extremo-a-Extremo (%1$s)</string> - <string name="notice_room_update_by_you">Has actualizado esta sala.</string> - <string name="notice_requested_voip_conference_by_you">Solicitaste una conferencia de VoIP</string> - <string name="notice_room_name_removed_by_you">Quitaste el nombre de la sala</string> - <string name="notice_room_topic_removed_by_you">Quitaste el tema de la sala</string> - <string name="notice_room_avatar_removed">%1$s eliminó el avatar de la sala</string> - <string name="notice_room_avatar_removed_by_you">Quitaste el avatar de la sala</string> - <string name="notice_profile_change_redacted_by_you">Actualizaste tu perfil %1$s</string> - <string name="notice_room_third_party_invite_by_you">Enviaste una invitación a %1$s para unirse a la sala</string> - <string name="notice_room_third_party_revoked_invite_by_you">Revocaste la invitación para que %1$s se una a la sala</string> - <string name="notice_room_third_party_registered_invite_by_you">Aceptaste la invitación para %1$s</string> - <string name="notice_widget_added">%1$s agrego el widget %2$s</string> - <string name="notice_widget_added_by_you">Agregaste el widget %1$s</string> - <string name="notice_widget_removed">%1$s eliminó el widget %2$s</string> - <string name="notice_widget_removed_by_you">Quitaste el widget %1$s</string> - <string name="notice_widget_modified">%1$s modifico el widget %2$s</string> - <string name="notice_widget_modified_by_you">Modificaste el widget %1$s</string> - <string name="power_level_admin">Administrador</string> - <string name="power_level_moderator">Moderador</string> - <string name="power_level_default">Por defecto</string> - <string name="power_level_custom">Personalizado (%1$d)</string> - <string name="power_level_custom_no_value">Personalizado</string> - <string name="notice_power_level_changed_by_you">Cambiaste el nivel de potencia de %1$s.</string> - <string name="notice_power_level_changed">%1$s cambió el nivel de potencia de %2$s.</string> - <string name="notice_power_level_diff">%1$s de %2$s a %3$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Tu invitación. Razón: %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Invitaste a %1$s. Razón: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Te uniste a la sala. Razón: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Dejaste la sala. Razón: %1$s</string> - <string name="notice_room_reject_with_reason_by_you">Rechazaste la invitación. Razón: %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Pateaste a %1$s. Motivo: %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Has desactivado a %1$s. Motivo: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Prohibiste a %1$s. Motivo: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Enviaste una invitación a %1$s para unirse a la sala. Motivo: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Revocaste la invitación para que %1$s se una a la sala. Motivo: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Aceptaste la invitación para %1$s. Motivo: %2$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Retiró la invitación de %1$s\'s. Motivo: %2$s</string> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Agregaste %1$s como dirección para esta sala.</item> - <item quantity="other">Agregaste %1$s como direcciones para esta sala.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Quitaste %1$s como dirección para esta sala.</item> - <item quantity="other">Quitaste %1$s como direcciones para esta sala.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s añadió %2$s y eliminó %3$s como alias para esta sala.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Agregaste %1$s y quitaste %2$s como direcciones para esta sala.</string> - <string name="notice_room_canonical_alias_set_by_you">Estableciste la dirección principal de esta sala en %1$s.</string> - <string name="notice_room_canonical_alias_unset_by_you">Quitaste la dirección principal de esta sala.</string> - <string name="notice_room_guest_access_can_join_by_you">Ha permitido que los invitados se unan a la sala.</string> - <string name="notice_room_guest_access_forbidden_by_you">Ha impedido que los invitados se unan a la sala.</string> - <string name="notice_end_to_end_ok_by_you">Tu has activado la encriptación de Extremo-a-Extremo.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Has activado la encriptación de Extremo-a-Extremo (algoritmo %1$s no reconocido).</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Has impedido que invitados se unan a la sala.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Has permitido a invitados unirse aquÃ.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Te has ido. Razón: %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Has revocado la invitación de %1$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Has invitado a %1$s</string> - <string name="notice_direct_room_update_by_you">Has actualizado aquÃ.</string> - <string name="notice_made_future_direct_room_visibility_by_you">Has hecho futuros mensajes visibles a %1$s</string> - <string name="notice_direct_room_leave_by_you">Te saliste de la sala</string> - <string name="notice_direct_room_join_by_you">Te uniste</string> - <string name="notice_direct_room_created_by_you">Creaste la conversación</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s ha impedido que invitados se unan a la sala.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s ha permitido a invitados a unirse aquÃ.</string> - <string name="notice_direct_room_leave_with_reason">%1$s se ha ido. Razón: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Tu te has unido. Razón: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s se ha unido. Razón: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s ha revocado la invitación de %2$s</string> - <string name="notice_direct_room_third_party_invite">%1$s ha invitado %2$s</string> - <string name="notice_direct_room_update">%s ha actualizado aquÃ.</string> - <string name="notice_made_future_direct_room_visibility">%1$s ha hecho futuros mensajes visibles a %2$s</string> - <string name="notice_direct_room_leave">%1$s ha salido de la sala</string> - <string name="notice_direct_room_join">%1$s se ha unido</string> - <string name="notice_direct_room_created">%1$s ha creado la conversación</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-es/strings_sas.xml b/matrix-sdk-android/src/main/res/values-es/strings_sas.xml index fd396c1778a787d1808e7ee99ca7870a0da03196..b5f062cb6288e85f958d7723f7fe404f67b42884 100644 --- a/matrix-sdk-android/src/main/res/values-es/strings_sas.xml +++ b/matrix-sdk-android/src/main/res/values-es/strings_sas.xml @@ -36,18 +36,33 @@ <string name="verification_emoji_hat">Sombrero</string> <string name="verification_emoji_glasses">Gafas</string> <string name="verification_emoji_spanner">Llave inglesa</string> + <string name="verification_emoji_santa">Papá Noel</string> + <string name="verification_emoji_thumbs_up">Pulgar arriba</string> + <string name="verification_emoji_umbrella">Paraguas</string> + <string name="verification_emoji_hourglass">Reloj de arena</string> <string name="verification_emoji_clock">Reloj</string> <string name="verification_emoji_gift">Regalo</string> + <string name="verification_emoji_light_bulb">Bombilla</string> <string name="verification_emoji_book">Libro</string> <string name="verification_emoji_pencil">Lápiz</string> + <string name="verification_emoji_paperclip">Clip</string> + <string name="verification_emoji_scissors">Tijeras</string> + <string name="verification_emoji_lock">Candado</string> <string name="verification_emoji_key">Llave</string> <string name="verification_emoji_hammer">Martillo</string> <string name="verification_emoji_telephone">Telefono</string> + <string name="verification_emoji_flag">Bandera</string> <string name="verification_emoji_train">Tren</string> <string name="verification_emoji_bicycle">Bicicleta</string> + <string name="verification_emoji_aeroplane">Avión</string> + <string name="verification_emoji_rocket">Cohete</string> + <string name="verification_emoji_trophy">Trofeo</string> <string name="verification_emoji_ball">Bola</string> <string name="verification_emoji_guitar">Guitarra</string> <string name="verification_emoji_trumpet">Trompeta</string> <string name="verification_emoji_bell">Campana</string> + <string name="verification_emoji_anchor">Ancla</string> + <string name="verification_emoji_headphones">Cascos</string> + <string name="verification_emoji_folder">Carpeta</string> <string name="verification_emoji_pin">Alfiler</string> </resources> diff --git a/matrix-sdk-android/src/main/res/values-et/strings.xml b/matrix-sdk-android/src/main/res/values-et/strings.xml deleted file mode 100644 index af2cc33b99c9e41952f1d92a6e5ae821413717f8..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-et/strings.xml +++ /dev/null @@ -1,261 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s saatis pildi.</string> - <string name="summary_user_sent_sticker">%1$s saatis kleepsu.</string> - <string name="notice_room_invite_no_invitee">Kasutaja %s kutse</string> - <string name="notice_room_invite">%1$s kutsus kasutajat %2$s</string> - <string name="notice_room_invite_you">%1$s kutsus sind</string> - <string name="notice_room_join">%1$s liitus jututoaga</string> - <string name="notice_room_leave">%1$s lahkus jututoast</string> - <string name="notice_room_reject">%1$s lükkas tagasi kutse</string> - <string name="notice_room_kick">%1$s müksas kasutajat %2$s</string> - <string name="notice_room_withdraw">%1$s võttis tagasi kutse kasutajale %2$s</string> - <string name="notice_avatar_url_changed">%1$s muutis oma avatari</string> - <string name="notice_display_name_set">%1$s määras oma kuvatavaks nimeks %2$s</string> - <string name="notice_display_name_changed_from">%1$s muutis senise kuvatava nime %2$s uueks nimeks %3$s</string> - <string name="notice_display_name_removed">%1$s eemaldas oma kuvatava nime (%2$s)</string> - <string name="notice_room_topic_changed">%1$s muutis uueks teemaks %2$s</string> - <string name="notice_room_name_changed">%1$s muutis jututoa uueks nimeks %2$s</string> - <string name="notice_placed_video_call">%s alustas videokõnet.</string> - <string name="notice_placed_voice_call">%s alustas häälkõnet.</string> - <string name="notice_answered_call">%s vastas kõnele.</string> - <string name="notice_ended_call">%s lõpetas kõne.</string> - <string name="notice_made_future_room_visibility">%1$s seadistas, et tulevane jututoa ajalugu on nähtav kasutajale %2$s</string> - <string name="notice_room_visibility_invited">kõikidele jututoa liikmetele alates kutsumise hetkest.</string> - <string name="notice_room_visibility_joined">kõikidele jututoa liikmetele alates liitumise hetkest.</string> - <string name="notice_room_visibility_shared">kõikidele jututoa liikmetele.</string> - <string name="notice_room_visibility_world_readable">kõikidele.</string> - <string name="notice_room_visibility_unknown">teadmata (%s).</string> - <string name="notice_end_to_end">%1$s lülitas sisse läbiva krüptimise (%2$s)</string> - <string name="notice_room_update">%s uuendas seda jututuba.</string> - <string name="notice_requested_voip_conference">%1$s saatis VoIP konverentsi kutse</string> - <string name="notice_voip_started">VoIP-konverents algas</string> - <string name="notice_voip_finished">VoIP-konverents lõppes</string> - <string name="notice_avatar_changed_too">(samuti sai avatar muudetud)</string> - <string name="notice_room_name_removed">%1$s eemaldas jututoa nime</string> - <string name="notice_room_topic_removed">%1$s eemaldas jututoa teema</string> - <string name="notice_event_redacted">Sõnum on eemaldatud</string> - <string name="notice_event_redacted_by">Sõnum on eemaldatud %1$s poolt</string> - <string name="notice_event_redacted_with_reason">Sõnum on eemaldatud [põhjus: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Sõnum on eemaldatud %1$s poolt [põhjus: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s uuendas oma profiili %2$s</string> - <string name="notice_room_third_party_invite">%1$s saatis jututoaga liitumiseks kutse kasutajale %2$s</string> - <string name="notice_room_third_party_revoked_invite">%1$s võttis tagasi jututoaga liitumise kutse kasutajalt %2$s</string> - <string name="notice_room_third_party_registered_invite">%1$s võttis vastu kutse %2$s nimel</string> - <string name="notice_crypto_unable_to_decrypt">** Ei õnnestu dekrüptida: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Sõnumi saatja seade ei ole selle sõnumi jaoks saatnud dekrüptimisvõtmeid.</string> - <string name="could_not_redact">Ei saanud muuta sõnumit</string> - <string name="unable_to_send_message">Sõnumi saatmine ei õnnestunud</string> - <string name="message_failed_to_upload">Pildi üleslaadimine ei õnnestunud</string> - <string name="network_error">Võrguühenduse viga</string> - <string name="matrix_error">Matrix\'i viga</string> - <string name="room_error_join_failed_empty_room">Hetkel ei ole võimalik uuesti liituda tühja jututoaga.</string> - <string name="encrypted_message">Krüptitud sõnum</string> - <string name="medium_email">E-posti aadress</string> - <string name="medium_phone_number">Telefoninumber</string> - <string name="room_displayname_invite_from">Kutse kasutajalt %s</string> - <string name="room_displayname_room_invite">Kutse jututuppa</string> - <string name="room_displayname_two_members">%1$s ja %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s ja üks muu</item> - <item quantity="other">%1$s ja %2$d muud</item> - </plurals> - <string name="room_displayname_empty_room">Tühi jututuba</string> - <string name="initial_sync_start_importing_account">Alglaadimine: -\nImpordin kontot…</string> - <string name="initial_sync_start_importing_account_crypto">Alglaadimine: -\nImpordin krüptoseadistusi</string> - <string name="initial_sync_start_importing_account_rooms">Alglaadimine: -\nImpordin jututubasid</string> - <string name="initial_sync_start_importing_account_joined_rooms">Alglaadimine: -\nImpordin liitutud jututubasid</string> - <string name="initial_sync_start_importing_account_invited_rooms">Alglaadimine: -\nImpordin kutsutud jututubasid</string> - <string name="initial_sync_start_importing_account_left_rooms">Alglaadimine: -\nImpordin lahkutud jututubasid</string> - <string name="initial_sync_start_importing_account_groups">Alglaadimine: -\nImpordin kogukondi</string> - <string name="initial_sync_start_importing_account_data">Alglaadimine: -\nImpordin kontoandmeid</string> - <string name="event_status_sending_message">Saadan sõnumit…</string> - <string name="clear_timeline_send_queue">Tühjenda saatmisjärjekord</string> - <string name="notice_room_invite_no_invitee_with_reason">Kasutaja %1$s kutse. Põhjus: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s kutsus kasutajat %2$s. Põhjus: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s kutsus sind. Põhjus: %2$s</string> - <string name="notice_room_join_with_reason">%1$s liitus jututoaga. Põhjus: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s lahkus jututoast. Põhjus: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s lükkas kutse tagasi. Põhjus: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s müksas välja kasutaja %2$s. Põhjus: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s saatis kasutajale %2$s kutse jututoaga liitumiseks. Põhjus: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s tühistas kasutajale %2$s saadetud kutse jututoaga liitumiseks. Põhjus: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s võttis vastu kutse %2$s jututoaga liitumiseks. Põhjus: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s võttis tagasi kasutajale %2$s saadetud kutse. Põhjus: %3$s</string> - <string name="notice_end_to_end_ok">%1$s lülitas sisse läbiva krüptimise.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s lülitas sisse läbiva krüptimise (tundmatu algoritm %2$s).</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s lisas %2$s selle jututoa aadressiks.</item> - <item quantity="other">%1$s lisas %2$s selle jututoa aadressideks.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s eemaldas %2$s kui selle jututoa aadressi.</item> - <item quantity="other">%1$s eemaldas %2$s selle jututoa aadresside hulgast.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s lisas %2$s ja eemaldas %3$s selle jututoa aadresside loendist.</string> - <string name="notice_room_canonical_alias_set">%1$s seadistas selle jututoa põhiaadressiks %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s eemaldas selle jututoa põhiaadressi.</string> - <string name="notice_room_guest_access_can_join">%1$s lubas külalistel selle jututoaga liituda.</string> - <string name="notice_room_guest_access_forbidden">%1$s seadistas, et külalised ei või selle jututoaga liituda.</string> - <string name="key_verification_request_fallback_message">%s soovib verifitseerida sinu võtmeid, kuid sinu kasutatav klient ei oska vestluse-sisest verifitseerimist teha. Sa pead kasutama traditsioonilist verifitseerimislahendust.</string> - <string name="notice_room_created">Kasutaja %1$s lõi jututoa</string> - <string name="summary_you_sent_image">Sina saatsid pildi.</string> - <string name="summary_you_sent_sticker">Sina saatsid kleepsu.</string> - <string name="notice_room_invite_no_invitee_by_you">Sinu kutse</string> - <string name="notice_room_created_by_you">Sa lõid jututoa</string> - <string name="notice_room_invite_by_you">Sina kutsusid kasutajat %1$s</string> - <string name="notice_room_join_by_you">Sina liitusid jututoaga</string> - <string name="notice_room_leave_by_you">Sina lahkusid jututoast</string> - <string name="notice_room_reject_by_you">Sina lükkasid kutse tagasi</string> - <string name="notice_room_kick_by_you">Sina müksasid %1$s välja</string> - <string name="notice_room_unban">%1$s taastas %2$s ligipääsu</string> - <string name="notice_room_unban_by_you">Sina taastasid %1$s ligipääsu</string> - <string name="notice_room_ban">%1$s keelas %2$s ligipääsu</string> - <string name="notice_room_ban_by_you">Sina keelasid %1$s ligipääsu</string> - <string name="notice_room_withdraw_by_you">Sina võtsid tagasi %1$s kutse</string> - <string name="notice_avatar_url_changed_by_you">Sina muutsid oma tunnuspilti</string> - <string name="notice_display_name_set_by_you">Sina määrasid oma kuvatavaks nimeks %1$s</string> - <string name="notice_display_name_changed_from_by_you">Sina muutsid senise kuvatava nime %1$s uueks nimeks %2$s</string> - <string name="notice_display_name_removed_by_you">Sina eemaldasid oma kuvatava nime (oli %1$s)</string> - <string name="notice_room_topic_changed_by_you">Sina muutsid uueks teemaks %1$s</string> - <string name="notice_room_avatar_changed">%1$s muutis jututoa tunnuspilti</string> - <string name="notice_room_avatar_changed_by_you">Sina muutsid jututoa tunnuspilti</string> - <string name="notice_room_name_changed_by_you">Sina muutsid jututoa uueks nimeks %1$s</string> - <string name="notice_placed_video_call_by_you">Sa alustasid videokõnet.</string> - <string name="notice_placed_voice_call_by_you">Sa alustasid häälkõnet.</string> - <string name="notice_call_candidates">%s saatis info kõne algatamiseks.</string> - <string name="notice_call_candidates_by_you">Sa saatsid info kõne algatamiseks.</string> - <string name="notice_answered_call_by_you">Sa vastasid kõnele.</string> - <string name="notice_ended_call_by_you">Sa lõpetasid kõne.</string> - <string name="notice_made_future_room_visibility_by_you">Sa seadistasid, et tulevane jututoa ajalugu on nähtav kasutajale %1$s</string> - <string name="notice_end_to_end_by_you">Sa lülitasid sisse läbiva krüptimise (%1$s)</string> - <string name="notice_room_update_by_you">Sa uuendasid seda jututuba.</string> - <string name="notice_requested_voip_conference_by_you">Sa algatasid VoIP rühmakõne</string> - <string name="notice_room_name_removed_by_you">Sa eemaldasid jututoa nime</string> - <string name="notice_room_topic_removed_by_you">Sa eemaldasid jututoa teema</string> - <string name="notice_room_avatar_removed">%1$s eemaldas jututoa tunnuspildi</string> - <string name="notice_room_avatar_removed_by_you">Sa eemaldasid jututoa tunnuspildi</string> - <string name="notice_profile_change_redacted_by_you">Sa uuendasid oma profiili %1$s</string> - <string name="notice_room_third_party_invite_by_you">Sina saatsid kasutajale %1$s kutse jututoaga liitumiseks</string> - <string name="notice_room_third_party_revoked_invite_by_you">Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s</string> - <string name="notice_room_third_party_registered_invite_by_you">Sina võtsid vastu kutse %1$s nimel</string> - <string name="notice_widget_added">%1$s lisas %2$s vidina</string> - <string name="notice_widget_added_by_you">Sina lisasid %1$s vidina</string> - <string name="notice_widget_removed">%1$s eemaldas %2$s vidina</string> - <string name="notice_widget_removed_by_you">Sina eemdaldasid %1$s vidina</string> - <string name="notice_widget_modified">%1$s muutis %2$s vidinat</string> - <string name="notice_widget_modified_by_you">Sa muutsid %1$s vidinat</string> - <string name="power_level_admin">Peakasutaja</string> - <string name="power_level_moderator">Moderaator</string> - <string name="power_level_default">Tavakasutaja</string> - <string name="power_level_custom">Kohandatud kasutajaõigused (%1$d)</string> - <string name="power_level_custom_no_value">Kohandatud õigused</string> - <string name="notice_power_level_changed_by_you">Sina muutsid kasutaja %1$s õigusi.</string> - <string name="notice_power_level_changed">%1$s muutis kasutaja %2$s õigusi.</string> - <string name="notice_power_level_diff">%1$s õiguste muutus %2$s -> %3$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Sinu kutse. Põhjus %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Sina kutsusid kasutajat %1$s. Põhjus: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Sina liitusid jututoaga. Põhjus: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Sina lahkusid jututoast. Põhjus: %1$s</string> - <string name="notice_room_reject_with_reason_by_you">Sina lükkasid kutse tagasi. Põhjus: %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Sina müksasid kasutaja %1$s välja. Põhjus: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s taastas ligipääsu kasutajale %2$s. Põhjus: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">Sina taastasid kasutaja %1$s ligipääsu. Põhjus: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s keelas kasutaja %2$s ligipääsu. Põhjus: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">Sina keelasid kasutaja %1$s ligipääsu. Põhjus: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Sina saatsid kasutajale %1$s kutse jututoaga liitumiseks. Põhjus: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s. Põhjus: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Sina võtsid vastu kutse %1$s nimel. Põhjus: %2$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Sina võtsid tagasi kasutaja %1$s kutse. Põhjus: %2$s</string> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Sina lisasid %1$s selle jututoa aadressiks.</item> - <item quantity="other">Sina lisasid %1$s selle jututoa aadressideks.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Sina eemaldasid %1$s, kui selle jututoa aadressi.</item> - <item quantity="other">Sina eemaldasid %1$s selle jututoa aadresside hulgast.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed_by_you">Sina lisasid %1$s selle jututoa aadressiks ning eemaldasid %2$s aadresside hulgast.</string> - <string name="notice_room_canonical_alias_set_by_you">Sina seadistasid selle jututoa põhiaadressiks %1$s.</string> - <string name="notice_room_canonical_alias_unset_by_you">Sina eemaldasid selle jututoa põhiaadressi.</string> - <string name="notice_room_guest_access_can_join_by_you">Sina lubasid külalistel selle jututoaga liituda.</string> - <string name="notice_room_guest_access_forbidden_by_you">Sina seadistasid, et külalised ei või selle jututoaga liituda.</string> - <string name="notice_end_to_end_ok_by_you">Sa lülitasid sisse läbiva krüptimise.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Sa lülitasid sisse läbiva krüptimise (kasutusel on tundmatu algoritm %1$s).</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Sina seadistasid, et külalised ei või selle jututoaga liituda.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s seadistas, et külalised ei või selle jututoaga liituda.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Sina lubasid külalistel selle jututoaga liituda.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s lubas külalistel selle jututoaga liituda.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Sina lahkusid jututoast. Põhjus: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s lahkus jututoast. Põhjus: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Sina liitusid jututoaga. Põhjus: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s liitus jututoaga. Põhjus: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s võttis tagasi jututoaga liitumise kutse kasutajalt %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Sina kutsusid kasutajat %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s kutsus kasutajat %2$s</string> - <string name="notice_direct_room_update_by_you">Sa uuendasid seda jututuba.</string> - <string name="notice_direct_room_update">%s uuendas seda jututuba.</string> - <string name="notice_made_future_direct_room_visibility_by_you">Sina seadistasid, et tulevased jututoa sõnumid on nähtavad kasutajale %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s seadistas, et tulevased jututoa sõnumid on nähtavad kasutajale %2$s</string> - <string name="notice_direct_room_leave_by_you">Sina lahkusid jututoast</string> - <string name="notice_direct_room_leave">%1$s lahkus jututoast</string> - <string name="notice_direct_room_join_by_you">Sina liitusid</string> - <string name="notice_direct_room_join">%1$s liitus</string> - <string name="notice_direct_room_created_by_you">Sina alustasid vestlust</string> - <string name="notice_direct_room_created">%1$s alustas vestlust</string> - <string name="room_displayname_empty_room_was">Tühi jututuba (oli %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s ja %4$d muu</item> - <item quantity="other">%1$s, %2$s, %3$s ja %4$d muud</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s ja %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s ja %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Kõikide serverite osalemine on keelatud! Seda jututuba ei saa enam kasutada.</string> - <string name="notice_room_server_acl_updated_no_change">Muudatusi ei ole.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Nüüd on keelatud serverid, mille ip-aadress vastab mustrile.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Nüüd on lubatud serverid, mille ip-aadress vastab mustrile.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Server, mille nimes leidub %s, eemaldati lubatud serverite loendist.</string> - <string name="notice_room_server_acl_updated_allowed">• Nüüd on lubatud serverid, mille nimes leidub %s.</string> - <string name="notice_room_server_acl_updated_was_banned">• Server, mille nimes leidub %s eemaldati keeluloendist.</string> - <string name="notice_room_server_acl_updated_banned">• Keelatud on server, mille nimes leidub %s.</string> - <string name="notice_room_server_acl_updated_title_by_you">Sina muutsid selle jututoa jaoks serverite pääsuloendit.</string> - <string name="notice_room_server_acl_updated_title">%s muutis selle jututoa jaoks serverite pääsuloendit.</string> - <string name="notice_room_server_acl_set_title_by_you">Sina kirjeldasid selle jututoa jaoks serverite pääsuloendi.</string> - <string name="notice_room_server_acl_set_title">%s kirjeldas selle jututoa jaoks serverite pääsuloendi.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Keelatud on serverid, mille ip-aadress vastab mustrile.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Lubatud on serverid, mille ip-aadress vastab mustrile.</string> - <string name="notice_room_server_acl_set_allowed">• Lubatud on serverid, mille nimes leidub %s.</string> - <string name="notice_room_server_acl_set_banned">• Keelatud on serverid, mille nimes leidub %s.</string> - <string name="notice_room_canonical_alias_no_change">%1$s muutis selle jututoa aadresse.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Sa muutsid selle jututoa põhiaadressi ja täiendavaid aadresse.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s muutis selle jututoa täiendavaid aadresse.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Sa muutsid selle jututoa täiendavaid aadresse.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s muutis selle jututoa põhiaadressi ja täiendavaid aadresse.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Sa eemaldasid selle jututoa täiendava aadressi %1$s.</item> - <item quantity="other">Sa eemaldasid selle jututoa täiendavad aadressid %1$s.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s eemaldas selle jututoa täiendava aadressi %2$s.</item> - <item quantity="other">%1$s eemaldas selle jututoa täiendavad aadressid %2$s.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Sa lisasid sellele jututoale täiendava aadressi %1$s.</item> - <item quantity="other">Sa lisasid sellele jututoale täiendavad aadressid %1$s.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s lisas sellele jututoale täiendava aadressi %2$s.</item> - <item quantity="other">%1$s lisas sellele jututoale täiendavad aadressid %2$s.</item> - </plurals> - <string name="notice_room_canonical_alias_no_change_by_you">Sa muutsid selle jututoa aadresse.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-eu/strings.xml b/matrix-sdk-android/src/main/res/values-eu/strings.xml deleted file mode 100644 index bc61035c245c02e6fa65a6d1dae15941f56a63eb..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-eu/strings.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s erabiltzaileak irudi bat bidali du.</string> - - <string name="notice_room_invite_no_invitee">%s erabiltzailearen gonbidapena</string> - <string name="notice_room_invite">%1$s erabiltzaileak %2$s gonbidatu du</string> - <string name="notice_room_invite_you">%1$s erabiltzaileak gonbidatu zaitu</string> - <string name="notice_room_join">%1$s gelara elkartu da</string> - <string name="notice_room_leave">%1$s gelatik atera da</string> - <string name="notice_room_reject">%1$s erabiltzaileak gonbidapena baztertu du</string> - <string name="notice_room_kick">%1$s erabiltzaileak %2$s kanporatu du</string> - <string name="notice_room_unban">%1$s erabiltzaileak debekua kendu dio %2$s erabiltzaileari</string> - <string name="notice_room_ban">%1$s erabiltzaileak %2$s debekatu du</string> - <string name="notice_room_withdraw">%1$s erabiltzaileak %2$s erabiltzailearen gonbidapena atzera bota du</string> - <string name="notice_avatar_url_changed">%1$s erabiltzaileak abatarra aldatu du</string> - <string name="notice_display_name_set">%1$s erabiltzaileak bere pantaila-izena aldatu du beste honetara: %2$s</string> - <string name="notice_display_name_changed_from">%1$s erabiltzaileak bere pantaila-izena aldatu du, honetatik: %2$s honetara: %3$s</string> - <string name="notice_display_name_removed">%1$s erabiltzaileak bere pantaila-izena kendu du (%2$s)</string> - <string name="notice_room_topic_changed">%1$s erabiltzaileak mintzagaia honetara aldatu du: %2$s</string> - <string name="notice_room_name_changed">%1$s erabiltzaileak gelaren izena honetara aldatu du: %2$s</string> - <string name="notice_placed_video_call">%s erabiltzaileak bideo deia hasi du.</string> - <string name="notice_placed_voice_call">%s erabiltzaileak ahots deia hasi du.</string> - <string name="notice_answered_call">%s erabiltzaileak deia erantzun du.</string> - <string name="notice_ended_call">%s erabiltzaileak deia amaitu du.</string> - <string name="notice_made_future_room_visibility">%1$s erabiltzaileak gelaren historiala ikusgai jarri du hauentzat: %2$s</string> - <string name="notice_room_visibility_invited">gelako kide guztiak, gonbidatu zitzaienetik.</string> - <string name="notice_room_visibility_joined">gelako kide guztiak, elkartu zirenetik.</string> - <string name="notice_room_visibility_shared">gelako kide guztiak.</string> - <string name="notice_room_visibility_world_readable">edonor.</string> - <string name="notice_room_visibility_unknown">ezezaguna (%s).</string> - <string name="notice_end_to_end">%1$s erabiltzaileak muturretik muturrera zifratzea aktibatu du (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s erabiltzaileak VoIP konferentzia bat eskatu du</string> - <string name="notice_voip_started">VoIP konferentzia hasita</string> - <string name="notice_voip_finished">VoIP konferentzia amaituta</string> - - <string name="notice_avatar_changed_too">(abatarra ere aldatu da)</string> - <string name="notice_room_name_removed">%1$s erabiltzaileak gelaren izena kendu du</string> - <string name="notice_room_topic_removed">%1$s erabiltzaileak gelaren mintzagaia kendu du</string> - <string name="notice_profile_change_redacted">%1$s erabiltzaileak bere profila eguneratu du %2$s</string> - <string name="notice_room_third_party_invite">%1$s erabiltzaileak gelara elkartzeko gonbidapen bat bidali dio %2$s erabiltzaileari</string> - <string name="notice_room_third_party_registered_invite">%1$s erabiltzaileak %2$s gelarako gonbidapena onartu du</string> - - <string name="notice_crypto_unable_to_decrypt">** Ezin izan da deszifratu: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Igorlearen gailuak ez dizkigu mezu honetarako gakoak bidali.</string> - - <string name="could_not_redact">Ezin izan da kendu</string> - <string name="unable_to_send_message">Ezin izan da mezua bidali</string> - - <string name="message_failed_to_upload">Huts egin du irudia igotzean</string> - - <string name="network_error">Sare errorea</string> - <string name="matrix_error">Matrix errorea</string> - - <string name="room_error_join_failed_empty_room">Ezin da oraingoz hutsik dagoen gela batetara berriro sartu.</string> - - <string name="encrypted_message">Zifratutako mezua</string> - - <string name="medium_email">E-mail helbidea</string> - <string name="medium_phone_number">Telefono zenbakia</string> - - <string name="summary_user_sent_sticker">%1$s erabiltzaileak eranskailu bat bidali du.</string> - - <string name="room_displayname_invite_from">%s gelarako gonbidapena</string> - <string name="room_displayname_room_invite">Gela gonbidapena</string> - <string name="room_displayname_two_members">%1$s eta %2$s</string> - <string name="room_displayname_empty_room">Gela hutsa</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s eta beste bat</item> - <item quantity="other">%1$s eta beste %2$d</item> - </plurals> - - - <string name="notice_event_redacted">Mezua kendu da</string> - <string name="notice_event_redacted_by">%1$s erabiltzaileak mezua kendu du</string> - <string name="notice_event_redacted_with_reason">Mezua kendu da [arrazoia: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">%1$s erabiltzaileak mezua kendu du [arrazoia: %2$s]</string> - - <string name="initial_sync_start_importing_account">Hasierako sinkronizazioa: -\nKontua inportatzen…</string> - <string name="initial_sync_start_importing_account_crypto">Hasierako sinkronizazioa: -\nZifratzea inportatzen</string> - <string name="initial_sync_start_importing_account_rooms">Hasierako sinkronizazioa: -\nGelak inportatzen</string> - <string name="initial_sync_start_importing_account_joined_rooms">Hasierako sinkronizazioa: -\nElkartutako gelak inportatzen</string> - <string name="initial_sync_start_importing_account_invited_rooms">Hasierako sinkronizazioa: -\nGonbidatutako gelak inportatzen</string> - <string name="initial_sync_start_importing_account_left_rooms">Hasierako sinkronizazioa: -\nUtzitako gelak inportatzen</string> - <string name="initial_sync_start_importing_account_groups">Hasierako sinkronizazioa: -\nKomunitateak inportatzen</string> - <string name="initial_sync_start_importing_account_data">Hasierako sinkronizazioa: -\nKontuaren datuak inportatzen</string> - - <string name="notice_room_update">%s erabiltzaileak gela hau eguneratu du.</string> - - <string name="event_status_sending_message">Mezua bidaltzen…</string> - <string name="clear_timeline_send_queue">Garbitu bidalketa-ilara</string> - - <string name="notice_room_third_party_revoked_invite">%1$s erabiltzaileak %2$s gelara elkartzeko gonbidapena indargabetu du</string> - <string name="notice_room_invite_no_invitee_with_reason">%1$s erabiltzailearen gonbidapena. Arrazoia: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s erabiltzaileak %2$s gonbidatu du. Arrazoia: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s erabiltzaileak gonbidatu zaitu. Arrazoia: %2$s</string> - <string name="notice_room_join_with_reason">%1$s gelara elkartu da. Arrazoia: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s gelatik atera da. Arrazoia: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s erabiltzaileak gonbidapena baztertu du. Arrazoia: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s erabiltzaileak %2$s kanporatu du. Arrazoia: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s erabiltzaileak debekua kendu dio %2$s erabiltzaileari. Arrazoia: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s erabiltzaileak %2$s debekatu du. Arrazoia: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">"%1$s erabiltzaileak gelara elkartzeko gonbidapen bat bidali dio %2$s erabiltzaileari. Arrazoia: %3$s"</string> - <string name="notice_room_third_party_revoked_invite_with_reason">"%1$s erabiltzaileak %2$s gelara elkartzeko gonbidapena indargabetu du. Arrazoia: %3$s"</string> - <string name="notice_room_third_party_registered_invite_with_reason">"%1$s erabiltzaileak %2$s gelarako gonbidapena onartu du. Arrazoia: %3$s"</string> - <string name="notice_room_withdraw_with_reason">"%1$s erabiltzaileak %2$s erabiltzailearen gonbidapena indargabetu du. Arrazoia: %3$s"</string> - - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s erabiltzaileak %2$s gehitu du gela honen helbide gisa.</item> - <item quantity="other">%1$s erabiltzaileak %2$s gehitu ditu gela honen helbide gisa.</item> - </plurals> - - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s erabiltzaileak %2$s kendu du gela honen helbide gisa.</item> - <item quantity="other">%1$s erabiltzaileak %3$s kendu ditu gela honen helbide gisa.</item> - </plurals> - - <string name="notice_room_aliases_added_and_removed">%1$s erabiltzaileak %2$s gehitu %3$s eta kendu ditu gela honen helbide gisa.</string> - - <string name="notice_room_canonical_alias_set">%1$s erabiltzaileak %2$s ezarri du gela honen helbide nagusi gisa.</string> - <string name="notice_room_canonical_alias_unset">%1$s erabiltzaileak gela honen helbide nagusia kendu du.</string> - - <string name="notice_room_guest_access_can_join">%1$k gonbidatuak gelara sartzea onartu du.</string> - <string name="notice_room_guest_access_forbidden">%1%k gonbidatuak gelara sartzea galerazi du.</string> - - <string name="notice_end_to_end_ok">%1$s erabiltzaileak muturretik muturrerako zifratzea gaitu du.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s erabiltzaileak muturretik muturrerako zifratzea gaitu du. (%2$s algoritmo ezezaguna).</string> - - <string name="key_verification_request_fallback_message">%s(e)k zure gakoa egiaztatzea eskatu du, baina zure bezeroak ez du txatean gakoa egiaztatzea onartzen. Gako egiaztaketa zaharra erabili beharko duzu.</string> - - <string name="notice_room_created">%1$s erabiltzaileak gela sortu du</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-fa/strings.xml b/matrix-sdk-android/src/main/res/values-fa/strings.xml deleted file mode 100644 index 50446b9708c5d79de412300207fa5c720ba25dc0..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-fa/strings.xml +++ /dev/null @@ -1,222 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <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> - <string name="notice_answered_call">%s تماس را پاسخ داد.</string> - <string name="notice_ended_call">%s به تماس پایان داد.</string> - <string name="notice_made_future_room_visibility">%1$s تاریخچهٔ آیندهٔ اتاق را برای %2$s نمایان کرد</string> - <string name="notice_room_visibility_invited">همهٔ اعضای اتاق، از زمان دعوت شدنشان.</string> - <string name="notice_room_visibility_joined">همهٔ اعضای اتاق، از زمان پیوستنشان.</string> - <string name="notice_room_visibility_shared">همهٔ اعضای اتاق.</string> - <string name="notice_room_visibility_world_readable">هرکسی.</string> - <string name="notice_room_visibility_unknown">ناشناخته (%s).</string> - <string name="notice_end_to_end">%1$s رمزنگاری سرتاسری را روشن کرد (%2$s)</string> - <string name="notice_room_update">%s این اتاق را ارتقا داد.</string> - <string name="notice_requested_voip_conference">%1$s درخواست یک گردهمایی صوتی داد</string> - <string name="notice_voip_started">گردهمایی صوتی آغاز شد</string> - <string name="notice_voip_finished">گردهمایی صوتی پایان یاÙت</string> - <string name="notice_avatar_changed_too">(تصویر هم عوض شد)</string> - <string name="notice_room_name_removed">%1$s نام اتاق را پاک کرد</string> - <string name="notice_room_topic_removed">%1$s موضوع اتاق را پاک کرد</string> - <string name="notice_event_redacted">پیام برداشته شد</string> - <string name="notice_event_redacted_by">پیام به دست %1$s برداشته شد</string> - <string name="notice_event_redacted_with_reason">پیام برداشته شد [دلیل: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">پیام به دست %1$s برداشته شد [دلیل: %2$s]</string> - <string name="notice_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> - <string name="notice_crypto_unable_to_decrypt">** ناتوان در رمزگشایی: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">دستگاه Ùرستنده، کلیدهای این پیام را برایمان Ù†Ùرستاده است.</string> - <string name="unable_to_send_message">ناتوان در Ùرستادن پیام</string> - <string name="message_failed_to_upload">شکست در بارگذاری تصویر</string> - <string name="network_error">خطای شبکه</string> - <string name="matrix_error">خطای ماتریکس</string> - <string name="room_error_join_failed_empty_room">در Øال Øاضر امکان بازپیوست به اتاقی خالی وجود ندارد‌‌.</string> - <string name="encrypted_message">پیام رمزنگاری شده</string> - <string name="medium_email">نشانی رایانامه</string> - <string name="medium_phone_number">شماره تلÙÙ†</string> - <string name="room_displayname_invite_from">دعوت از %s</string> - <string name="room_displayname_room_invite">دعوت اتاق</string> - <string name="room_displayname_two_members">%1$s Ùˆ %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s Ùˆ Û± Ù†Ùر دیگر</item> - <item quantity="other">%1$s Ùˆ %2$d Ù†Ùر دیگر</item> - </plurals> - <string name="room_displayname_empty_room">اتاق خالی</string> - <string name="initial_sync_start_importing_account">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی Øساب…</string> - <string name="initial_sync_start_importing_account_crypto">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی رمزنگاری</string> - <string name="initial_sync_start_importing_account_rooms">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی اتاق‌ها</string> - <string name="initial_sync_start_importing_account_joined_rooms">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی اتاق‌های پیوسته</string> - <string name="initial_sync_start_importing_account_invited_rooms">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی اتاق‌های دعوت‌شده</string> - <string name="initial_sync_start_importing_account_left_rooms">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی اتاق‌های ترک‌شده</string> - <string name="initial_sync_start_importing_account_groups">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی انجمن‌ها</string> - <string name="initial_sync_start_importing_account_data">همگام‌سازی نخستین: -\nدر Øال درون‌ریزی داده‌های Øساب</string> - <string name="event_status_sending_message">در Øال Ùرستادن پیام…</string> - <string name="clear_timeline_send_queue">پاک‌سازی صÙ٠در Øال ارسال</string> - <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_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> - <string name="notice_room_ban_with_reason">%1$sØŒ %2$s را مسدود کرد. دلیل: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s دعوتی برای پیوستن %2$s به اتاق Ùرستاد. دلیل: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s دعوت %2$s برای پیوستن به اتاق را باطل کرد. دلیل: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s دعوت برای %2$s را پذیرÙت. دلیل: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s دعوت %2$s را نپذیرÙت. دلیل: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$sØŒ %2$s را به عنوان نشانی‌ای برای این اتاق اÙزود.</item> - <item quantity="other">%1$sØŒ %2$s را به عنوان نشانی‌هایی برای این اتاق اÙزود.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$sØŒ %2$s را به عنوان نشانی‌ای برای این اتاق پاک کرد.</item> - <item quantity="other">%1$sØŒ %3$s را به عنوان نشانی‌هایی برای این اتاق پاک کرد.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s برای نشانی این اتاق، %2$s را اÙزود Ùˆ %3$s را پاک کرد.</string> - <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> - <string name="notice_room_created">%1$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> - <string name="notice_direct_room_guest_access_forbidden_by_you">مهمان‌ها را از پیوستن به اتاق بازداشتید.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s مهمان‌ها را از پیوستن به اتاق بازداشت.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">به مهمان‌ها اجازه دادید به این‌جا بپیوندند.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s به مهمان‌ها اجازه داد به این‌جا بپیوندند.</string> - <string name="notice_direct_room_leave_with_reason_by_you">رÙتید. دلیل: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s رÙت. دلیل: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">پیوستید. دلیل: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$sپیوست. دلیل: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">دعوت %1$s را پس گرÙتید</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s دعوت %2$s را پس گرÙت</string> - <string name="notice_direct_room_third_party_invite_by_you">%1$s را دعوت کردید</string> - <string name="notice_direct_room_third_party_invite">%1$sØŒ %2$s را دعوت کرد</string> - <string name="notice_direct_room_update_by_you">این‌جا را ارتقا دادید.</string> - <string name="notice_direct_room_update">%s این‌جا را ارتقا داد.</string> - <string name="notice_made_future_direct_room_visibility_by_you">پیام‌های آینده را برای %1$s نمایان کردید</string> - <string name="notice_made_future_direct_room_visibility">%1$s پیام‌های آینده را برای %2$s نمایان کرد</string> - <string name="notice_direct_room_leave_by_you">اتاق را ترک کردید</string> - <string name="notice_direct_room_leave">%1$s اتاق را ترک کرد</string> - <string name="notice_direct_room_join_by_you">پیوستید</string> - <string name="notice_direct_room_join">%1$s پیوست</string> - <string name="notice_direct_room_created_by_you">Ú¯Ùت‌وگو را ایجاد کردید</string> - <string name="notice_direct_room_created">%1$s Ú¯Ùت‌وگو را ایجاد کرد</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$sØŒ %2$sØŒ %3$s Ùˆ %4$d Ù†Ùر دیگر</item> - <item quantity="other">%1$sØŒ %2$sØŒ %3$s Ùˆ %4$d Ù†Ùر دیگر</item> - </plurals> - <string name="room_displayname_3_members">%1$sØŒ %2$s Ùˆ %3$s</string> - <string name="room_displayname_4_members">%1$sØŒ %2$sØŒ %3$s Ùˆ %4$s</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-fi/strings.xml b/matrix-sdk-android/src/main/res/values-fi/strings.xml deleted file mode 100644 index 1e3788476fb13db1ad29d1a5e2d073754353829a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-fi/strings.xml +++ /dev/null @@ -1,213 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_user_sent_image">%1$s lähetti kuvan.</string> - <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 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> - <string name="notice_room_ban">%1$s antoi porttikiellon käyttäjälle %2$s</string> - <string name="notice_room_withdraw">%1$s veti takaisin kutsun käyttäjälle %2$s</string> - <string name="notice_avatar_url_changed">%1$s vaihtoi profiilikuvaansa</string> - <string name="notice_display_name_set">%1$s asetti näyttönimekseen %2$s</string> - <string name="notice_display_name_changed_from">%1$s muutti näyttönimensä nimestä %2$s nimeen %3$s</string> - <string name="notice_display_name_removed">%1$s poisti näyttönimensä (%2$s)</string> - <string name="notice_room_topic_changed">%1$s vaihtoi aiheeksi: %2$s</string> - <string name="notice_room_name_changed">%1$s vaihtoi huoneen nimeksi %2$s</string> - <string name="notice_placed_video_call">%s soitti videopuhelun.</string> - <string name="notice_placed_voice_call">%s soitti äänipuhelun.</string> - <string name="notice_answered_call">%s vastasi puheluun.</string> - <string name="notice_ended_call">%s lopetti puhelun.</string> - <string name="notice_made_future_room_visibility">%1$s muutti tulevan huonehistorian näkyväksi seuraaville: %2$s</string> - <string name="notice_room_visibility_invited">kaikki huoneen jäsenet, kutsumisestaan asti.</string> - <string name="notice_room_visibility_joined">kaikki huoneen jäsenet, liittymisestään asti.</string> - <string name="notice_room_visibility_shared">kaikki huoneen jäsenet.</string> - <string name="notice_room_visibility_world_readable">kaikki.</string> - <string name="notice_room_visibility_unknown">tuntematon (%s).</string> - <string name="notice_end_to_end">%1$s otti käyttöön osapuolten välisen salauksen (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s lähetti VoIP-konferenssipyynnön</string> - <string name="notice_voip_started">VoIP-konferenssi alkoi</string> - <string name="notice_voip_finished">VoIP-konferenssi päättyi</string> - <string name="notice_avatar_changed_too">(myös kuva vaihdettiin)</string> - <string name="notice_room_name_removed">%1$s poisti huoneen nimen</string> - <string name="notice_room_topic_removed">%1$s poisti huoneen aiheen</string> - <string name="notice_profile_change_redacted">%1$s päivitti profiilinsa %2$s</string> - <string name="notice_room_third_party_invite">%1$s lähetti liittymiskutsun huoneeseen käyttäjälle %2$s</string> - <string name="notice_room_third_party_registered_invite">%1$s hyväksyi kutsun käyttäjän %2$s puolesta</string> - <string name="notice_crypto_unable_to_decrypt">** Salauksen purku epäonnistui: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Lähettäjän laite ei ole lähettänyt avaimia tähän viestiin.</string> - <string name="unable_to_send_message">Viestin lähetys epäonnistui</string> - <string name="message_failed_to_upload">Kuvan lataaminen epäonnistui</string> - <string name="network_error">Verkkovirhe</string> - <string name="matrix_error">Matrix-virhe</string> - <string name="room_error_join_failed_empty_room">Tällä hetkellä ei ole mahdollista liittyä uudelleen tyhjään huoneeseen.</string> - <string name="encrypted_message">Salattu viesti</string> - <string name="medium_email">Sähköpostiosoite</string> - <string name="medium_phone_number">Puhelinnumero</string> - <string name="could_not_redact">Takaisinveto epäonnistui</string> - <string name="summary_message">%1$s: %2$s</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Kutsu käyttäjältä %s</string> - <!-- Grammar problem --> - <string name="room_displayname_room_invite">Huonekutsu</string> - <string name="room_displayname_two_members">%1$s ja %2$s</string> - <string name="room_displayname_empty_room">Tyhjä huone</string> - <string name="summary_user_sent_sticker">%1$s lähetti tarran.</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s ja yksi muu</item> - <item quantity="other">%1$s ja %2$d muuta</item> - </plurals> - <string name="notice_event_redacted">Viesti poistettu</string> - <string name="notice_event_redacted_by">%1$s poisti viestin</string> - <string name="notice_event_redacted_with_reason">Viesti poistettu [syy: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">%1$s poisti viestin [syy: %2$s]</string> - <string name="initial_sync_start_importing_account">Alkusynkronointi: -\nTuodaan tiliä…</string> - <string name="initial_sync_start_importing_account_crypto">Alkusynkronointi: -\nTuodaan kryptoa</string> - <string name="initial_sync_start_importing_account_rooms">Alkusynkronointi: -\nTuodaan huoneita</string> - <string name="initial_sync_start_importing_account_joined_rooms">Alkusynkronointi: -\nTuodaan liityttyjä huoneita</string> - <string name="initial_sync_start_importing_account_invited_rooms">Alkusynkronointi: -\nTuodaan kutsuttuja huoneita</string> - <string name="initial_sync_start_importing_account_left_rooms">Alkusynkronointi: -\nTuodaan poistuttuja huoneita</string> - <string name="initial_sync_start_importing_account_groups">Alkusynkronointi: -\nTuodaan yhteisöjä</string> - <string name="initial_sync_start_importing_account_data">Alkusynkronointi: -\nTuodaan tilin tietoja</string> - <string name="notice_room_update">%s päivitti tämän huoneen.</string> - <string name="event_status_sending_message">Lähetetään viestiä…</string> - <string name="clear_timeline_send_queue">Tyhjennä lähetysjono</string> - <string name="notice_room_third_party_revoked_invite">%1$s veti takaisin käyttäjän %2$s liittymiskutsun huoneeseen</string> - <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 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> - <string name="notice_room_ban_with_reason">%1$s antoi porttikiellon käyttäjälle %2$s. Syy: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s lähetti kutsun liittyä huoneeseen käyttäjälle %2$s. Syy: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s kumosi kutsun liittyä huoneeseen käyttäjälle %2$s. Syy: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s hyväksyi kutsun liityäkseen huoneeseen %2$s. Syy: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s veti takaisin käyttäjän %2$s kutsun. Syy: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s lisäsi tälle huoneelle osoitteen %2$s.</item> - <item quantity="other">%1$s lisäsi tälle huoneelle osoitteet %2$s.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s poisti tältä huoneelta osoitteen %2$s.</item> - <item quantity="other">%1$s poisti tältä huoneelta osoitteet %3$s.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s lisäsi tälle huoneelle osoitteen %2$s ja poisti osoitteen %3$s.</string> - <string name="notice_room_canonical_alias_set">%1$s asetti tämän huoneen pääosoitteeksi %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s poisti tämän huoneen pääosoitteen.</string> - <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> - <string name="key_verification_request_fallback_message">%s haluaa varmentaa salausavaimesi, mutta asiakasohjelmasi ei tue keskustelun aikana tapahtuvaa avainten varmennusta. Joudut käyttämään perinteistä varmennustapaa.</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Hyväksyit käyttäjän %1$s kutsun. Syy: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Peruutit kutsun liittyä huoneeseen käyttäjältä %1$s. Syy: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Lähetit kutsun liittyä huoneeseen käyttäjälle %1$s. Syy: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Estit käyttäjän %1$s. Syy: %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Peruutit eston %1$s. Syy: %2$s</string> - <string name="notice_room_kick_with_reason_by_you">Poistit käyttäjän %1$s. Syy: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Hylkäsit kutsun. Syy: %1$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">Lähdit. Syy: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s lähti. Syy: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Poistuit huoneesta. Syy: %1$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Liityit. Syy: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s liittyi. Syy: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Liityit ryhmään. Syy: %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Kutsuit %1$s. Syy: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Kutsusi. Syy: %1$s</string> - <string name="room_displayname_empty_room_was">Tyhjä huone (oli %s)</string> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s ja %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s ja %3$s</string> - <string name="power_level_custom_no_value">Mukautettu</string> - <string name="power_level_custom">Mukautettu (%1$d)</string> - <string name="power_level_default">Oletus</string> - <string name="power_level_moderator">Valvoja</string> - <string name="power_level_admin">Ylläpitäjä</string> - <string name="notice_widget_modified">%1$s muutti %2$s sovelmaa</string> - <string name="notice_widget_removed_by_you">Poistit %1$s sovelman</string> - <string name="notice_widget_removed">%1$s poisti %2$s sovelman</string> - <string name="notice_widget_added_by_you">Lisäsit %1$s sovelman</string> - <string name="notice_widget_added">%1$s lisäsi %2$s sovelman</string> - <string name="notice_widget_modified_by_you">Muutit %1$s sovelmaa</string> - <string name="notice_room_third_party_registered_invite_by_you">Hyväksyit kutsun henkilölle %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Peruutit kutsun henkilöltä %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s peruutti kutsun henkilöltä %2$s</string> - <string name="notice_room_third_party_revoked_invite_by_you">Peruutit henkilön %1$s kutsun liittyä ryhmään</string> - <string name="notice_direct_room_third_party_invite_by_you">Kutsuit %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s kutsui %2$s</string> - <string name="notice_room_third_party_invite_by_you">Lähetit henkilölle %1$s kutsun liittyä huoneeseen</string> - <string name="notice_profile_change_redacted_by_you">Päivitit profiilisi %1$s</string> - <string name="notice_room_avatar_removed_by_you">Poistit huoneen profiilikuvan</string> - <string name="notice_room_avatar_removed">%1$s poisti huoneen profiilikuvan</string> - <string name="notice_room_topic_removed_by_you">Poistit huoneen aiheen</string> - <string name="notice_room_name_removed_by_you">Poistit huoneen nimen</string> - <string name="notice_requested_voip_conference_by_you">Pyysit ryhmäpuhelua</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Kaikki palvelimet on estetty osallistumasta! Tätä huonetta ei voi enää käyttää.</string> - <string name="notice_room_server_acl_updated_no_change">Ei muutosta.</string> - <string name="notice_room_server_acl_updated_was_banned">• Palvelimet jotka %s poistettiin estolistalta.</string> - <string name="notice_room_server_acl_updated_banned">• Palvelimen haku %s on nyt kielletty.</string> - <string name="notice_room_server_acl_set_allowed">• Palvelimen haku %s on sallittu.</string> - <string name="notice_room_server_acl_set_banned">• Palvelimen haku %s on kielletty.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s on estänyt vieraita liittymästä huoneeseen.</string> - <string name="notice_room_guest_access_forbidden_by_you">Estit vieraita liittymästä huoneeseen.</string> - <string name="notice_room_guest_access_can_join_by_you">Annoit vieraille luvan liittyä huoneeseen.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Annoit vieraille luvan liittyä tänne.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s on antanut vieraille luvan liittyä tänne.</string> - <string name="notice_room_canonical_alias_unset_by_you">Poistit tämän huoneen pääosoitteen.</string> - <string name="notice_end_to_end_ok_by_you">Otit käyttöön päästä päähän -salauksen.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Olet estänyt vieraiden liittymisen huoneeseen.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Otit päästä päähän -salauksen käyttöön (tuntematon algoritmi %1$s).</string> - <string name="notice_direct_room_update_by_you">Päivitit tässä.</string> - <string name="notice_direct_room_update">%s päivitti täällä.</string> - <string name="notice_room_update_by_you">Päivitit tämän huoneen.</string> - <string name="notice_end_to_end_by_you">Otit päästä päähän -salauksen käyttöön (%1$s)</string> - <string name="notice_made_future_direct_room_visibility_by_you">Teit tulevista viesteistä näkyviä käyttäjälle %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s teki tulevista viesteistä näkyviä käyttäjälle %2$s</string> - <string name="notice_made_future_room_visibility_by_you">Teit tulevan huonehistorian näkyväksi %1$s</string> - <string name="notice_ended_call_by_you">Lopetit puhelun.</string> - <string name="notice_answered_call_by_you">Vastasit puheluun.</string> - <string name="notice_call_candidates_by_you">Lähetit tietoja puhelun valmistelemiseksi.</string> - <string name="notice_call_candidates">%s lähetti tietoja puhelun valmistelemiseksi.</string> - <string name="notice_placed_voice_call_by_you">Aloitit äänipuhelun.</string> - <string name="notice_placed_video_call_by_you">Aloitit videopuhelun.</string> - <string name="notice_room_name_changed_by_you">Vaihdoit huoneen nimeksi: %1$s</string> - <string name="notice_room_avatar_changed_by_you">Vaihdoit huoneen profiilikuvaa</string> - <string name="notice_room_avatar_changed">%1$s muutti huoneen profiilikuvaa</string> - <string name="notice_room_topic_changed_by_you">Vaihdoit aiheen: %1$s</string> - <string name="notice_display_name_removed_by_you">Poistit nimimerkkisi (se oli %1$s)</string> - <string name="notice_display_name_changed_from_by_you">Vaihdoit nimimerkkisi %1$s nimeen %2$s</string> - <string name="notice_display_name_set_by_you">Asetit nimimerkiksesi %1$s</string> - <string name="notice_room_invite_no_invitee_by_you">Kutsusi</string> - <string name="notice_avatar_url_changed_by_you">Vaihdoit profiilikuvaasi</string> - <string name="notice_room_withdraw_by_you">Peruutit %1$sn kutsun</string> - <string name="notice_room_ban_by_you">Estit %1$s</string> - <string name="notice_room_unban_by_you">Poistit eston %1$s</string> - <string name="notice_room_kick_by_you">Poistit %1$s</string> - <string name="notice_room_reject_by_you">Hylkäsit kutsun</string> - <string name="notice_direct_room_leave_by_you">Poistuit huoneesta</string> - <string name="notice_direct_room_leave">%1$s poistui huoneesta</string> - <string name="notice_room_leave_by_you">Poistuit huoneesta</string> - <string name="notice_direct_room_join_by_you">Liityit</string> - <string name="notice_direct_room_join">%1$s liittyi</string> - <string name="notice_room_join_by_you">Liityit huoneeseen</string> - <string name="notice_room_invite_by_you">Kutsuit %1$s</string> - <string name="notice_direct_room_created_by_you">Loit keskustelun</string> - <string name="notice_direct_room_created">%1$s loi keskustelun</string> - <string name="notice_room_created_by_you">Loit huoneen</string> - <string name="notice_room_created">%1$s loi huoneen</string> - <string name="summary_you_sent_sticker">Lähetit tarran.</string> - <string name="summary_you_sent_image">Lähetit kuvan.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-fr/strings.xml b/matrix-sdk-android/src/main/res/values-fr/strings.xml deleted file mode 100644 index f49c54a8ba9288a8f2abf3f649e1f79d8f4ea608..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-fr/strings.xml +++ /dev/null @@ -1,239 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s : %2$s</string> - <string name="summary_user_sent_image">%1$s a envoyé une image.</string> - <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 le salon</string> - <string name="notice_room_leave">%1$s est parti du salon</string> - <string name="notice_room_reject">%1$s a rejeté l’invitation</string> - <string name="notice_room_kick">%1$s a expulsé %2$s</string> - <string name="notice_room_unban">%1$s a révoqué l\'exclusion de %2$s</string> - <string name="notice_room_ban">%1$s a exclus %2$s</string> - <string name="notice_room_withdraw">%1$s a annulé l’invitation de %2$s</string> - <string name="notice_avatar_url_changed">%1$s a changé d’avatar</string> - <string name="notice_display_name_set">%1$s a modifié son nom affiché en %2$s</string> - <string name="notice_display_name_changed_from">%1$s a modifié son nom affiché de %2$s en %3$s</string> - <string name="notice_display_name_removed">%1$s a supprimé son nom affiché (précédemment %2$s)</string> - <string name="notice_room_topic_changed">%1$s a changé le sujet en : %2$s</string> - <string name="notice_room_name_changed">%1$s a changé le nom du salon en : %2$s</string> - <string name="notice_placed_video_call">%s a passé un appel vidéo.</string> - <string name="notice_placed_voice_call">%s a passé un appel vocal.</string> - <string name="notice_answered_call">%s a répondu à l’appel.</string> - <string name="notice_ended_call">%s a raccroché.</string> - <string name="notice_made_future_room_visibility">%1$s a rendu l’historique futur du salon visible pour %2$s</string> - <string name="notice_room_visibility_invited">tous les membres du salon, depuis qu’ils ont été invités.</string> - <string name="notice_room_visibility_joined">tous les membres du salon, depuis qu’ils l’ont rejoint.</string> - <string name="notice_room_visibility_shared">tous les membres du salon.</string> - <string name="notice_room_visibility_world_readable">n’importe qui.</string> - <string name="notice_room_visibility_unknown">inconnu (%s).</string> - <string name="notice_end_to_end">%1$s a activé le chiffrement de bout en bout (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s a demandé une téléconférence VoIP</string> - <string name="notice_voip_started">Téléconférence VoIP démarrée</string> - <string name="notice_voip_finished">Téléconférence VoIP terminée</string> - <string name="notice_avatar_changed_too">(l’avatar a aussi changé)</string> - <string name="notice_room_name_removed">%1$s a supprimé le nom du salon</string> - <string name="notice_room_topic_removed">%1$s a supprimé le sujet du salon</string> - <string name="notice_profile_change_redacted">%1$s a mis à jour son profil %2$s</string> - <string name="notice_room_third_party_invite">%1$s a envoyé une invitation à %2$s pour rejoindre le salon</string> - <string name="notice_room_third_party_registered_invite">%1$s a accepté l’invitation pour %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Déchiffrement impossible : %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">L’appareil de l’expéditeur ne nous a pas envoyé les clés pour ce message.</string> - <string name="could_not_redact">Effacement impossible</string> - <string name="unable_to_send_message">Envoi du message impossible</string> - <string name="message_failed_to_upload">L’envoi de l’image a échoué</string> - <string name="network_error">Erreur de réseau</string> - <string name="matrix_error">Erreur de Matrix</string> - <string name="room_error_join_failed_empty_room">Il est impossible pour le moment de revenir dans un salon vide.</string> - <string name="encrypted_message">Message chiffré</string> - <string name="medium_email">Adresse e-mail</string> - <string name="medium_phone_number">Numéro de téléphone</string> - <string name="summary_user_sent_sticker">%1$s a envoyé un sticker.</string> - <string name="room_displayname_invite_from">Invitation de %s</string> - <string name="room_displayname_room_invite">Invitation au salon</string> - <string name="room_displayname_empty_room">Salon vide</string> - <string name="room_displayname_two_members">%1$s et %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s et 1 autre</item> - <item quantity="other">%1$s et %2$d autres</item> - </plurals> - <string name="notice_event_redacted">Message supprimé</string> - <string name="notice_event_redacted_by">Message supprimé par %1$s</string> - <string name="notice_event_redacted_with_reason">Message supprimé [motif : %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Message supprimé par %1$s [motif : %2$s]</string> - <string name="initial_sync_start_importing_account">Synchronisation initiale : -\nImportation du compte…</string> - <string name="initial_sync_start_importing_account_crypto">Synchronisation initiale : -\nImportation de la cryptographie</string> - <string name="initial_sync_start_importing_account_rooms">Synchronisation initiale : -\nImportation des salons</string> - <string name="initial_sync_start_importing_account_joined_rooms">Synchronisation initiale : -\nImportation des salons que vous avez rejoints</string> - <string name="initial_sync_start_importing_account_invited_rooms">Synchronisation initiale : -\nImportation des salons où vous avez été invités</string> - <string name="initial_sync_start_importing_account_left_rooms">Synchronisation initiale : -\nImportation des salons que vous avez quittés</string> - <string name="initial_sync_start_importing_account_groups">Synchronisation initiale : -\nImportation des communautés</string> - <string name="initial_sync_start_importing_account_data">Synchronisation initiale : -\nImportation des données du compte</string> - <string name="notice_room_update">%s a mis à niveau ce salon.</string> - <string name="event_status_sending_message">Envoi du message…</string> - <string name="clear_timeline_send_queue">Vider la file d’envoi</string> - <string name="notice_room_third_party_revoked_invite">%1$s a révoqué l’invitation pour %2$s à rejoindre le salon</string> - <string name="notice_room_invite_no_invitee_with_reason">Invitation de %1$s. Raison : %2$s</string> - <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 du salon. Raison : %2$s</string> - <string name="notice_room_reject_with_reason">%1$s a refusé l’invitation. 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é l\'exclusion de %2$s. Raison : %3$s</string> - <string name="notice_room_ban_with_reason">%1$s a exclus %2$s. Raison : %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s a envoyé une invitation à %2$s pour rejoindre le salon. Raison : %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s a révoqué l’invitation de %2$s à rejoindre le salon. Raison : %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s a accepté l’invitation pour %2$s. Raison : %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s a annulé l’invitation de %2$s. Raison : %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s a ajouté %2$s comme adresse pour ce salon.</item> - <item quantity="other">%1$s a ajouté %2$s comme adresses pour ce salon.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s a supprimé %2$s comme adresse pour ce salon.</item> - <item quantity="other">%1$s a supprimé %3$s comme adresses pour ce salon.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s a ajouté %2$s et supprimé %3$s comme adresses pour ce salon.</string> - <string name="notice_room_canonical_alias_set">%1$s a défini %2$s comme adresse principale pour ce salon.</string> - <string name="notice_room_canonical_alias_unset">%1$s a supprimé l’adresse principale de ce salon.</string> - <string name="notice_room_guest_access_can_join">%1$s a autorisé les visiteurs à rejoindre le salon.</string> - <string name="notice_room_guest_access_forbidden">%1$s a empêché les visiteurs de rejoindre le salon.</string> - <string name="notice_end_to_end_ok">%1$s a activé le chiffrement de bout en bout.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s a activé le chiffrement de bout en bout (algorithme %2$s inconnu).</string> - <string name="key_verification_request_fallback_message">%s demande à vérifier votre clé, mais votre client ne supporte pas la vérification de clés dans les discussions. Vous devrez utiliser l’ancienne vérification de clés pour vérifier les clés.</string> - <string name="notice_room_created">%1$s a créé le salon</string> - <string name="notice_direct_room_update_by_you">Vous avez mis cet endroit à niveau.</string> - <string name="notice_direct_room_update">%s a mis cet endroit à niveau.</string> - <string name="notice_room_update_by_you">Vous avez mis à niveau ce salon.</string> - <string name="notice_room_kick_by_you">Vous avez expulsé %1$s</string> - <string name="notice_room_reject_by_you">Vous avez rejeté l\'invitation</string> - <string name="notice_direct_room_leave_by_you">Vous avez quitté le salon</string> - <string name="notice_direct_room_leave">%1$s a quitté le salon</string> - <string name="notice_room_leave_by_you">Vous avez quitté le salon</string> - <string name="notice_direct_room_join_by_you">Vous avez rejoint le salon</string> - <string name="notice_direct_room_join">%1$s a rejoint le salon</string> - <string name="notice_room_join_by_you">Vous avez rejoint le salon</string> - <string name="notice_room_invite_by_you">Vous avez invité %1$s</string> - <string name="notice_direct_room_created_by_you">Vous avez créé la discussion</string> - <string name="notice_direct_room_created">%1$s a créé la discussion</string> - <string name="notice_room_created_by_you">Vous avez créé le salon</string> - <string name="notice_room_invite_no_invitee_by_you">Votre invitation</string> - <string name="summary_you_sent_sticker">Vous avez envoyé un autocollant.</string> - <string name="summary_you_sent_image">Vous avez envoyé une image.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Vous avez activé le chiffrement de bout en bout (algorithme %1$s inconnu).</string> - <string name="notice_end_to_end_ok_by_you">Vous avez activé le chiffrement de bout en bout.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Vous avez empêché les visiteurs de rejoindre le salon.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s a empêché les visiteurs de rejoindre le salon.</string> - <string name="notice_room_guest_access_forbidden_by_you">Vous avez empêché les visiteurs de rejoindre le salon.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Vous avez autorisé les visiteurs à venir ici.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s a autorisé les visiteurs à venir ici.</string> - <string name="notice_room_guest_access_can_join_by_you">Vous avez autorisé les visiteurs à rejoindre le salon.</string> - <string name="notice_room_canonical_alias_unset_by_you">Vous avez supprimé l’adresse principale de ce salon.</string> - <string name="notice_room_canonical_alias_set_by_you">Vous avez défini %1$s comme adresse principale pour ce salon.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Vous avez ajouté %1$s et supprimé %2$s comme adresses pour ce salon.</string> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Vous avez supprimé %1$s comme adresse pour ce salon.</item> - <item quantity="other">Vous avez supprimé %1$s comme adresses pour ce salon.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Vous avez ajouté %1$s comme adresse pour ce salon.</item> - <item quantity="other">Vous avez ajouté %1$s comme adresses pour ce salon.</item> - </plurals> - <string name="notice_room_withdraw_with_reason_by_you">Vous avez annulé l’invitation de %1$s. Raison : %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Vous avez accepté l’invitation pour %1$s. Raison : %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Vous avez révoqué l’invitation de %1$s à rejoindre le salon. Raison : %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Vous avez envoyé une invitation à %1$s pour rejoindre le salon. Raison : %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Vous avez refusé l’invitation. Raison : %1$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">Vous êtes parti. Raison : %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s est parti. Raison : %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Vous êtes parti du salon. Raison : %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s rejoint. Raison : %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Vous avez rejoint. Raison : %1$s</string> - <string name="notice_room_join_with_reason_by_you">Vous avez rejoint le salon. Raison : %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Vous avez invité %1$s. Raison : %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Votre invitation. Raison %1$s</string> - <string name="notice_power_level_diff">%1$s de %2$s à %3$s</string> - <string name="notice_power_level_changed">%1$s a modifié le niveau de pouvoir de %2$s.</string> - <string name="notice_power_level_changed_by_you">Vous avez modifié le niveau de pouvoir de %1$s.</string> - <string name="power_level_custom_no_value">Personnalisé</string> - <string name="power_level_custom">Personnalisé (%1$d)</string> - <string name="power_level_default">Défaut</string> - <string name="power_level_moderator">Modérateur</string> - <string name="power_level_admin">Admin</string> - <string name="notice_widget_modified_by_you">Vous avez modifié le widget %1$s</string> - <string name="notice_widget_modified">%1$s a modifié le widget %2$s</string> - <string name="notice_widget_removed_by_you">Vous avez supprimé le widget %1$s</string> - <string name="notice_widget_removed">%1$s a supprimé le widget %2$s</string> - <string name="notice_widget_added_by_you">Vous avez ajouté le widget %1$s</string> - <string name="notice_widget_added">%1$s a ajouté le widget %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">Vous avez accepté l’invitation pour %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Vous avez révoqué l\'invitation pour %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s a révoqué l\'invitation pour %2$s</string> - <string name="notice_made_future_direct_room_visibility_by_you">Vous avez rendu les futurs messages visible pour %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s a rendu les futurs messages visible pour %2$s</string> - <string name="notice_room_avatar_removed_by_you">Vous avez supprimé l\'avatar du salon</string> - <string name="notice_room_avatar_removed">%1$s a supprimé l\'avatar du salon</string> - <string name="notice_room_name_removed_by_you">Vous avez supprimé le nom du salon</string> - <string name="notice_requested_voip_conference_by_you">Vous avez demandé une téléconférence VoIP</string> - <string name="notice_end_to_end_by_you">Vous avez activé le chiffrement de bout en bout (%1$s)</string> - <string name="notice_made_future_room_visibility_by_you">Vous avez rendu l’historique futur du salon visible pour %1$s</string> - <string name="notice_ended_call_by_you">Vous avez raccroché.</string> - <string name="notice_answered_call_by_you">Vous avez répondu à l’appel.</string> - <string name="notice_call_candidates_by_you">Vous avez envoyé les données pour configurer l\'appel.</string> - <string name="notice_call_candidates">%s a envoyé les données pour configurer l\'appel.</string> - <string name="notice_placed_voice_call_by_you">Vous avez passé un appel vocal.</string> - <string name="notice_placed_video_call_by_you">Vous avez passé un appel vidéo.</string> - <string name="notice_room_name_changed_by_you">Vous avez changé le nom du salon en : %1$s</string> - <string name="notice_room_avatar_changed_by_you">Vous avez modifié l\'avatar du salon</string> - <string name="notice_room_avatar_changed">%1$s a modifié l\'avatar du salon</string> - <string name="notice_display_name_changed_from_by_you">Vous avez modifié votre nom affiché de %1$s en %2$s</string> - <string name="notice_display_name_set_by_you">Vous avez modifié votre nom affiché en %1$s</string> - <string name="notice_avatar_url_changed_by_you">Vous avez changé votre avatar</string> - <string name="notice_room_withdraw_by_you">Vous avez annulé l’invitation de %1$s</string> - <string name="notice_room_topic_changed_by_you">Vous avez changé le sujet en : %1$s</string> - <string name="notice_display_name_removed_by_you">Vous avez supprimé votre nom affiché (précédemment %1$s)</string> - <string name="notice_room_third_party_revoked_invite_by_you">Vous avez révoqué l’invitation pour %1$s à rejoindre le salon</string> - <string name="notice_direct_room_third_party_invite_by_you">Vous avez invité %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s a invité %2$s</string> - <string name="notice_room_third_party_invite_by_you">Vous avez envoyé une invitation à %1$s pour rejoindre le salon</string> - <string name="notice_profile_change_redacted_by_you">Vous avez mis à jour votre profile %1$s</string> - <string name="notice_room_topic_removed_by_you">Vous avez supprimé le sujet du salon</string> - <string name="notice_room_ban_with_reason_by_you">Vous avez exclus %1$s. Raison : %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Vous avez révoqué l\'exclusion de %1$s. Raison : %2$s</string> - <string name="notice_room_ban_by_you">Vous avez exclus %1$s</string> - <string name="notice_room_unban_by_you">Vous avez révoqué l\'exclusion de %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Vous avez expulsé %1$s. Raison : %2$s</string> - <string name="room_displayname_empty_room_was">Salon vide (était %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s et %4$d autre</item> - <item quantity="other">%1$s, %2$s, %3$s et %4$d autres</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s et %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s et %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Tous les serveurs sont interdits de participer ! Ce salon ne peut plus être utilisé.</string> - <string name="notice_room_server_acl_updated_no_change">Aucun changement.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Les serveurs correspondant à des IP littérales sont maintenant interdits.</string> - <string name="notice_room_server_acl_set_banned">• Les serveurs correspondant à %s sont interdits.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Les serveurs correspondants à des IP littérales sont interdites.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Les serveurs correspondants à des IP littérales sont autorisés.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Les serveurs correspondants à des IP littérales sont maintenant autorisées.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Les serveurs correspondant à %s sont supprimés de la liste autorisée.</string> - <string name="notice_room_server_acl_updated_allowed">• les serveur correspondant à %s sont maintenant autorisés.</string> - <string name="notice_room_server_acl_updated_was_banned">• Les serveurs correspondant à %s étaient supprimés de la liste des interdits.</string> - <string name="notice_room_server_acl_updated_banned">• Les serveurs correspondant à %s sont maintenant interdits.</string> - <string name="notice_room_server_acl_updated_title_by_you">Vous avez changé les droits ACL du serveur pour ce salon.</string> - <string name="notice_room_server_acl_updated_title">%s a changé les droits ACL du serveur pour ce salon.</string> - <string name="notice_room_server_acl_set_allowed">• Les serveurs correspondant à %s sont autorisés.</string> - <string name="notice_room_server_acl_set_title_by_you">Vous avez paramétré les ACL pour ce salon.</string> - <string name="notice_room_server_acl_set_title">%s paramètre les autorisations étendues (ACL) du serveur pour ce salon.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-gl/strings.xml b/matrix-sdk-android/src/main/res/values-gl/strings.xml deleted file mode 100644 index 77868e7df3a8f96a6976659cb4b63400aeadb841..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-gl/strings.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="medium_email">Enderezo de correo</string> - <string name="message_failed_to_upload">Fallo ao subir a páxina</string> - - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s enviou unha imaxe.</string> - <string name="summary_user_sent_sticker">%1$s enviou unha icona.</string> - - <string name="notice_room_invite_no_invitee">Convite de %s</string> - <string name="notice_room_invite">%1$s convidou a %2$s</string> - <string name="notice_room_invite_you">%1$s convidouno</string> - <string name="notice_room_join">%1$s entrou</string> - <string name="notice_room_leave">%1$s saÃu</string> - <string name="notice_room_reject">%1$s rexeitou o convite</string> - <string name="notice_room_kick">%1$s expulsou a %2$s</string> - <string name="notice_room_unban">%1$s desbloqueou a %2$s</string> - <string name="notice_room_ban">%1$s bloqueou a %2$s</string> - <string name="notice_room_withdraw">%1$s cancelou o convite de %2$s</string> - <string name="notice_avatar_url_changed">%1$s cambiou o seu avatar</string> - <string name="notice_display_name_set">%1$s cambiou o seu nome a %2$s</string> - <string name="notice_display_name_changed_from">%1$s cambiou o seu nome de %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s borrou o seu nome público (%2$s)</string> - <string name="notice_room_topic_changed">%1$s cambiou o tema desta sala para: %2$s</string> - <string name="notice_room_name_changed">%1$s cambiou o nome desta sala para: %2$s</string> - <string name="notice_placed_video_call">%s iniciou unha chamada de vÃdeo.</string> - <string name="notice_placed_voice_call">%s iniciou unha chamada de voz.</string> - <string name="notice_answered_call">%s respondeu á chamada.</string> - <string name="notice_ended_call">%s terminou a chamada.</string> - <string name="notice_made_future_room_visibility">%1$s fixo visible os próximos históricos para %2$s</string> - <string name="notice_room_visibility_invited">toda a xente que integran esta sala, desde o momento en que foron convidados.</string> - <string name="notice_room_visibility_joined">todas a xente da sala, desde o momento en que entraron.</string> - <string name="notice_room_visibility_shared">todas os membros da sala.</string> - <string name="notice_room_visibility_world_readable">todos.</string> - <string name="notice_room_visibility_unknown">descoñecido (%s).</string> - <string name="notice_end_to_end">%1$s activou a criptografÃa par-a-par (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s solicitou unha conferencia VoIP</string> - <string name="notice_voip_started">A conferencia VoIP comenzou</string> - <string name="notice_voip_finished">A conferencia VoIP terminou</string> - - <string name="notice_avatar_changed_too">(o avatar tamén foi cambiado)</string> - <string name="notice_room_name_removed">%1$s borrou o nome da sala</string> - <string name="notice_room_topic_removed">%1$s removeu o tema da sala</string> - <string name="notice_profile_change_redacted">%1$s actualizou o seu perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s envioulle un convite a %2$s para que entre na sala</string> - <string name="notice_room_third_party_registered_invite">%1$s aceptou o convite para %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** ImposÃbel descifrar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">O dispositivo do que envÃa non enviou as chaves desta mensaxe.</string> - - <string name="could_not_redact">Non se puido redactar</string> - <string name="unable_to_send_message">Non foi posÃbel enviar a mensaxe</string> - - <string name="network_error">Erro da conexión</string> - <string name="matrix_error">Erro de Matrix</string> - - <string name="room_error_join_failed_empty_room">AÃnda non é posÃbel volver a entrar nunha sala baleira.</string> - - <string name="encrypted_message">Mensaxe cifrada</string> - - <string name="medium_phone_number">Número de teléfono</string> - - <string name="room_displayname_two_members">%1$s e %2$s</string> - - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-hr/strings_sas.xml b/matrix-sdk-android/src/main/res/values-hr/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..423ab20186897d412c7c5057c0e4bd7a68e6b25c --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-hr/strings_sas.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> + <string name="verification_emoji_dog">pas</string> + <string name="verification_emoji_cat">maÄka</string> + <string name="verification_emoji_lion">lav</string> + <string name="verification_emoji_horse">konj</string> + <string name="verification_emoji_unicorn">jednorog</string> + <string name="verification_emoji_pig">svinja</string> + <string name="verification_emoji_elephant">slon</string> + <string name="verification_emoji_rabbit">zec</string> + <string name="verification_emoji_panda">panda</string> + <string name="verification_emoji_rooster">kokot</string> + <string name="verification_emoji_penguin">pingvin</string> + <string name="verification_emoji_turtle">kornjaÄa</string> + <string name="verification_emoji_fish">riba</string> + <string name="verification_emoji_octopus">hobotnica</string> + <string name="verification_emoji_butterfly">leptir</string> + <string name="verification_emoji_flower">svijet</string> + <string name="verification_emoji_tree">drvo</string> + <string name="verification_emoji_cactus">kaktus</string> + <string name="verification_emoji_mushroom">gljiva</string> + <string name="verification_emoji_globe">Globus</string> + <string name="verification_emoji_moon">mjesec</string> + <string name="verification_emoji_cloud">oblak</string> + <string name="verification_emoji_fire">vatra</string> + <string name="verification_emoji_banana">banana</string> + <string name="verification_emoji_apple">jabuka</string> + <string name="verification_emoji_strawberry">jagoda</string> + <string name="verification_emoji_corn">kukuruza</string> + <string name="verification_emoji_pizza">pizza</string> + <string name="verification_emoji_cake">torta</string> + <string name="verification_emoji_heart">srca</string> + <string name="verification_emoji_smiley">smajlića</string> + <string name="verification_emoji_robot">robot</string> + <string name="verification_emoji_hat">kapa</string> + <string name="verification_emoji_glasses">naoÄale</string> + <string name="verification_emoji_spanner">kljuÄ</string> + <string name="verification_emoji_santa">deda Mraz</string> + <string name="verification_emoji_thumbs_up">palac gore</string> + <string name="verification_emoji_umbrella">kiÅ¡obran</string> + <string name="verification_emoji_hourglass">pjeÅ¡Äani sat</string> + <string name="verification_emoji_clock">sat</string> + <string name="verification_emoji_gift">poklon</string> + <string name="verification_emoji_light_bulb">žarulja</string> + <string name="verification_emoji_book">knjiga</string> + <string name="verification_emoji_pencil">olovka</string> + <string name="verification_emoji_paperclip">spajalica</string> + <string name="verification_emoji_scissors">Å¡kare</string> + <string name="verification_emoji_lock">zakljuÄati</string> + <string name="verification_emoji_key">kljuÄ</string> + <string name="verification_emoji_hammer">Äekić</string> + <string name="verification_emoji_telephone">telefon</string> + <string name="verification_emoji_flag">zastava</string> + <string name="verification_emoji_train">vlak</string> + <string name="verification_emoji_bicycle">bicikl</string> + <string name="verification_emoji_aeroplane">avion</string> + <string name="verification_emoji_rocket">raketa</string> + <string name="verification_emoji_trophy">trofej</string> + <string name="verification_emoji_ball">lopta</string> + <string name="verification_emoji_guitar">gitara</string> + <string name="verification_emoji_trumpet">truba</string> + <string name="verification_emoji_bell">zvono</string> + <string name="verification_emoji_anchor">sidro</string> + <string name="verification_emoji_headphones">sluÅ¡alice</string> + <string name="verification_emoji_folder">mapu</string> + <string name="verification_emoji_pin">pribadaÄa</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-hu/strings.xml b/matrix-sdk-android/src/main/res/values-hu/strings.xml deleted file mode 100644 index 49238ee8ff15e865f77b8026585f82fd7fa8ada6..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-hu/strings.xml +++ /dev/null @@ -1,178 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s küldött egy képet.</string> - <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 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> - <string name="notice_room_ban">%1$s kitiltotta: %2$s</string> - <string name="notice_room_withdraw">%1$s visszavonta %2$s meghÃvását</string> - <string name="notice_avatar_url_changed">%1$s megváltoztatta a profilképét</string> - <string name="notice_display_name_set">%1$s megváltoztatta a megjelenÅ‘ nevét erre: %2$s</string> - <string name="notice_display_name_changed_from">%1$s megváltoztatta a megjelenÃtendÅ‘ nevét errÅ‘l: %2$s, erre: %3$s</string> - <string name="notice_display_name_removed">%1$s eltávolÃtotta a megjelenÃtendÅ‘ nevét (%2$s)</string> - <string name="notice_room_topic_changed">%1$s megváltoztatta a témát erre: %2$s</string> - <string name="notice_room_name_changed">%1$s megváltoztatta a szoba nevét erre: %2$s</string> - <string name="notice_placed_video_call">%s videóhÃvást kezdeményezett.</string> - <string name="notice_placed_voice_call">%s hanghÃvást indÃtott.</string> - <string name="notice_answered_call">%s fogadta a hÃvást.</string> - <string name="notice_ended_call">%s befejezte a hÃvást.</string> - <string name="notice_made_future_room_visibility">%1$s láthatóvá tette a jövÅ‘beli elÅ‘zményeket %2$s</string> - <string name="notice_room_visibility_invited">a szoba összes tagja számára, a meghÃvásuk idÅ‘pontjától kezdve.</string> - <string name="notice_room_visibility_joined">a szoba összes tagja számára, a csatlakozásuk idÅ‘pontjától kezdve.</string> - <string name="notice_room_visibility_shared">az összes szobatag számára.</string> - <string name="notice_room_visibility_world_readable">bárki.</string> - <string name="notice_room_visibility_unknown">ismeretlen (%s).</string> - <string name="notice_end_to_end">%1$s bekapcsolta a végpontok közötti titkosÃtást (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s hanghÃvás konferenciát kérelmezett</string> - <string name="notice_voip_started">HanghÃvás konferencia elindult</string> - <string name="notice_voip_finished">HanghÃvás konferencia befejezÅ‘dött</string> - <string name="notice_avatar_changed_too">(a profilkép is megváltozott)</string> - <string name="notice_room_name_removed">%1$s eltávolÃtotta a szoba nevét</string> - <string name="notice_room_topic_removed">%1$s eltávolÃtotta a szoba témáját</string> - <string name="notice_profile_change_redacted">%1$s megváltoztatta a(z) %2$s profilját</string> - <string name="notice_room_third_party_invite">%1$s meghÃvót küldött %2$s számára, hogy csatlakozzon a szobához</string> - <string name="notice_room_third_party_registered_invite">%1$s elfogadta a meghÃvót ebbe: %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Visszafejtés sikertelen: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">A küldÅ‘ eszköze nem küldte el a kulcsokat ehhez az üzenethez.</string> - <string name="could_not_redact">Kitakarás sikertelen</string> - <string name="unable_to_send_message">Ãœzenet küldése sikertelen</string> - <string name="message_failed_to_upload">Kép feltöltése sikertelen</string> - <string name="network_error">Hálózati hiba</string> - <string name="matrix_error">Matrix hiba</string> - <string name="room_error_join_failed_empty_room">Jelenleg nem lehetséges újracsatlakozni egy üres szobához.</string> - <string name="encrypted_message">TitkosÃtott üzenet</string> - <string name="medium_email">E-mail cÃm</string> - <string name="medium_phone_number">Telefonszám</string> - <string name="summary_user_sent_sticker">%1$s küldött egy matricát.</string> - <string name="room_displayname_invite_from">MeghÃvó tÅ‘le: %s</string> - <string name="room_displayname_room_invite">MeghÃvó egy szobába</string> - <string name="room_displayname_two_members">%1$s és %2$s</string> - <string name="room_displayname_empty_room">Ãœres szoba</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s és 1 másik</item> - <item quantity="other">%1$s és %2$d másik</item> - </plurals> - <string name="notice_event_redacted">Ãœzenet eltávolÃtva</string> - <string name="notice_event_redacted_by">Ãœzenetet eltávolÃtotta: %1$s</string> - <string name="notice_event_redacted_with_reason">Ãœzenet eltávolÃtva [ok: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Ãœzenetet eltávolÃtotta: %1$s [ok: %2$s]</string> - <string name="initial_sync_start_importing_account">Induló szinkronizáció: -\nFiók betöltése…</string> - <string name="initial_sync_start_importing_account_crypto">Induló szinkronizáció: -\nTitkosÃtás betöltése</string> - <string name="initial_sync_start_importing_account_rooms">Induló szinkronizáció: -\nSzobák betöltése</string> - <string name="initial_sync_start_importing_account_joined_rooms">Induló szinkronizáció: -\nCsatlakozott szobák betöltése</string> - <string name="initial_sync_start_importing_account_invited_rooms">Induló szinkronizáció: -\nMeghÃvott szobák betöltése</string> - <string name="initial_sync_start_importing_account_left_rooms">Induló szinkronizáció: -\nElhagyott szobák betöltése</string> - <string name="initial_sync_start_importing_account_groups">Induló szinkronizáció: -\nKözösségek betöltése</string> - <string name="initial_sync_start_importing_account_data">Induló szinkronizáció: -\nFiók adatok betöltése</string> - <string name="notice_room_update">%s frissÃtette ezt a szobát.</string> - <string name="event_status_sending_message">Ãœzenet küldése…</string> - <string name="clear_timeline_send_queue">KüldÅ‘ sor ürÃtése</string> - <string name="notice_room_third_party_revoked_invite">%1$s visszavonta %2$s meghÃvását, hogy csatlakozzon a szobához</string> - <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 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> - <string name="notice_room_ban_with_reason">%1$s kitiltotta Å‘t: %2$s. Ok: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s meghÃvót küldött neki: %2$s, hogy lépjen be a szobába. Ok: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s visszavonta %2$s meghÃvóját a szobába való belépéshez. Ok: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s elfogadta a meghÃvót ide: %2$s. Ok: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s visszavonta %2$s meghÃvóját. Ok: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s ezt a cÃmet adta a szobához: %2$s.</item> - <item quantity="other">%1$s ezeket a cÃmeket adta a szobához: %2$s.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s ezt a cÃmet törölte a szobából: %3$s.</item> - <item quantity="other">%1$s ezeket a cÃmeket törölte a szobából: %3$s.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s a szobához adta ezeket:%2$s és törölte ezeket: %3$s.</string> - <string name="notice_room_canonical_alias_set">%1$s a szoba elsÅ‘dleges cÃmét erre állÃtotta be: %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s eltávolÃtotta a szoba elsÅ‘dleges cÃmét.</string> - <string name="notice_room_guest_access_can_join">%1$s megengedte a vendégeknek, hogy belépjenek ebbe a szobába.</string> - <string name="notice_room_guest_access_forbidden">%1$s megtiltotta a vendégeknek, hogy belépjenek ebbe a szobába.</string> - <string name="notice_end_to_end_ok">%1$s bekapcsolta a végpontok közötti titkosÃtást.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s bekapcsolta a végpontok közötti titkosÃtást (ismeretlen algoritmus %2$s).</string> - <string name="key_verification_request_fallback_message">%s kéri a kulcsok ellenÅ‘rzését de a kliens nem támogatja a szobán belüli kulcs ellenÅ‘rzést. A hagyományos módon kell ellenÅ‘rizned a kulcsokat.</string> - <string name="notice_room_created">%1$s szobát készÃtett</string> - <string name="notice_answered_call_by_you">Fogadtad a hÃvást.</string> - <string name="notice_ended_call_by_you">Befejezted a hÃvást.</string> - <string name="notice_call_candidates_by_you">HÃvási adatokat küldtél.</string> - <string name="notice_call_candidates">%s hÃvási adatokat küldött.</string> - <string name="notice_placed_voice_call_by_you">HanghÃvást indÃtottál.</string> - <string name="notice_placed_video_call_by_you">VideóhÃvást indÃtottál.</string> - <string name="notice_room_avatar_changed_by_you">Megváltoztattad a szoba képét</string> - <string name="notice_room_avatar_changed">%1$s megváltoztatta a szoba képét</string> - <string name="notice_room_topic_changed_by_you">Megváltoztattad a témát erre: %1$s</string> - <string name="notice_avatar_url_changed_by_you">Megváltoztattad a profilképed</string> - <string name="notice_room_withdraw_by_you">Visszavontad %1$s meghÃvóját</string> - <string name="notice_room_ban_by_you">Kitiltottad %1$s felhasználót</string> - <string name="notice_room_unban_by_you">Visszaengedted %1$s felhasználót</string> - <string name="notice_room_kick_by_you">Kirúgtad %1$s felhasználót</string> - <string name="notice_room_reject_by_you">VisszautasÃtottad a meghÃvót</string> - <string name="notice_direct_room_leave_by_you">Elhagytad a szobát</string> - <string name="notice_direct_room_leave">%1$s elhagyta a szobát</string> - <string name="notice_room_leave_by_you">Elhagytad a szobát</string> - <string name="notice_direct_room_join_by_you">Csatlakoztál</string> - <string name="notice_direct_room_join">%1$s csatlakozott</string> - <string name="notice_room_join_by_you">Beléptél a szobába</string> - <string name="notice_room_invite_by_you">MeghÃvtad: %1$s</string> - <string name="notice_direct_room_created_by_you">Létrehoztad a beszélgetést</string> - <string name="notice_direct_room_created">%1$s létrehozta a beszélgetést</string> - <string name="notice_room_created_by_you">Létrehoztad a szobát</string> - <string name="summary_you_sent_sticker">Matricát küldtél.</string> - <string name="summary_you_sent_image">Képet küldtél.</string> - <string name="power_level_custom_no_value">Saját</string> - <string name="power_level_custom">Saját (%1$d)</string> - <string name="power_level_default">Alapértelmezett</string> - <string name="power_level_moderator">Moderátor</string> - <string name="power_level_admin">Admin</string> - <string name="notice_widget_modified_by_you">Ön megváltoztatta a %1$s kisalkalmazást</string> - <string name="notice_widget_modified">%1$s megváltoztatta a %2$s kisalkalmazást</string> - <string name="notice_widget_removed_by_you">Ön eltávolÃtotta a %1$s kisalkalmazást</string> - <string name="notice_widget_removed">%1$s eltávolÃtotta a %2$s kisalkalmazást</string> - <string name="notice_widget_added_by_you">Ön hozzáadott egy %1$s kisalkalmazást</string> - <string name="notice_widget_added">%1$s hozzáadott egy %2$s kisalkalmazást</string> - <string name="notice_room_third_party_registered_invite_by_you">Ön elfogadta a meghÃvót ehhez: %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Ön visszavonta %1$s felhasználó meghÃvóját</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s visszavonta %2$s felhasználó meghÃvóját</string> - <string name="notice_room_third_party_revoked_invite_by_you">Ön visszavonta %1$s felhasználó meghÃvóját</string> - <string name="notice_direct_room_third_party_invite_by_you">Ön meghÃvta %1$s felhasználót</string> - <string name="notice_direct_room_third_party_invite">%1$s meghÃvta %2$s felhasználót</string> - <string name="notice_room_third_party_invite_by_you">Ön meghÃvót küldött %1$s felhasználónak, hogy csatlakozzon a szobához</string> - <string name="notice_profile_change_redacted_by_you">Ön frissÃtette a saját profilját %1$s</string> - <string name="notice_room_avatar_removed_by_you">Ön eltávolÃtotta a szoba képét</string> - <string name="notice_room_avatar_removed">%1$s eltávolÃtotta a szoba képét</string> - <string name="notice_room_topic_removed_by_you">Ön eltávolÃtotta a szoba témáját</string> - <string name="notice_room_name_removed_by_you">Ön eltávolÃtotta a szoba nevét</string> - <string name="notice_requested_voip_conference_by_you">Ön videókonferencia kezdeményezését kérte</string> - <string name="notice_direct_room_update_by_you">Ön frissÃtette ezt a szobát.</string> - <string name="notice_direct_room_update">%s frissÃtette a szobát.</string> - <string name="notice_room_update_by_you">Ön frissÃtette ezt a szobát.</string> - <string name="notice_end_to_end_by_you">Ön bekapcsolta a végpontok közötti titkosÃtást (%1$s)</string> - <string name="notice_made_future_direct_room_visibility_by_you">Ön elérhetÅ‘vé tette a jövÅ‘beni üzeneteket %1$s</string> - <string name="notice_made_future_room_visibility_by_you">Ön elérhetÅ‘vé tette a jövÅ‘beni üzeneteket %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s elérhetÅ‘vé tette a jövÅ‘beni üzeneteket %2$s</string> - <string name="notice_room_name_changed_by_you">Ön megváltoztatta a szoba nevét erre: %1$s</string> - <string name="notice_display_name_removed_by_you">Ön eltávolÃtotta a saját megjelenÃtett nevét (%1$s volt)</string> - <string name="notice_display_name_changed_from_by_you">Ön megváltoztatta a saját megjelenÃtési nevét errÅ‘l: %1$s, erre: %2$s</string> - <string name="notice_display_name_set_by_you">Ön beállÃtotta a saját megjelenÃtési nevét erre: %1$s</string> - <string name="notice_room_invite_no_invitee_by_you">Az ön meghÃvása</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-id/strings.xml b/matrix-sdk-android/src/main/res/values-id/strings.xml deleted file mode 100644 index 157b23d4017375bb9e9a253e19884046623e20fe..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-id/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="room_displayname_invite_from">Undang dari %s</string> - <string name="room_displayname_room_invite">Undangan Ruang</string> - <string name="room_displayname_two_members">%1$s dan %2$s</string> - - <string name="room_displayname_empty_room">Ruang kosong</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$s dan %2$d yang lain</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-in/strings.xml b/matrix-sdk-android/src/main/res/values-in/strings.xml deleted file mode 100644 index 157b23d4017375bb9e9a253e19884046623e20fe..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-in/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="room_displayname_invite_from">Undang dari %s</string> - <string name="room_displayname_room_invite">Undangan Ruang</string> - <string name="room_displayname_two_members">%1$s dan %2$s</string> - - <string name="room_displayname_empty_room">Ruang kosong</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$s dan %2$d yang lain</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-is/strings.xml b/matrix-sdk-android/src/main/res/values-is/strings.xml deleted file mode 100644 index ecf19edb8ac48e58d210944b4618108df363de25..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-is/strings.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s sendi mynd.</string> - <string name="summary_user_sent_sticker">%1$s sendi lÃmmerki.</string> - - <string name="notice_room_invite_no_invitee">%s sendi boð um þátttöku</string> - <string name="notice_room_invite">%1$s bauð %2$s</string> - <string name="notice_room_invite_you">%1$s bauð þér</string> - <string name="notice_room_join">%1$s gekk à hópinn</string> - <string name="notice_room_leave">%1$s hætti</string> - <string name="notice_room_reject">%1$s hafnaði boðinu</string> - <string name="notice_room_kick">%1$s sparkaði %2$s</string> - <string name="notice_room_unban">%1$s afbannaði %2$s</string> - <string name="notice_room_ban">%1$s bannaði %2$s</string> - <string name="notice_avatar_url_changed">%1$s breyttu auðkennismynd sinni</string> - <string name="notice_room_visibility_invited">allir meðlimir spjallrásar, sÃðan þeim var boðið.</string> - <string name="notice_room_visibility_joined">allir meðlimir spjallrásar, sÃðan þeir skráðu sig.</string> - <string name="notice_room_visibility_shared">allir meðlimir spjallrásar.</string> - <string name="notice_room_visibility_world_readable">hver sem er.</string> - <string name="notice_room_visibility_unknown">óþekktur (%s).</string> - <string name="notice_voip_started">VoIP-sÃmafundur hafinn</string> - <string name="notice_voip_finished">VoIP-sÃmafundi lokið</string> - - <string name="notice_avatar_changed_too">(einnig var skipt um auðkennismynd)</string> - <string name="notice_crypto_unable_to_decrypt">** Mistókst að afkóða: %s **</string> - - <string name="unable_to_send_message">Gat ekki sent skilaboð</string> - - <string name="message_failed_to_upload">Gat ekki sent inn mynd</string> - - <string name="network_error">Villa à netkerfi</string> - <string name="matrix_error">Villa à Matrix</string> - - <string name="encrypted_message">Dulrituð skilaboð</string> - - <string name="medium_email">Tölvupóstfang</string> - <string name="medium_phone_number">SÃmanúmer</string> - - <string name="notice_room_withdraw">%1$s tók til baka boð frá %2$s</string> - <string name="notice_display_name_set">%1$s setti birtingarnafn sitt sem %2$s</string> - <string name="notice_display_name_changed_from">%1$s breytti birtingarnafni sÃnu úr %2$s à %3$s</string> - <string name="notice_display_name_removed">%1$s fjarlægði birtingarnafn sitt (%2$s)</string> - <string name="notice_room_topic_changed">%1$s breytti umræðuefninu Ã: %2$s</string> - <string name="notice_room_name_changed">%1$s breytti heiti spjallrásarinnar Ã: %2$s</string> - <string name="notice_placed_video_call">%s hringdi myndsamtal.</string> - <string name="notice_placed_voice_call">%s hringdi raddsamtal.</string> - <string name="notice_answered_call">%s svaraði sÃmtalinu.</string> - <string name="notice_ended_call">%s lauk sÃmtalinu.</string> - <string name="notice_end_to_end">%1$s kveikti á enda-Ã-enda dulritun (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s bað um VoIP-sÃmafund</string> - <string name="notice_room_name_removed">%1$s fjarlægði heiti spjallrásar</string> - <string name="notice_room_topic_removed">%1$s fjarlægði umfjöllunarefni spjallrásar</string> - <string name="notice_made_future_room_visibility">%1$s gerði ferilskrá spjallrásar héðan à frá sýnilega fyrir %2$s</string> - <string name="notice_profile_change_redacted">%1$s uppfærði notandasniðið sitt %2$s</string> - <string name="notice_room_third_party_invite">%1$s sendi boð til %2$s um þátttöku à spjallrásinni</string> - <string name="notice_room_third_party_registered_invite">%1$s samþykkti boð um að taka þátt à %2$s</string> - - <string name="notice_crypto_error_unkwown_inbound_session_id">Tæki sendandans hefur ekki sent okkur dulritunarlyklana fyrir þessi skilaboð.</string> - - <string name="could_not_redact">Gat ekki ritstýrt</string> - <string name="room_error_join_failed_empty_room">Ekki er à augnablikinu hægt að taka aftur þátt à spjallrás sem er tóm.</string> - - <string name="room_displayname_room_invite">Boð á spjallrás</string> - <string name="room_displayname_two_members">%1$s og %2$s</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s og 1 annar</item> - <item quantity="other">%1$s og %2$d aðrir</item> - </plurals> - - <string name="room_displayname_empty_room">Tóm spjallrás</string> - <string name="room_displayname_invite_from">Boð frá %s</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-it/strings.xml b/matrix-sdk-android/src/main/res/values-it/strings.xml deleted file mode 100644 index ec19cd5c17e072ae7c7add030363b3135bd92210..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-it/strings.xml +++ /dev/null @@ -1,262 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s ha inviato un\'immagine.</string> - <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 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> - <string name="notice_room_ban">%1$s ha bandito %2$s</string> - <string name="notice_room_withdraw">%1$s ha revocato l\'invito per %2$s</string> - <string name="notice_avatar_url_changed">%1$s ha modificato il suo avatar</string> - <string name="notice_display_name_set">%1$s hanno cambiato il nome visualizzato con %2$s</string> - <string name="notice_display_name_changed_from">%1$s ha cambiato il nome visualizzato da %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s ha rimosso il nome visibile (%2$s)</string> - <string name="notice_room_topic_changed">%1$s ha cambiato l\'argomento con: %2$s</string> - <string name="notice_room_name_changed">%1$s ha cambiato il nome della stanza con: %2$s</string> - <string name="notice_placed_video_call">%s ha iniziato una chiamata video.</string> - <string name="notice_placed_voice_call">%s ha iniziato una chiamata vocale.</string> - <string name="notice_answered_call">%s ha risposto alla chiamata.</string> - <string name="notice_ended_call">%s ha terminato la chiamata.</string> - <string name="notice_made_future_room_visibility">%1$s ha reso la futura cronologia della stanza visibile a %2$s</string> - <string name="notice_room_visibility_invited">tutti i membri della stanza, dal momento del loro invito.</string> - <string name="notice_room_visibility_joined">tutti i membri della stanza, dal momento in cui sono entrati.</string> - <string name="notice_room_visibility_shared">tutti i membri della stanza.</string> - <string name="notice_room_visibility_world_readable">chiunque.</string> - <string name="notice_room_visibility_unknown">sconosciuto (%s).</string> - <string name="notice_end_to_end">%1$s ha attivato la crittografia end-to-end (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s ha richiesto una conferenza VoIP</string> - <string name="notice_voip_started">Conferenza VoIP iniziata</string> - <string name="notice_voip_finished">Conferenza VoIP terminata</string> - <string name="notice_avatar_changed_too">(anche l\'avatar è cambiato)</string> - <string name="notice_room_name_removed">%1$s ha rimosso il nome della stanza</string> - <string name="notice_room_topic_removed">%1$s ha rimosso l\'argomento della stanza</string> - <string name="notice_profile_change_redacted">%1$s ha aggiornato il profilo %2$s</string> - <string name="notice_room_third_party_invite">%1$s ha mandato un invito a %2$s per unirsi alla stanza</string> - <string name="notice_room_third_party_registered_invite">%1$s ha accettato l\'invito per %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Impossibile decriptare: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Il dispositivo del mittente non ci ha inviato le chiavi per questo messaggio.</string> - <string name="could_not_redact">Impossibile revisionare</string> - <string name="unable_to_send_message">Impossibile inviare il messaggio</string> - <string name="message_failed_to_upload">Invio dell\'immagine fallito</string> - <string name="network_error">Errore di rete</string> - <string name="matrix_error">Errore di Matrix</string> - <string name="room_error_join_failed_empty_room">Al momento non è possibile rientrare in una stanza vuota.</string> - <string name="encrypted_message">Messaggio criptato</string> - <string name="medium_email">Indirizzo email</string> - <string name="medium_phone_number">Numero di telefono</string> - <string name="summary_user_sent_sticker">%1$s ha inviato un adesivo.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Invito da %s</string> - <string name="room_displayname_room_invite">Invito nella stanza</string> - <string name="room_displayname_two_members">%1$s e %2$s</string> - <string name="room_displayname_empty_room">Stanza vuota</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s e 1 altro</item> - <item quantity="other">%1$s e %2$d altri</item> - </plurals> - <string name="notice_event_redacted">Messaggio rimosso</string> - <string name="notice_event_redacted_by">Messaggio rimosso da %1$s</string> - <string name="notice_event_redacted_with_reason">Messaggio rimosso [motivo: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Messaggio rimosso da %1$s [motivo: %2$s]</string> - <string name="initial_sync_start_importing_account">Sync iniziale: -\nImportazione account…</string> - <string name="initial_sync_start_importing_account_crypto">Sync iniziale: -\nImportazione cifratura</string> - <string name="initial_sync_start_importing_account_rooms">Sync iniziale: -\nImportazione stanze</string> - <string name="initial_sync_start_importing_account_joined_rooms">Sync iniziale: -\nImportazione stanze partecipate</string> - <string name="initial_sync_start_importing_account_invited_rooms">Sync iniziale: -\nImportazione stanze invitate</string> - <string name="initial_sync_start_importing_account_left_rooms">Sync iniziale: -\nImportazione stanze lasciate</string> - <string name="initial_sync_start_importing_account_groups">Sync iniziale: -\nImportazione comunità </string> - <string name="initial_sync_start_importing_account_data">Sync iniziale: -\nImportazione dati account</string> - <string name="notice_room_update">%s ha aggiornato questa stanza.</string> - <string name="event_status_sending_message">Invio messaggio in corso …</string> - <string name="clear_timeline_send_queue">Cancella la coda di invio</string> - <string name="notice_room_third_party_revoked_invite">%1$s ha revocato l\'invito a %2$s di unirsi alla stanza</string> - <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 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> - <string name="notice_room_ban_with_reason">%1$s ha bandito %2$s. Motivo: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s ha inviato un invito a %2$s di unirsi alla stanza. Motivo: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s ha revocato l\'invito a %2$s di unirsi alla stanza. Motivo: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s ha accettato l\'invito per %2$s. Motivo: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s ha rifiutato l\'invito di %2$s. Motivo: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s ha aggiunto %2$s come indirizzo per questa stanza.</item> - <item quantity="other">%1$s ha aggiunto %2$s come indirizzi per questa stanza.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s ha rimosso %2$s come indirizzo per questa stanza.</item> - <item quantity="other">%1$s ha rimosso %2$s come indirizzi per questa stanza.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s ha aggiunto %2$s e rimosso %3$s come indirizzi per questa stanza.</string> - <string name="notice_room_canonical_alias_set">%1$s ha impostato l\'indirizzo principale per questa stanza a %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s ha rimosso l\'indirizzo principale per questa stanza.</string> - <string name="notice_room_guest_access_can_join">%1$s ha permesso l\'accesso alla stanza per gli ospiti.</string> - <string name="notice_room_guest_access_forbidden">%1$s ha impedito l\'accesso alla stanza per gli ospiti.</string> - <string name="notice_end_to_end_ok">%1$s ha attivato la cifratura end-to-end.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s ha attivato la cifratura end-to-end (algoritmo %2$s non riconosciuto).</string> - <string name="key_verification_request_fallback_message">%s sta chiedendo di verificare la tua chiave, ma il tuo client non supporta la verifica in-chat. Dovrai usare il metodo di verifica obsoleto per verificare le chiavi.</string> - <string name="notice_room_created">%1$s ha creato la stanza</string> - <string name="summary_you_sent_image">Hai inviato un\'immagine.</string> - <string name="summary_you_sent_sticker">Hai inviato un adesivo.</string> - <string name="notice_room_invite_no_invitee_by_you">Il tuo invito</string> - <string name="notice_room_created_by_you">Hai creato la stanza</string> - <string name="notice_room_invite_by_you">Hai invitato %1$s</string> - <string name="notice_room_join_by_you">Sei entrato nella stanza</string> - <string name="notice_room_leave_by_you">Sei uscito dalla stanza</string> - <string name="notice_room_reject_by_you">Hai rifiutato l\'invito</string> - <string name="notice_room_kick_by_you">Hai buttato fuori %1$s</string> - <string name="notice_room_unban_by_you">Hai riammesso %1$s</string> - <string name="notice_room_ban_by_you">Hai bandito %1$s</string> - <string name="notice_room_withdraw_by_you">Hai ritirato l\'invito di %1$s</string> - <string name="notice_avatar_url_changed_by_you">Hai cambiato il tuo avatar</string> - <string name="notice_display_name_set_by_you">Hai impostato il tuo nome visualizzato a %1$s</string> - <string name="notice_display_name_changed_from_by_you">Hai cambiato il tuo nome visualizzato da %1$s a %2$s</string> - <string name="notice_display_name_removed_by_you">Hai rimosso il tuo nome visibile (era %1$s)</string> - <string name="notice_room_topic_changed_by_you">Hai cambiato l\'argomento a: %1$s</string> - <string name="notice_room_avatar_changed">%1$s ha modificato l\'avatar della stanza</string> - <string name="notice_room_avatar_changed_by_you">Hai modificato l\'avatar della stanza</string> - <string name="notice_room_name_changed_by_you">Hai cambiato il nome della stanza a: %1$s</string> - <string name="notice_placed_video_call_by_you">Hai iniziato una videochiamata.</string> - <string name="notice_placed_voice_call_by_you">Hai iniziato una telefonata.</string> - <string name="notice_call_candidates">%s ha inviato dati per impostare la chiamata.</string> - <string name="notice_call_candidates_by_you">Hai inviato dati per impostare la chiamata.</string> - <string name="notice_answered_call_by_you">Hai risposto alla chiamata.</string> - <string name="notice_ended_call_by_you">Hai terminato la chiamata.</string> - <string name="notice_made_future_room_visibility_by_you">Hai reso visibile la futura cronologia della stanza a %1$s</string> - <string name="notice_end_to_end_by_you">Hai attivato la crittografia end-to-end (%1$s)</string> - <string name="notice_room_update_by_you">Hai aggiornato questa stanza.</string> - <string name="notice_requested_voip_conference_by_you">Hai richiesto una conferenza VoIP</string> - <string name="notice_room_name_removed_by_you">Hai rimosso il nome della stanza</string> - <string name="notice_room_topic_removed_by_you">Hai rimosso l\'argomento della stanza</string> - <string name="notice_room_avatar_removed">%1$s ha rimosso l\'avatar della stanza</string> - <string name="notice_room_avatar_removed_by_you">Hai rimosso l\'avatar della stanza</string> - <string name="notice_profile_change_redacted_by_you">Hai aggiornato il tuo profilo %1$s</string> - <string name="notice_room_third_party_invite_by_you">Hai mandato un invito a %1$s a unirsi alla stanza</string> - <string name="notice_room_third_party_revoked_invite_by_you">Hai revocato l\'invito per %1$s a unirsi alla stanza</string> - <string name="notice_room_third_party_registered_invite_by_you">Hai accettato l\'invito per %1$s</string> - <string name="notice_widget_added">%1$s ha aggiunto il widget %2$s</string> - <string name="notice_widget_added_by_you">Hai aggiunto il widget %1$s</string> - <string name="notice_widget_removed">%1$s ha rimosso il widget %2$s</string> - <string name="notice_widget_removed_by_you">Hai rimosso il widget %1$s</string> - <string name="notice_widget_modified">%1$s ha modificato il widget %2$s</string> - <string name="notice_widget_modified_by_you">Hai modificato il widget %1$s</string> - <string name="power_level_admin">Amministratore</string> - <string name="power_level_moderator">Moderatore</string> - <string name="power_level_default">Predefinito</string> - <string name="power_level_custom">Personalizzato (%1$d)</string> - <string name="power_level_custom_no_value">Personalizzato</string> - <string name="notice_power_level_changed_by_you">Hai cambiato il livello di potere di %1$s.</string> - <string name="notice_power_level_changed">%1$s ha cambiato il livello di potere di %2$s.</string> - <string name="notice_power_level_diff">%1$s da %2$s a %3$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Il tuo invito. Motivo: %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Hai invitato %1$s. Motivo: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Sei entrato nella stanza. Motivo: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Sei uscito dalla stanza. Motivo: %1$s</string> - <string name="notice_room_reject_with_reason_by_you">Hai rifiutato l\'invito. Motivo: %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Hai buttato fuori %1$s. Motivo: %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Hai riammesso %1$s. Motivo: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Hai bandito %1$s. Motivo: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Hai mandato un invito a %1$s a unirsi alla stanza. Motivo: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Hai revocato l\'invito a %1$s a unirsi alla stanza. Motivo: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Hai accettato l\'invito per %1$s. Motivo: %2$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Hai ritirato l\'invito di %1$s. Motivo: %2$s</string> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Hai aggiunto %1$s come indirizzo per questa stanza.</item> - <item quantity="other">Hai aggiunto %1$s come indirizzi per questa stanza.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Hai rimosso %1$s come indirizzo per questa stanza.</item> - <item quantity="other">Hai rimosso %1$s come indirizzi per questa stanza.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed_by_you">Hai aggiunto %1$s e rimosso %2$s come indirizzi per questa stanza.</string> - <string name="notice_room_canonical_alias_set_by_you">Hai impostato l\'indirizzo principale per questa stanza a %1$s.</string> - <string name="notice_room_canonical_alias_unset_by_you">Hai rimosso l\'indirizzo principale per questa stanza.</string> - <string name="notice_room_guest_access_can_join_by_you">Hai permesso l\'accesso alla stanza per gli ospiti.</string> - <string name="notice_room_guest_access_forbidden_by_you">Hai impedito l\'accesso alla stanza per gli ospiti.</string> - <string name="notice_end_to_end_ok_by_you">Hai attivato la crittografia end-to-end.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Hai attivato la crittografia end-to-end (algoritmo %1$s sconosciuto).</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Hai impedito l\'accesso alla stanza agli ospiti.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s ha impedito l\'accesso alla stanza agli ospiti.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Hai permesso l\'accesso agli ospiti.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s ha permesso l\'accesso agli ospiti.</string> - <string name="notice_direct_room_join_with_reason_by_you">Sei entrato. Motivo: %1$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">Sei uscito. Motivo: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s è uscito. Motivo: %2$s</string> - <string name="notice_direct_room_join_with_reason">%1$s è entrato. Motivo: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Hai revocato l\'invito per %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s ha revocato l\'invito per %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Hai invitato %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s ha invitato %2$s</string> - <string name="notice_direct_room_update_by_you">Hai aggiornato la stanza.</string> - <string name="notice_direct_room_update">%s ha aggiornato la stanza.</string> - <string name="notice_made_future_direct_room_visibility_by_you">Hai reso visibili i messaggi futuri a %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s ha reso visibili i messaggi futuri a %2$s</string> - <string name="notice_direct_room_leave_by_you">Sei uscito dalla stanza</string> - <string name="notice_direct_room_leave">%1$s è uscito dalla stanza</string> - <string name="notice_direct_room_join_by_you">Sei entrato</string> - <string name="notice_direct_room_join">%1$s è entrato</string> - <string name="notice_direct_room_created_by_you">Hai creato la discussione</string> - <string name="notice_direct_room_created">%1$s ha creato la discussione</string> - <string name="room_displayname_empty_room_was">Stanza vuota (era %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s e %4$d altro</item> - <item quantity="other">%1$s, %2$s, %3$s e altri %4$d</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s e %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s e %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Tutti i server sono banditi dalla partecipazione! Questa stanza non può più essere usata.</string> - <string name="notice_room_server_acl_updated_no_change">Nessuna modifica.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• I server che corrispondono a IP letterali ora sono banditi.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• I server che corrispondono a IP letterali ora sono permessi.</string> - <string name="notice_room_server_acl_updated_was_allowed">• I server che corrispondono a %s sono stati rimossi dalla lista dei consentiti.</string> - <string name="notice_room_server_acl_updated_allowed">• I server che corrispondono a %s ora sono permessi.</string> - <string name="notice_room_server_acl_updated_was_banned">• I server che corrispondono a %s sono stati rimossi dalla lista di ban.</string> - <string name="notice_room_server_acl_updated_banned">• I server che corrispondono a %s ora sono banditi.</string> - <string name="notice_room_server_acl_updated_title_by_you">Hai cambiato le ACL del server per questa stanza.</string> - <string name="notice_room_server_acl_updated_title">%s ha cambiato le ACL del server per questa stanza.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• I server che corrispondono a IP letterali sono banditi.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• I server che corrispondono a IP letterali sono permessi.</string> - <string name="notice_room_server_acl_set_allowed">• I server che corrispondono a %s sono permessi.</string> - <string name="notice_room_server_acl_set_banned">• I server che corrispondono a %s sono banditi.</string> - <string name="notice_room_server_acl_set_title_by_you">Hai impostato le ACL del server per questa stanza.</string> - <string name="notice_room_server_acl_set_title">%s ha impostato le ACL del server per questa stanza.</string> - <string name="notice_room_canonical_alias_no_change_by_you">Hai cambiato gli indirizzi per questa stanza.</string> - <string name="notice_room_canonical_alias_no_change">%1$s ha cambiato gli indirizzi per questa stanza.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Hai cambiato gli indirizzi principali ed alternativi per questa stanza.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s ha cambiato gli indirizzi principali ed alternativi per questa stanza.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Hai cambiato gli indirizzi alternativi per questa stanza.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s ha cambiato gli indirizzi alternativi per questa stanza.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Hai rimosso l\'indirizzo alternativo %1$s per questa stanza.</item> - <item quantity="other">Hai rimosso gli indirizzi alternativi %1$s per questa stanza.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s ha rimosso l\'indirizzo alternativo %2$s per questa stanza.</item> - <item quantity="other">%1$s ha rimosso gli indirizzi alternativi %2$s per questa stanza.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Hai aggiunto l\'indirizzo alternativo %1$s per questa stanza.</item> - <item quantity="other">Hai aggiunto gli indirizzi alternativi %1$s per questa stanza.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s ha aggiunto l\'indirizzo alternativo %2$s per questa stanza.</item> - <item quantity="other">%1$s ha aggiunto gli indirizzi alternativi %2$s per questa stanza.</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-ja/strings.xml b/matrix-sdk-android/src/main/res/values-ja/strings.xml deleted file mode 100644 index add19edfaf0ffbd8f30ef6701fc6065e273e9f6a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-ja/strings.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <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> - <string name="notice_answered_call">%sãŒé›»è©±ã«å‡ºã¾ã—ãŸã€‚</string> - <string name="notice_ended_call">%sãŒé€šè©±ã‚’終了ã—ã¾ã—ãŸã€‚</string> - <string name="room_displayname_invite_from">%sã•ã‚“ã‹ã‚‰ã®æ‹›å¾…</string> - <string name="room_displayname_room_invite">部屋ã¸ã®æ‹›å¾…</string> - <string name="room_displayname_two_members">%1$sã¨%2$s</string> - <string name="room_displayname_empty_room">空ã®éƒ¨å±‹</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$sã¨ä»–%2$då</item> - </plurals> - <string name="notice_made_future_room_visibility">%1$sã¯ã€ä»Šå¾Œã®éƒ¨å±‹å±¥æ´ã‚’%2$sã«è¡¨ç¤ºã•ã›ã¾ã—ãŸ</string> - <string name="notice_room_visibility_invited">部屋ã®ãƒ¡ãƒ³ãƒãƒ¼å…¨å“¡ã€æ‹›å¾…ã•ã‚ŒãŸæ™‚点ã‹ã‚‰ã€‚</string> - <string name="notice_room_visibility_joined">部屋ã®ãƒ¡ãƒ³ãƒãƒ¼å…¨å“¡ã€å‚åŠ ã—ãŸæ™‚点ã‹ã‚‰ã€‚</string> - <string name="notice_room_visibility_shared">部屋ã®ãƒ¡ãƒ³ãƒãƒ¼å…¨å“¡ã€‚</string> - <string name="notice_room_visibility_world_readable">誰ã§ã‚‚。</string> - <string name="notice_room_visibility_unknown">ä¸æ˜Ž (%s)。</string> - <string name="notice_end_to_end">%1$s ãŒã‚¨ãƒ³ãƒ‰ãƒ„ーエンド暗å·åŒ–を有効ã«ã—ã¾ã—㟠(%2$s)</string> - <string name="notice_requested_voip_conference">%1$s ãŒVoIP会è°ã‚’リクエストã—ã¾ã—ãŸ</string> - <string name="notice_voip_started">VoIP会è°ãŒé–‹å§‹ã•ã‚Œã¾ã—ãŸ</string> - <string name="notice_voip_finished">VoIP会è°ãŒçµ‚了ã—ã¾ã—ãŸ</string> - <string name="notice_avatar_changed_too">(ã‚¢ãƒã‚¿ãƒ¼ã‚‚変更ã•ã‚ŒãŸ)</string> - <string name="notice_room_name_removed">%1$s ãŒéƒ¨å±‹åを削除ã—ã¾ã—ãŸ</string> - <string name="notice_room_topic_removed">%1$s ãŒãƒ«ãƒ¼ãƒ トピックを削除ã—ã¾ã—ãŸ</string> - <string name="notice_profile_change_redacted">%1$s ãŒãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ« %2$s ã‚’æ›´æ–°ã—ã¾ã—ãŸ</string> - <string name="notice_room_third_party_invite">%1$s 㯠%2$s ã«éƒ¨å±‹ã«å‚åŠ ã™ã‚‹ã‚ˆã†æ‹›å¾…状をé€ã‚Šã¾ã—ãŸ</string> - <string name="notice_room_third_party_registered_invite">%1$sã¯%2$sã®æ‹›å¾…ã‚’å—ã‘入れã¾ã—ãŸ</string> - <string name="notice_crypto_unable_to_decrypt">** 解èªã§ãã¾ã›ã‚“: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">é€ä¿¡è€…ã®ç«¯æœ«ã‹ã‚‰ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®ã‚ーãŒé€ä¿¡ã•ã‚Œã¦ã„ã¾ã›ã‚“。</string> - <string name="could_not_redact">ä¿®æ£ã§ãã¾ã›ã‚“ã§ã—ãŸ</string> - <string name="unable_to_send_message">メッセージをé€ä¿¡ã§ãã¾ã›ã‚“</string> - <string name="message_failed_to_upload">ç”»åƒã®ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ</string> - <string name="network_error">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¨ãƒ©ãƒ¼</string> - <string name="matrix_error">Matrixエラー</string> - <string name="room_error_join_failed_empty_room">ç¾åœ¨ç©ºã®éƒ¨å±‹ã«å†å‚åŠ ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。</string> - <string name="encrypted_message">æš—å·åŒ–ã•ã‚ŒãŸãƒ¡ãƒƒã‚»ãƒ¼ã‚¸</string> - <string name="medium_email">メールアドレス</string> - <string name="medium_phone_number">電話番å·</string> - <string name="notice_room_avatar_changed_by_you">ルームã®ã‚¢ãƒã‚¿ãƒ¼ã‚’変更ã—ã¾ã—ãŸ</string> - <string name="notice_room_avatar_changed">%1$sãŒãƒ«ãƒ¼ãƒ ã®ã‚¢ãƒã‚¿ãƒ¼ã‚’変更ã—ã¾ã—ãŸ</string> - <string name="notice_room_topic_changed_by_you">トピックを%1$sã«å¤‰æ›´ã—ã¾ã—ãŸ</string> - <string name="notice_display_name_removed_by_you">表示åを削除ã—ã¾ã—ãŸ(%1$sã§ã—ãŸ)</string> - <string name="notice_display_name_changed_from_by_you">表示åã‚’%1$sã‹ã‚‰%2$sã«å¤‰æ›´ã—ã¾ã—ãŸ</string> - <string name="notice_display_name_set_by_you">表示åã‚’%1$sã«è¨å®šã—ã¾ã—ãŸ</string> - <string name="notice_avatar_url_changed_by_you">ã‚¢ãƒã‚¿ãƒ¼ã‚’変更ã—ã¾ã—ãŸ</string> - <string name="notice_room_withdraw_by_you">%1$sã®æ‹›å¾…ã‚’å–り下ã’ã¾ã—ãŸ</string> - <string name="notice_room_ban_by_you">%1$sã‚’BANã—ã¾ã—ãŸ</string> - <string name="notice_room_unban_by_you">%1$sã®BANを解除ã—ã¾ã—ãŸ</string> - <string name="notice_room_kick_by_you">%1$sを退出ã•ã›ã¾ã—ãŸ</string> - <string name="notice_room_reject_by_you">招待を拒å¦ã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_leave_by_you">ルームã‹ã‚‰é€€å‡ºã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_leave">%1$sãŒãƒ«ãƒ¼ãƒ ã‹ã‚‰é€€å‡ºã—ã¾ã—ãŸ</string> - <string name="notice_room_leave_by_you">ルームã‹ã‚‰é€€å‡ºã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_join_by_you">å‚åŠ ã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_join">%1$sãŒå‚åŠ ã—ã¾ã—ãŸ</string> - <string name="notice_room_join_by_you">ルームã«å‚åŠ ã—ã¾ã—ãŸ</string> - <string name="notice_room_invite_by_you">%1$sを招待ã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_created_by_you">ディスカッションを作æˆã—ã¾ã—ãŸ</string> - <string name="notice_direct_room_created">%1$sãŒãƒ‡ã‚£ã‚¹ã‚«ãƒƒã‚·ãƒ§ãƒ³ã‚’作æˆã—ã¾ã—ãŸ</string> - <string name="notice_room_created_by_you">ルームを作æˆã—ã¾ã—ãŸ</string> - <string name="notice_room_created">%1$sãŒãƒ«ãƒ¼ãƒ を作æˆã—ã¾ã—ãŸ</string> - <string name="notice_room_invite_no_invitee_by_you">招待</string> - <string name="summary_you_sent_sticker">ステッカーをé€ä¿¡ã—ã¾ã—ãŸã€‚</string> - <string name="summary_you_sent_image">ç”»åƒã‚’é€ä¿¡ã—ã¾ã—ãŸã€‚</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-kab/strings.xml b/matrix-sdk-android/src/main/res/values-kab/strings.xml deleted file mode 100644 index 8b94fad9eb35f3091a2ea486d2bd732876de4960..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-kab/strings.xml +++ /dev/null @@ -1,194 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s t.yuzen tugna.</string> - <string name="summary_you_sent_image">TuzneḠtugna.</string> - <string name="notice_room_invite_no_invitee">Tinubga n %s</string> - <string name="notice_room_invite_no_invitee_by_you">Tinubga-k•m</string> - <string name="notice_room_created">%1$s yesnulfa-d taxxamt</string> - <string name="notice_room_created_by_you">Tesnulfaá¸-d taxxamt-a</string> - <string name="notice_room_invite">%1$s inced-d %2$s</string> - <string name="notice_room_invite_by_you">Tnecdeá¸-d %1$s</string> - <string name="notice_room_invite_you">%1$s inced-ik-id</string> - <string name="notice_room_join">%1$s yedda É£er texxamt</string> - <string name="notice_room_join_by_you">TeddiḠɣer texxamt</string> - <string name="notice_room_leave">%1$s yeǧǧa taxxamt</string> - <string name="notice_room_leave_by_you">TeǧǧiḠtaxxamt</string> - <string name="notice_room_reject">%1$s yugi/tugi tinubga</string> - <string name="notice_room_reject_by_you">TufiḠtinubga</string> - <string name="notice_room_kick">%1$s yessufeÉ£ %2$s</string> - <string name="notice_room_kick_by_you">TessufÉ£eḠ%1$s</string> - <string name="notice_avatar_url_changed_by_you">TbeddleḠavatar-inek·inem</string> - <string name="power_level_admin">Anedbal</string> - <string name="power_level_moderator">AseÉ£yad</string> - <string name="power_level_default">Amezwer</string> - <string name="power_level_custom_no_value">Sagen</string> - <string name="notice_power_level_diff">%1$s seg %2$s É£er %3$s</string> - <string name="message_failed_to_upload">Tegguma ad d-tali tugna</string> - <string name="medium_email">Tansa n yimayl</string> - <string name="summary_user_sent_sticker">%1$s azen astiker.</string> - <string name="summary_you_sent_sticker">TuzneḠamená¹aá¸.</string> - <string name="notice_room_unban">%1$s yekkes agdal i %2$s</string> - <string name="notice_room_unban_by_you">TekkseḠagdal i %1$s</string> - <string name="notice_room_ban">%1$s igdel %2$s</string> - <string name="notice_room_ban_by_you">TgedleḠ%1$s</string> - <string name="notice_room_withdraw">%1$s issefsex tinubga n %2$s</string> - <string name="notice_room_withdraw_by_you">TesfesxeḠtinubga n %1$s</string> - <string name="notice_avatar_url_changed">%1$s ibeddel avatar-is</string> - <string name="notice_display_name_set">%1$s isbadu isem-is i d-ittuseknen É£er %2$s</string> - <string name="notice_display_name_set_by_you">TesbaduḠisem-ik•im i d-ittuseknen É£er %1$s</string> - <string name="notice_display_name_changed_from">%1$s ibeddel isem-is i d-ittuseknen seg %2$s É£er %3$s</string> - <string name="notice_display_name_changed_from_by_you">TbeddleḠisem-ik•im i d-ittuseknen seg %1$s É£er %2$s</string> - <string name="notice_display_name_removed">%1$s yekkes isem-is i d-ittuseknen (yella %2$s)</string> - <string name="notice_display_name_removed_by_you">TekkseḠisem-ik·im yettwaskanen (d %1$s)</string> - <string name="notice_room_topic_changed">%1$s isnifel asentel s: %2$s</string> - <string name="notice_room_topic_changed_by_you">TesnifleḠasentel s: %1$s</string> - <string name="notice_room_avatar_changed">%1$s ibeddel avaá¹ar n texxamt</string> - <string name="notice_room_avatar_changed_by_you">TbeddleḠavaá¹ar n texxamt</string> - <string name="notice_room_name_changed">%1$s ibeddel isem n texxamt s: %2$s</string> - <string name="notice_room_name_changed_by_you">TbeddleḠisem n texxamt s: %1$s</string> - <string name="notice_placed_video_call">%s isÉ›edda siwel s tvidyut.</string> - <string name="notice_placed_video_call_by_you">TesÉ›eddaḠsiwel s tvidyut.</string> - <string name="notice_placed_voice_call">%s isÉ›edda asiwel s taÉ£ect.</string> - <string name="notice_placed_voice_call_by_you">TesÉ›eddaḠsiwel s taÉ£ect.</string> - <string name="notice_call_candidates">%s yuzen isefka i usbadu n usiwel.</string> - <string name="notice_call_candidates_by_you">TuzneḠisefka i usbadu n usiwel.</string> - <string name="notice_answered_call">%s yerra É£ef usiwel.</string> - <string name="notice_answered_call_by_you">TerriḠɣef usiwel.</string> - <string name="notice_ended_call">%s iḥbes asiwel.</string> - <string name="notice_ended_call_by_you">TḥebseḠasiwel.</string> - <string name="notice_room_visibility_invited">meṛṛa iÉ›eggalen n texxamt, segmi ara d-ttwanecden.</string> - <string name="notice_room_visibility_joined">meṛṛa iÉ›eggalen n texamt, segmi ara d-rnun.</string> - <string name="notice_room_visibility_shared">meṛṛa iÉ›eggalen n texxamt.</string> - <string name="notice_room_visibility_world_readable">yal yiwen.</string> - <string name="notice_room_visibility_unknown">arussin (%s).</string> - <string name="notice_end_to_end">%1$s isermed awgelhen seg yixef É£er yixef (%2$s)</string> - <string name="notice_end_to_end_by_you">TesremdeḠawgelhen seg yixef É£er yixef (%1$s)</string> - <string name="notice_room_update">%s ileqqem taxxamt-a.</string> - <string name="notice_room_update_by_you">TleqqmeḠtaxxamt-a.</string> - <string name="notice_requested_voip_conference">%1$s isuter-d asarag VoIP</string> - <string name="notice_requested_voip_conference_by_you">Tsutreá¸-d asarag VoIP</string> - <string name="notice_voip_started">Asarag VoIP yebda</string> - <string name="notice_voip_finished">Asarag VoIP yekfa</string> - <string name="notice_avatar_changed_too">(avatar daÉ£en ibeddel)</string> - <string name="notice_room_name_removed">%1$s yekkes isem n texxamt</string> - <string name="notice_room_name_removed_by_you">TekkseḠisem n texxamt</string> - <string name="notice_room_topic_removed">%1$s yekkes asentel n texxamt</string> - <string name="notice_room_topic_removed_by_you">TekkseḠasentel n texxamt</string> - <string name="notice_room_avatar_removed">%1$s yekkes avatar n texxamt</string> - <string name="notice_room_avatar_removed_by_you">TekkseḠavatar n texxamt</string> - <string name="notice_event_redacted">Izen ittwakkes</string> - <string name="notice_event_redacted_by">Izen ittwakkes sÉ£ur %1$s</string> - <string name="notice_event_redacted_with_reason">Izen ittwakkes [tamentilt: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Izen ittwakkes sÉ£ur %1$s [tamentilt: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s ileqqem amaÉ£nu-ines %2$s</string> - <string name="notice_profile_change_redacted_by_you">TleqqmeḠamaÉ£nu-inek•inem %1$s</string> - <string name="notice_room_third_party_invite">%1$s yuzen tinubga i %2$s akken ad yeddu É£er texxamt</string> - <string name="notice_room_third_party_invite_by_you">TuzneḠtinubga i %1$s akken ad yeddu É£er texxamt</string> - <string name="notice_room_third_party_registered_invite">%1$s iqbel tinubga i %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">TqebleḠtinubga i %1$s</string> - <string name="notice_widget_added">%1$s yerna awiǧit %2$s</string> - <string name="notice_widget_added_by_you">TerniḠawiǧit %1$s</string> - <string name="notice_widget_removed">%1$s yekkes awiǧit %2$s</string> - <string name="notice_widget_removed_by_you">TekkseḠawiǧit %1$s</string> - <string name="notice_widget_modified">%1$s ibeddel awiǧit %2$s</string> - <string name="notice_widget_modified_by_you">TbeddleḠawiǧit %1$s</string> - <string name="power_level_custom">Sagen (%1$d)</string> - <string name="notice_power_level_changed_by_you">TbeddleḠaswir n tezmert n %1$s.</string> - <string name="notice_power_level_changed">%1$s ibeddel aswir n tezmert n %2$s.</string> - <string name="notice_crypto_unable_to_decrypt">** Awgelhen d awezÉ£i: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Ibenk n umazan ur aÉ£-d-yuzin ara tisura i yizen-a.</string> - <string name="unable_to_send_message">Tuzna n yizen d tawezÉ£it</string> - <string name="network_error">Tuccá¸a deg uẓeá¹á¹a</string> - <string name="matrix_error">Tuccá¸a deg Matrix</string> - <string name="notice_made_future_room_visibility">%1$s iga amazray n texxamyt i d-iteddun yettban i %2$s</string> - <string name="notice_made_future_room_visibility_by_you">TgiḠamazray n texxamyt i d-iteddun yettban i %1$s</string> - <string name="notice_room_third_party_revoked_invite">%1$s issefsax tinubga i %2$s i wakken ad d-yekcem É£er texxamt</string> - <string name="notice_room_third_party_revoked_invite_by_you">TesfesxeḠtinubga i %1$s i wakken ad d-yernu É£er texxamt</string> - <string name="room_error_join_failed_empty_room">D awezÉ£i tura ad nales ad nuÉ£al É£er texxamt tilemt.</string> - <string name="encrypted_message">Izen yettwawgelhen</string> - <string name="medium_phone_number">Uá¹á¹un n tiliÉ£ri</string> - <string name="room_displayname_invite_from">Tinubga sÉ£ur %s</string> - <string name="room_displayname_room_invite">Tinubga É£er texxamt</string> - <string name="room_displayname_two_members">%1$s d %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s d 1 wayeá¸</item> - <item quantity="other">%1$s d %2$d wiyaá¸</item> - </plurals> - <string name="notice_end_to_end_unknown_algorithm_by_you">TremdeḠawgelhen seg yixef É£er yixef (alguritm %1$s ur yettwassen ara).</string> - <string name="key_verification_request_fallback_message">%s isuter-d ad isenqed tasarut-ik·im, maca amsaÉ£-ik·im ur issefrak ara asenqed n tsura deg yidiwenniyen. Ilaq-ak·am useqdec asenqed iqdim n tsura i usenqed n tsura.</string> - <string name="room_displayname_empty_room">Taxxamt tilemt</string> - <string name="initial_sync_start_importing_account">Amtawi n tazwara: -\nAktar n umiá¸an…</string> - <string name="initial_sync_start_importing_account_crypto">Amtawi n tazwara: -\nAktar n uwgelhen</string> - <string name="initial_sync_start_importing_account_rooms">Amtawi n tazwara: -\nAktar n texxamin</string> - <string name="initial_sync_start_importing_account_joined_rooms">Amtawi n tazwara: -\nAktar n texxamin iÉ£er terniá¸</string> - <string name="initial_sync_start_importing_account_invited_rooms">Amtawi n tazwara: -\nAktar n texxamin iÉ£er tettwanecdeá¸</string> - <string name="initial_sync_start_importing_account_left_rooms">Amtawi n tazwara: -\nAktar n texxamin i teǧǧiá¸</string> - <string name="initial_sync_start_importing_account_groups">Amtawi n tazwara: -\nAktar n tmezdagnutin</string> - <string name="initial_sync_start_importing_account_data">Amtawi n tazwara: -\nAktar n yisefka n umiá¸an</string> - <string name="event_status_sending_message">Tuzzna n yizen…</string> - <string name="notice_room_invite_no_invitee_with_reason">Tinubga n %1$s. Tamentilt: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Tinubga-k•m. Tamentilt: %1$s</string> - <string name="notice_room_invite_with_reason">%1$s inced %2$s. Tamentilt: %3$s</string> - <string name="notice_room_invite_with_reason_by_you">TnecdeḠ%1$s. Tamentilt: %2$s</string> - <string name="notice_room_invite_you_with_reason">%1$s inced-ik•ikem. Tamentilt: %2$s</string> - <string name="notice_room_join_with_reason">%1$s yedda É£er texxamt. Tamentilt: %2$s</string> - <string name="notice_room_join_with_reason_by_you">TeddiḠɣer texxamt. Tamentilt: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s yeǧǧa taxxamt. Tamentilt: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">TeǧǧiḠtaxxamt. Tamentilt: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s yugi tinubga. Tamentilt: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">TugiḠtinubga. Tamentilt: %1$s</string> - <string name="notice_room_kick_with_reason">%1$s yessufeÉ£ %2$s. Tamentilt: %3$s</string> - <string name="notice_room_kick_with_reason_by_you">TessufÉ£eḠ%1$s. Tamentilt: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s yekkes agdal i %2$s. Tamentilt: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">TekkseḠagdal i %1$s. Tamentilt: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s igdel %2$s. Tamentilt: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">TgedleḠ%1$s. Tamentilt: %2$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s yuzen tinubga i %2$s akken ad yeddu É£er texxamt. Tamentilt: %3$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">TuzneḠtinubga i %1$s iwakken ad yeddu É£er texxamt. Tamentilt: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s iqbel tinubga i %2$s. Tamentilt: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">TqebleḠtinubga i %1$s. Tamentilt: %2$s</string> - <string name="notice_room_withdraw_with_reason">%1$s issefsex tinubga n %2$s. Tamentilt: %3$s</string> - <string name="notice_room_withdraw_with_reason_by_you">TesfesxeḠtinubga n %1$s. Tamentilt: %2$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s yerna %2$s d tansa i texxamt-a.</item> - <item quantity="other">%1$s yerna %2$s d tansiwin i texxamt-a.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">TerniḠ%1$s d tansa i texxamt-a.</item> - <item quantity="other">TerniḠ%1$s d tansiwin i texxamt-a.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s yekkes %2$s am tansa i texxamt-a.</item> - <item quantity="other">%1$s yekkes %3$s am tansiwin i texxamt-a.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">TekkseḠ%1$s am tansa i texxamt-a.</item> - <item quantity="other">TekkseḠ%1$s am tansiwin i texxamt-a.</item> - </plurals> - <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> - <string name="notice_room_canonical_alias_set_by_you">TesbaduḠ%1$s am tansa tagejdant i texxamt-a.</string> - <string name="notice_room_canonical_alias_unset">%1$s yekkes tansa tagejdant i texxamt-a.</string> - <string name="notice_room_canonical_alias_unset_by_you">TekkseḠtansa tagejdant i texxamt-a.</string> - <string name="notice_room_guest_access_can_join">%1$s isireg inebgawen ad ddun É£er texxamt.</string> - <string name="notice_room_guest_access_can_join_by_you">TsirgeḠinebgawen ad ddun É£er texxamt.</string> - <string name="notice_room_guest_access_forbidden">%1$s issewḥel inebgawen iwakken ur tteddun ara É£er texxamt.</string> - <string name="notice_room_guest_access_forbidden_by_you">TesweḥleḠinebgawen iwakken ur tteddun ara É£er texxamt.</string> - <string name="notice_end_to_end_ok">%1$s yermed awgelhen seg yixef É£er yixef.</string> - <string name="notice_end_to_end_ok_by_you">TremdeḠawgelhen seg yixef É£er yixef.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s yermed awgelhen seg yixef É£er yixef (alguritm %2$s ur yettwassen ara).</string> - <string name="clear_timeline_send_queue">SfeḠtabdart n uraǧu n tuzzna</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s issefsex tinubga n %2$s i tmerniwt É£er texxamt. Tamentilt: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">TesfesxeḠtinubga n %1$s i tmerna É£er texxamt. Tamentilt: %2$s</string> - <string name="could_not_redact">Yegguma ad yaru</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-ko/strings.xml b/matrix-sdk-android/src/main/res/values-ko/strings.xml deleted file mode 100644 index eee67628ebeb16deea3a02e4f8a8da576a83d100..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-ko/strings.xml +++ /dev/null @@ -1,101 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="notice_room_invite_no_invitee">%së‹˜ì˜ ì´ˆëŒ€</string> - <string name="summary_user_sent_image">%1$së‹˜ì´ ì‚¬ì§„ì„ ë³´ëƒˆìŠµë‹ˆë‹¤.</string> - <string name="summary_user_sent_sticker">%1$së‹˜ì´ ìŠ¤í‹°ì»¤ë¥¼ 보냈습니다.</string> - - <string name="notice_room_invite">%1$së‹˜ì´ %2$së‹˜ì„ ì´ˆëŒ€í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_invite_you">%1$së‹˜ì´ ë‹¹ì‹ ì„ ì´ˆëŒ€í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_join">%1$së‹˜ì´ ì°¸ê°€í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_leave">%1$së‹˜ì´ ë– ë‚¬ìŠµë‹ˆë‹¤</string> - <string name="notice_room_reject">%1$së‹˜ì´ ì´ˆëŒ€ë¥¼ 거부했습니다</string> - <string name="notice_room_kick">%1$së‹˜ì´ %2$së‹˜ì„ ì¶”ë°©í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_unban">%1$së‹˜ì´ %2$së‹˜ì˜ ì¶œìž… 금지를 풀었습니다</string> - <string name="notice_room_ban">%1$së‹˜ì´ %2$së‹˜ì„ ì¶œìž… 금지했습니다</string> - <string name="notice_room_withdraw">%1$së‹˜ì´ %2$së‹˜ì˜ ì´ˆëŒ€ë¥¼ 취소했습니다</string> - <string name="notice_avatar_url_changed">%1$së‹˜ì´ ì•„ë°”íƒ€ë¥¼ 변경했습니다</string> - <string name="notice_display_name_set">%1$së‹˜ì´ í‘œì‹œ ì´ë¦„ì„ %2$s(으)ë¡œ ì„¤ì •í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_display_name_changed_from">%1$së‹˜ì´ í‘œì‹œ ì´ë¦„ì„ %2$sì—ì„œ %3$s(으)ë¡œ 변경했습니다</string> - <string name="notice_display_name_removed">%1$së‹˜ì´ í‘œì‹œ ì´ë¦„ì„ ì‚ì œí–ˆìŠµë‹ˆë‹¤ (%2$s)</string> - <string name="notice_room_topic_changed">%1$së‹˜ì´ ì£¼ì œë¥¼ 다ìŒìœ¼ë¡œ 변경했습니다: %2$s</string> - <string name="notice_room_name_changed">%1$së‹˜ì´ ë°© ì´ë¦„ì„ ë‹¤ìŒìœ¼ë¡œ 변경했습니다: %2$s</string> - <string name="notice_placed_video_call">%së‹˜ì´ ì˜ìƒ 통화를 걸었습니다.</string> - <string name="notice_placed_voice_call">%së‹˜ì´ ìŒì„± 통화를 걸었습니다.</string> - <string name="notice_answered_call">%së‹˜ì´ ì „í™”ë¥¼ 받았습니다.</string> - <string name="notice_ended_call">%së‹˜ì´ ì „í™”ë¥¼ ëŠì—ˆìŠµë‹ˆë‹¤.</string> - <string name="notice_made_future_room_visibility">%1$së‹˜ì´ ì´í›„ %2$sì—게 ë°© 기ë¡ì„ 공개했습니다</string> - <string name="notice_room_visibility_invited">ì´ˆëŒ€ëœ ì‹œì 부터 ëª¨ë“ ë°© 구성ì›</string> - <string name="notice_room_visibility_joined">들어온 ì‹œì 부터 ëª¨ë“ ë°© 구성ì›</string> - <string name="notice_room_visibility_shared">ëª¨ë“ ë°© 구성ì›</string> - <string name="notice_room_visibility_world_readable">누구나.</string> - <string name="notice_room_visibility_unknown">ì•Œ 수 ì—†ìŒ (%s).</string> - <string name="notice_end_to_end">%1$së‹˜ì´ ì¢…ë‹¨ê°„ 암호화를 켰습니다 (%2$s)</string> - <string name="notice_room_update">%së‹˜ì´ ë°©ì„ ì—…ê·¸ë ˆì´ë“œí–ˆìŠµë‹ˆë‹¤.</string> - - <string name="notice_requested_voip_conference">%1$së‹˜ì´ VoIP 회ì˜ë¥¼ ìš”ì²í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_voip_started">VoIP 회ì˜ê°€ 시작했습니다</string> - <string name="notice_voip_finished">VoIP 회ì˜ê°€ ë났습니다</string> - - <string name="notice_avatar_changed_too">(ì•„ë°”íƒ€ë„ ë³€ê²½ë¨)</string> - <string name="notice_room_name_removed">%1$së‹˜ì´ ë°© ì´ë¦„ì„ ì‚ì œí–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_topic_removed">%1$së‹˜ì´ ë°© ì£¼ì œë¥¼ ì‚ì œí–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_event_redacted">메시지가 ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤</string> - <string name="notice_event_redacted_by">메시지가 %1$së‹˜ì— ì˜í•´ ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤</string> - <string name="notice_event_redacted_with_reason">메시지가 ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤ [ì´ìœ : %1$s]</string> - <string name="notice_event_redacted_by_with_reason">메시지가 %1$së‹˜ì— ì˜í•´ ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤ [ì´ìœ : %2$s]</string> - <string name="notice_profile_change_redacted">%1$së‹˜ì´ í”„ë¡œí•„ %2$sì„(를) ì—…ë°ì´íŠ¸í–ˆìŠµë‹ˆë‹¤</string> - <string name="notice_room_third_party_invite">%1$së‹˜ì´ %2$s님ì—게 ë°© 초대를 보냈습니다</string> - <string name="notice_room_third_party_registered_invite">%1$së‹˜ì´ %2$sì˜ ì´ˆëŒ€ë¥¼ 수ë½í–ˆìŠµë‹ˆë‹¤</string> - - <string name="notice_crypto_unable_to_decrypt">** 암호를 ë³µí˜¸í™”í• ìˆ˜ ì—†ìŒ: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">ë°œì‹ ì¸ì˜ 기기ì—ì„œ ì´ ë©”ì‹œì§€ì˜ í‚¤ë¥¼ 보내지 않았습니다.</string> - - <string name="could_not_redact">ê²€ì—´í• ìˆ˜ 없습니다</string> - <string name="unable_to_send_message">메시지를 보낼 수 없습니다</string> - - <string name="message_failed_to_upload">사진 ì—…ë¡œë“œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤</string> - - <string name="network_error">ë„¤íŠ¸ì›Œí¬ ì˜¤ë¥˜</string> - <string name="matrix_error">Matrix 오류</string> - - <string name="room_error_join_failed_empty_room">현재 빈 ë°©ì— ë‹¤ì‹œ 들어갈 수 없습니다.</string> - - <string name="encrypted_message">ì•”í˜¸í™”ëœ ë©”ì‹œì§€</string> - - <string name="medium_email">ì´ë©”ì¼ ì£¼ì†Œ</string> - <string name="medium_phone_number">ì „í™”ë²ˆí˜¸</string> - - <string name="room_displayname_invite_from">%sì—ì„œ 초대함</string> - <string name="room_displayname_room_invite">ë°© 초대</string> - - <string name="room_displayname_two_members">%1$s님과 %2$s님</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$s님 외 %2$d명</item> - </plurals> - - <string name="room_displayname_empty_room">빈 ë°©</string> - - <string name="initial_sync_start_importing_account">초기 ë™ê¸°í™”: -\nê³„ì • ê°€ì ¸ì˜¤ëŠ” 중…</string> - <string name="initial_sync_start_importing_account_crypto">초기 ë™ê¸°í™”: -\n암호 ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_rooms">초기 ë™ê¸°í™”: -\në°© ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_joined_rooms">초기 ë™ê¸°í™”: -\n들어간 ë°© ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_invited_rooms">초기 ë™ê¸°í™”: -\nì´ˆëŒ€ë°›ì€ ë°© ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_left_rooms">초기 ë™ê¸°í™”: -\në– ë‚œ ë°© ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_groups">초기 ë™ê¸°í™”: -\n커뮤니티 ê°€ì ¸ì˜¤ëŠ” 중</string> - <string name="initial_sync_start_importing_account_data">초기 ë™ê¸°í™”: -\nê³„ì • ë°ì´í„° ê°€ì ¸ì˜¤ëŠ” 중</string> - - <string name="event_status_sending_message">메시지 보내는 중…</string> - <string name="clear_timeline_send_queue">ì „ì†¡ 대기 ì—´ 지우기</string> - - <string name="notice_room_third_party_revoked_invite">%1$së‹˜ì´ %2$s님ì—게 ë°©ì— ì°¸ê°€í•˜ë¼ê³ 보낸 초대를 취소했습니다</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-lt/strings.xml b/matrix-sdk-android/src/main/res/values-lt/strings.xml deleted file mode 100644 index 4b07e1ec8818f64ceea10de8bfdff9fb0d6cf3cc..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-lt/strings.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s iÅ¡siuntÄ— vaizdÄ….</string> - <string name="summary_user_sent_sticker">%1$s iÅ¡siuntÄ— lipdukÄ….</string> - <string name="notice_room_invite_no_invitee">%s pakvietimas</string> - <string name="notice_room_join_by_you">JÅ«s prisijungÄ—te prie kambario</string> - <string name="notice_room_join">%1$s prisijungÄ— prie kambario</string> - <string name="notice_room_invite_you">%1$s pakvietÄ— jus</string> - <string name="notice_room_invite_by_you">JÅ«s pakvietÄ—te %1$s</string> - <string name="notice_room_invite">%1$s pakvietÄ— %2$s</string> - <string name="notice_direct_room_created_by_you">JÅ«s sukÅ«rÄ—te diskusijÄ…</string> - <string name="notice_direct_room_created">%1$s sukÅ«rÄ— diskusijÄ…</string> - <string name="notice_room_created_by_you">JÅ«s sukÅ«rÄ—te kambarį</string> - <string name="notice_room_created">%1$s sukÅ«rÄ— kambarį</string> - <string name="notice_room_invite_no_invitee_by_you">JÅ«sų pakvietimas</string> - <string name="summary_you_sent_sticker">JÅ«s iÅ¡siuntÄ—te lipdukÄ….</string> - <string name="summary_you_sent_image">JÅ«s iÅ¡siuntÄ—te vaizdÄ….</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-lv/strings.xml b/matrix-sdk-android/src/main/res/values-lv/strings.xml deleted file mode 100644 index ec107b47d6bd385cd63704e78d060e8e3f302f8c..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-lv/strings.xml +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s nosÅ«tÄ«ja attÄ“lu.</string> - <string name="notice_room_invite_no_invitee">%s\'s uzaicinÄjums</string> - <string name="notice_room_invite">%1$s uzaicinÄja %2$s</string> - <string name="notice_room_invite_you">%1$s uzaicinÄja tevi</string> - <string name="notice_room_join">%1$s pievienojÄs</string> - <string name="notice_room_leave">%1$s atstÄja</string> - <string name="notice_room_reject">%1$s noraidÄ«ja uzaicinÄjumu</string> - <string name="notice_room_kick">%1$s \"izspÄ“ra\" ÄrÄ %2$s</string> - <string name="notice_room_unban">%1$s atbanoja (atcÄ“la pieejas liegumu) %2$s</string> - <string name="notice_room_ban">%1$s liedza pieeju (banoja) %2$s</string> - <string name="notice_room_withdraw">%1$s atsauca %2$s uzaicinÄjumu</string> - <string name="notice_avatar_url_changed">%1$s nomainÄ«ja profila attÄ“lu</string> - <string name="notice_display_name_set">%1$s uzstÄdÄ«ja redzamo vÄrdu uz %2$s</string> - <string name="notice_display_name_changed_from">%1$s nomainÄ«ja redzamo vÄrdu no %2$s uz %3$s</string> - <string name="notice_display_name_removed">%1$s dzÄ“sa savu redzamo vÄrdu (%2$s)</string> - <string name="notice_room_topic_changed">%1$s nomainÄ«ja tÄ“mas nosaukumu uz: %2$s</string> - <string name="notice_room_name_changed">%1$s nomainÄ«ja istabas nosaukumu uz: %2$s</string> - <string name="notice_placed_video_call">%s veica video zvanu.</string> - <string name="notice_placed_voice_call">%s veica audio zvanu.</string> - <string name="notice_answered_call">%s atbildÄ“ja zvanam.</string> - <string name="notice_ended_call">%s beidza zvanu.</string> - <string name="notice_made_future_room_visibility">%1$s padarÄ«ja istabas nÄkamo ziņu vÄ“sturi redzamu %2$s</string> - <string name="notice_room_visibility_invited">visi istabas biedri no brīža, kad tika uzaicinÄti.</string> - <string name="notice_room_visibility_joined">visi istabas biedri no brīža, kad tika pievienojuÅ¡ies.</string> - <string name="notice_room_visibility_shared">visi istabas biedri.</string> - <string name="notice_room_visibility_world_readable">ikviens.</string> - <string name="notice_room_visibility_unknown">nezinÄms (%s).</string> - <string name="notice_end_to_end">%1$s ieslÄ“dza ierÄ«ce-ierÄ«ce Å¡ifrÄ“Å¡anu (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s vÄ“las VoIP konferenci</string> - <string name="notice_voip_started">VoIP konference sÄkusies</string> - <string name="notice_voip_finished">VoIP konference ir beigusies</string> - <string name="notice_avatar_changed_too">(arÄ« profila attÄ“ls mainÄ«jÄs)</string> - <string name="notice_room_name_removed">%1$s dzÄ“sa istabas nosaukumu</string> - <string name="notice_room_topic_removed">%1$s dzÄ“sa istabas tÄ“mas nosaukumu</string> - <string name="notice_profile_change_redacted">%1$s atjaunoja profila informÄciju %2$s</string> - <string name="notice_room_third_party_invite">%1$s nosÅ«tÄ«ja uzaicinÄjumu %2$s pievienoties istabai</string> - <string name="notice_room_third_party_registered_invite">%1$s apstiprinÄja uzaicinÄjumu priekÅ¡ %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Nav iespÄ“jams atkodÄ“t: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">SÅ«tÄ«tÄja ierÄ«ce mums nenosÅ«tÄ«ja atslÄ“gas priekÅ¡ Å¡Ä«s ziņas.</string> - <string name="could_not_redact">NevarÄ“ja rediģēt</string> - <string name="unable_to_send_message">Nav iespÄ“jams nosÅ«tÄ«t ziņu</string> - <string name="message_failed_to_upload">NeizdevÄs augÅ¡uplÄdÄ“t attÄ“lu</string> - <string name="network_error">TÄ«kla kļūda</string> - <string name="matrix_error">Matrix kļūda</string> - <string name="room_error_join_failed_empty_room">Å obrÄ«d nav iespÄ“jams atkÄrtoti pievienoties tukÅ¡ai istabai.</string> - <string name="encrypted_message">Å ifrÄ“ta ziņa</string> - <string name="medium_email">Epasta adrese</string> - <string name="medium_phone_number">Telefona numurs</string> - <string name="room_displayname_invite_from">UzaicinÄjums no %s</string> - <string name="room_displayname_room_invite">UzaicinÄjums uz istabu</string> - <string name="room_displayname_two_members">%1$s un %2$s</string> - <string name="room_displayname_empty_room">TukÅ¡a istaba</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="zero">%1$s un 1 cits</item> - <item quantity="one">%1$s un %2$d citi</item> - <item quantity="other">%1$s un %2$d citu</item> - </plurals> - <string name="notice_display_name_changed_from_by_you">Tu nomainÄ«ji savu attÄ“lojamo vÄrdu no %1$s uz %2$s</string> - <string name="notice_display_name_set_by_you">Tu nomainÄ«jis savu attÄ“lojamo vÄrdu uz %1$s</string> - <string name="notice_avatar_url_changed_by_you">Tu nomainÄ«ji savu avataru</string> - <string name="notice_room_withdraw_by_you">Tu atsauci %1$s uzaicinÄjumu</string> - <string name="notice_room_ban_by_you">Tu nobanoji %1$s</string> - <string name="notice_room_unban_by_you">Tu atbanoji %1$s</string> - <string name="notice_room_kick_by_you">Tu izspÄ“ri %1$s</string> - <string name="notice_room_reject_by_you">Tu noraidÄ«ji uzaicinÄjumu</string> - <string name="notice_direct_room_leave_by_you">Tu pameti telpu</string> - <string name="notice_direct_room_leave">%1$s atstÄja telpu</string> - <string name="notice_room_leave_by_you">Tu atstÄji telpu</string> - <string name="notice_direct_room_join_by_you">Tu pievienojies</string> - <string name="notice_direct_room_join">%1$s pievienojÄs telpai</string> - <string name="notice_room_join_by_you">Tu pievienojies telpai</string> - <string name="notice_room_invite_by_you">Tu uzaicinÄji %1$s</string> - <string name="notice_direct_room_created_by_you">Tu izveidoji apspriedi (diskusiju)</string> - <string name="notice_direct_room_created">%1$s izveidoja apspriedi (diskusiju)</string> - <string name="notice_room_created_by_you">Tu izveidoji istabu</string> - <string name="notice_room_created">%1$s izveidoja telpu</string> - <string name="notice_room_invite_no_invitee_by_you">Tavs uzaicinÄjums</string> - <string name="summary_you_sent_sticker">Tu nosÅ«tÄ«ji uzlÄ«mi/lipekli.</string> - <string name="summary_user_sent_sticker">%1$s nosÅ«tÄ«ja uzlÄ«mi/lipekli.</string> - <string name="summary_you_sent_image">Tu nosÅ«tÄ«ji attÄ“lu.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-nb/strings.xml b/matrix-sdk-android/src/main/res/values-nb/strings.xml deleted file mode 100644 index 07cf4226e02eb7338bca42a647227c62c0fc1d4d..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-nb/strings.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="notice_direct_room_created_by_you">Du opprettet diskusjonen</string> - <string name="notice_room_created_by_you">Du opprettet rommet</string> - <string name="notice_room_invite_no_invitee_by_you">Invitasjonen din</string> - <string name="summary_you_sent_sticker">Du sendte et klistremerke.</string> - <string name="summary_user_sent_sticker">%1$s sendte et klistremerke.</string> - <string name="summary_you_sent_image">Du sendte et bilde.</string> - <string name="summary_user_sent_image">%1$s sendte et bilde.</string> - <string name="summary_message">%1$s: %2$s</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-nl/strings.xml b/matrix-sdk-android/src/main/res/values-nl/strings.xml deleted file mode 100644 index 1b05052ba6c24ab2eb9abe2ef7adf3aa04913b9a..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-nl/strings.xml +++ /dev/null @@ -1,151 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s heeft een afbeelding gestuurd.</string> - - <string name="notice_room_invite_no_invitee">Uitnodiging van %s</string> - <string name="notice_room_invite">%1$s heeft %2$s uitgenodigd</string> - <string name="notice_room_invite_you">%1$s heeft u uitgenodigd</string> - <string name="notice_room_join">%1$s neemt nu deel aan het gesprek</string> - <string name="notice_room_leave">%1$s heeft het gesprek verlaten</string> - <string name="notice_room_reject">%1$s heeft de uitnodiging geweigerd</string> - <string name="notice_room_kick">%1$s heeft %2$s uit het gesprek verwijderd</string> - <string name="notice_room_unban">%1$s heeft %2$s ontbannen</string> - <string name="notice_room_ban">%1$s heeft %2$s verbannen</string> - <string name="notice_room_withdraw">%1$s heeft de uitnodiging van %2$s ingetrokken</string> - <string name="notice_avatar_url_changed">%1$s heeft zijn/haar avatar aangepast</string> - <string name="notice_display_name_set">%1$s heeft zijn/haar naam aangepast naar %2$s</string> - <string name="notice_display_name_changed_from">%1$s heeft zijn/haar naam aangepast van %2$s naar %3$s</string> - <string name="notice_display_name_removed">%1$s heeft zijn/haar naam verwijderd (%2$s)</string> - <string name="notice_room_topic_changed">%1$s heeft het onderwerp veranderd naar: %2$s</string> - <string name="notice_room_name_changed">%1$s heeft de gespreksnaam veranderd naar: %2$s</string> - <string name="notice_placed_video_call">%s heeft een video-oproep gemaakt.</string> - <string name="notice_placed_voice_call">%s heeft een spraakoproep gemaakt.</string> - <string name="notice_answered_call">%s heeft de oproep beantwoord.</string> - <string name="notice_ended_call">%s heeft opgehangen.</string> - <string name="notice_made_future_room_visibility">%1$s heeft de toekomstige gespreksgeschiedenis zichtbaar gemaakt voor %2$s</string> - <string name="notice_room_visibility_invited">alle deelnemers aan het gesprek, vanaf het punt dat ze zijn uitgenodigd.</string> - <string name="notice_room_visibility_joined">alle deelnemers aan het gesprek, vanaf het punt dat ze zijn toegetreden.</string> - <string name="notice_room_visibility_shared">alle deelnemers aan het gesprek.</string> - <string name="notice_room_visibility_world_readable">iedereen.</string> - <string name="notice_room_visibility_unknown">onbekend (%s).</string> - <string name="notice_end_to_end">%1$s heeft eind-tot-eind-versleuteling aangezet (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s heeft een VoIP-vergadering aangevraagd</string> - <string name="notice_voip_started">VoIP-vergadering gestart</string> - <string name="notice_voip_finished">VoIP-vergadering gestopt</string> - - <string name="notice_avatar_changed_too">(avatar is ook veranderd)</string> - <string name="notice_room_name_removed">%1$s heeft de gespreksnaam verwijderd</string> - <string name="notice_room_topic_removed">%1$s heeft het gespreksonderwerp verwijderd</string> - <string name="notice_profile_change_redacted">%1$s heeft zijn/haar profiel %2$s bijgewerkt</string> - <string name="notice_room_third_party_invite">%1$s heeft een uitnodiging naar %2$s gestuurd om het gesprek toe te treden</string> - <string name="notice_room_third_party_registered_invite">%1$s heeft de uitnodiging voor %2$s aanvaard</string> - - <string name="notice_crypto_unable_to_decrypt">** Kan niet ontsleutelen: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Het apparaat van de afzender heeft geen sleutels voor dit bericht gestuurd.</string> - - <!-- Room Screen --> - <string name="could_not_redact">Kon niet verwijderd worden</string> - <string name="unable_to_send_message">Kan bericht niet verzenden</string> - - <string name="message_failed_to_upload">Uploaden van de afbeelding mislukt</string> - - <!-- general errors --> - <string name="network_error">Netwerkfout</string> - <string name="matrix_error">Matrix-fout</string> - - <!-- Home Screen --> - - <!-- Last seen time --> - - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Het is momenteel niet mogelijk om een leeg gesprek opnieuw toe te treden.</string> - - <string name="encrypted_message">Versleuteld bericht</string> - - <!-- medium friendly name --> - <string name="medium_email">E-mailadres</string> - <string name="medium_phone_number">Telefoonnummer</string> - - <string name="summary_user_sent_sticker">%1$s heeft een sticker gestuurd.</string> - - <!-- Room display name --> - <string name="room_displayname_invite_from">Uitnodiging van %s</string> - <string name="room_displayname_room_invite">Gespreksuitnodiging</string> - <string name="room_displayname_two_members">%1$s en %2$s</string> - <string name="room_displayname_empty_room">Leeg gesprek</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s en 1 andere</item> - <item quantity="other">%1$s en %2$d anderen</item> - </plurals> - - - <string name="notice_event_redacted">Bericht verwijderd</string> - <string name="notice_event_redacted_by">Bericht verwijderd door %1$s</string> - <string name="notice_event_redacted_with_reason">Bericht verwijderd [reden: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Bericht verwijderd door %1$s [reden: %2$s]</string> - - <string name="initial_sync_start_importing_account">Initiële synchronisatie: -\nAccount wordt geïmporteerd…</string> - <string name="initial_sync_start_importing_account_crypto">Initiële synchronisatie: -\nCrypto wordt geïmporteerd</string> - <string name="initial_sync_start_importing_account_rooms">Initiële synchronisatie: -\nGesprekken worden geïmporteerd</string> - <string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisatie: -\nDeelgenomen gesprekken worden geïmporteerd</string> - <string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisatie: -\nUitgenodigde gesprekken worden geïmporteerd</string> - <string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisatie: -\nVerlaten gesprekken worden geïmporteerd</string> - <string name="initial_sync_start_importing_account_groups">Initiële synchronisatie: -\nGemeenschappen worden geïmporteerd</string> - <string name="initial_sync_start_importing_account_data">Initiële synchronisatie: -\nAccountgegevens worden geïmporteerd</string> - - <string name="notice_room_update">%s heeft dit gesprek opgewaardeerd.</string> - - <string name="event_status_sending_message">Bericht wordt verstuurd…</string> - <string name="clear_timeline_send_queue">Uitgaande wachtrij legen</string> - - <string name="notice_room_third_party_revoked_invite">%1$s heeft de uitnodiging voor %2$s om het gesprek toe te treden ingetrokken</string> - <string name="notice_room_invite_no_invitee_with_reason">Uitnodiging van %1$s. Reden: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s heeft %2$s uitgenodigd. Reden: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s heeft u uitgenodigd. Reden: %2$s</string> - <string name="notice_room_join_with_reason">%1$s neemt nu deel. Reden: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s is weggegaan. Reden: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s heeft de uitnodiging geweigerd. Reden: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s heeft %2$s verwijderd. Reden: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s heeft %2$s ontbannen. Reden: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s heeft %2$s verbannen. Reden: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s heeft %2$s een uitnodiging voor het gesprek gestuurd. Reden: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s heeft de uitnodiging voor %2$s ingetrokken. Reden: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s heeft de uitnodiging voor %2$s aanvaard. Reden: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s heeft de uitnodiging van %2$s ingetrokken. Reden: %3$s</string> - - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s heeft %2$s als gespreksadres toegevoegd.</item> - <item quantity="other">%1$s heeft %2$s als gespreksadressen toegevoegd.</item> - </plurals> - - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s heeft %2$s als gespreksadres verwijderd.</item> - <item quantity="other">%1$s heeft %3$s als gespreksadressen verwijderd.</item> - </plurals> - - <string name="notice_room_aliases_added_and_removed">%1$s heeft %2$s als gespreksadres toegevoegd en %3$s verwijderd.</string> - - <string name="notice_room_canonical_alias_set">%1$s heeft het hoofdadres voor dit gesprek ingesteld op %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s heeft het hoofdadres voor dit gesprek verwijderd.</string> - - <string name="notice_room_guest_access_can_join">%1$s heeft gasten de toegang tot het gesprek verleend.</string> - <string name="notice_room_guest_access_forbidden">%1$s heeft gasten de toegang tot het gesprek verhinderd.</string> - - <string name="notice_end_to_end_ok">%1$s heeft eind-tot-eind-versleuteling ingeschakeld.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s heeft eind-tot-eind-versleuteling ingeschakeld (onbekend algoritme %2$s).</string> - - <string name="key_verification_request_fallback_message">%s vraagt om uw sleutel te verifiëren, maar uw cliënt biedt geen ondersteuning voor verificatie in het gesprek. U zult de verouderde sleutelverificatie moeten gebruiken om de sleutels te verifiëren.</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-nn/strings.xml b/matrix-sdk-android/src/main/res/values-nn/strings.xml deleted file mode 100644 index 441d568fc3c92a39c9486007e79bddd81b675c0e..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-nn/strings.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="encrypted_message">Kryptert melding</string> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s sende eit bilæte.</string> - <string name="summary_user_sent_sticker">%1$s sende eit klistremerke.</string> - <string name="notice_room_invite_no_invitee">%s si innbjoding</string> - <string name="notice_room_invite">%1$s inviterte %2$s</string> - <string name="notice_room_invite_you">%1$s inviterte deg</string> - <string name="notice_room_join">%1$s kom inn</string> - <string name="notice_room_leave">%1$s forlot rommet</string> - <string name="notice_room_reject">%1$s sa nei til innbjodingi</string> - <string name="notice_room_kick">%1$s sparka %2$s</string> - <string name="notice_room_unban">%1$s slapp %2$s inn att</string> - <string name="notice_room_ban">%1$s stengde %2$s ute</string> - <string name="notice_room_withdraw">%1$s tok attende %2$s si innbjoding</string> - <string name="notice_avatar_url_changed">%1$s byta avataren sin</string> - <string name="notice_display_name_set">%1$s sette visingsnamnet sitt som %2$s</string> - <string name="notice_display_name_changed_from">%1$s byta visingsnamnet sitt frÃ¥ %2$s til %3$s</string> - <string name="notice_display_name_removed">%1$s tok burt visingsnamnet sitt (%2$s)</string> - <string name="notice_room_topic_changed">%1$s gjorde emnet til: %2$s</string> - <string name="notice_room_name_changed">%1$s gjorde romnamnet til: %2$s</string> - <string name="notice_placed_video_call">%s starta ei videosamtala.</string> - <string name="notice_placed_voice_call">%s starta ein talesamtale.</string> - <string name="notice_answered_call">%s tok røyret.</string> - <string name="notice_ended_call">%s la pÃ¥ røyret.</string> - <string name="notice_made_future_room_visibility">%1$s gjorde den framtidige romsoga synleg for %2$s</string> - <string name="notice_room_visibility_invited">alle rommedlemmar, frÃ¥ dÃ¥ dei vart invitert inn.</string> - <string name="notice_room_visibility_joined">alle rommedlemmar, frÃ¥ dÃ¥ dei kom inn.</string> - <string name="notice_room_visibility_shared">alle rommedlemmar.</string> - <string name="notice_room_visibility_world_readable">kven som heldst.</string> - <string name="notice_room_visibility_unknown">uvisst (%s).</string> - <string name="notice_end_to_end">%1$s skrudde ende-til-ende-kryptering pÃ¥ (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s bad um ei VoIP-gruppasamtala</string> - <string name="notice_voip_started">VoIP-gruppasamtala er starta</string> - <string name="notice_voip_finished">VoIP-gruppasamtala er ferdug</string> - <string name="notice_avatar_changed_too">(avataren vart au byta)</string> - <string name="notice_room_name_removed">%1$s tok burt romnamnet</string> - <string name="notice_room_topic_removed">%1$s tok burt romemnet</string> - <string name="notice_profile_change_redacted">%1$s gjorde um pÃ¥ skildringi si %2$s</string> - <string name="notice_room_third_party_invite">%1$s inviterte %2$s til rommet</string> - <string name="notice_room_third_party_registered_invite">%1$s sa ja til innbjodingi til %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Fekk ikkje til Ã¥ dekryptera: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Avsendareiningi hev ikkje sendt oss nyklane fyr denna meldingi.</string> - <string name="could_not_redact">Kunde ikkje gjera um</string> - <string name="unable_to_send_message">Fekk ikkje til Ã¥ senda meldingi</string> - <string name="message_failed_to_upload">Fekk ikkje til Ã¥ lasta biletet upp</string> - <string name="network_error">Noko gjekk gale med netverket</string> - <string name="matrix_error">Noko gjekk gale med Matrix</string> - <string name="room_error_join_failed_empty_room">Det lèt seg fyrebils ikkje gjera Ã¥ fara inn att i eit tomt rom.</string> - <string name="medium_email">Epostadresse</string> - <string name="medium_phone_number">Telefonnummer</string> - <string name="room_displayname_invite_from">Innbjoding frÃ¥ %s</string> - <string name="room_displayname_room_invite">Rominnbjoding</string> - <string name="room_displayname_two_members">%1$s og %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s og 1 til</item> - <item quantity="other">%1$s og %2$d til</item> - </plurals> - <string name="room_displayname_empty_room">Tomt rom</string> - <string name="notice_event_redacted">Ei melding vart stroki</string> - <string name="notice_event_redacted_by">%1$s strauk meldingi</string> - <string name="notice_event_redacted_with_reason">Meldingi vart stroki [av di: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">%1$s strauk meldingi [av di: %2$s]</string> - <string name="notice_room_update">%s oppgraderte rommet.</string> - <string name="clear_timeline_send_queue">Nullstill sendingskø</string> - <string name="notice_room_leave_with_reason">%1$s forlot rommet. Grunn: %2$s</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-pl/strings.xml b/matrix-sdk-android/src/main/res/values-pl/strings.xml deleted file mode 100644 index 0d79edc658242cb1bd9586039185a3ef35ad0219..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-pl/strings.xml +++ /dev/null @@ -1,104 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s wysÅ‚aÅ‚(a) zdjÄ™cie.</string> - - <string name="notice_room_invite_no_invitee">Zaproszenie od %s</string> - <string name="notice_room_invite">%1$s zaprosiÅ‚(a) %2$s</string> - <string name="notice_room_invite_you">%1$s zaprosiÅ‚(a) CiÄ™</string> - <string name="notice_room_join">%1$s doÅ‚Ä…czyÅ‚(a)</string> - <string name="notice_room_leave">%1$s opuÅ›ciÅ‚(a)</string> - <string name="notice_room_reject">%1$s odrzuciÅ‚(a) zaproszenie</string> - <string name="notice_room_kick">%1$s wyrzuciÅ‚(a) %2$s</string> - <string name="notice_room_unban">%1$s odblokowaÅ‚(a) %2$s</string> - <string name="notice_room_ban">%1$s zablokowaÅ‚(a) %2$s</string> - <string name="notice_avatar_url_changed">%1$s zmieniÅ‚(a) awatar</string> - <string name="notice_display_name_set">%1$s zmieniÅ‚(a) wyÅ›wietlanÄ… nazwÄ™ na %2$s</string> - <string name="notice_display_name_changed_from">%1$s zmieniÅ‚(a) wyÅ›wietlanÄ… nazwÄ™ z %2$s na %3$s</string> - <string name="notice_display_name_removed">%1$s usunÄ…Å‚(-ęła) swojÄ… wyÅ›wietlanÄ… nazwÄ™ (%2$s)</string> - <string name="notice_room_topic_changed">%1$s zmieniÅ‚(a) temat na: %2$s</string> - <string name="unable_to_send_message">Nie można wysÅ‚ać wiadomoÅ›ci</string> - - <string name="message_failed_to_upload">PrzesyÅ‚anie zdjÄ™cia nie powiodÅ‚o siÄ™</string> - - <string name="network_error">BÅ‚Ä…d sieci</string> - <string name="matrix_error">BÅ‚Ä…d Matrixa</string> - - <string name="encrypted_message">Wiadomość zaszyfrowana</string> - - <string name="medium_email">Adres e-mail</string> - <string name="medium_phone_number">Numer telefonu</string> - - <string name="notice_room_visibility_shared">wszyscy czÅ‚onkowie pokoju.</string> - <string name="notice_room_visibility_world_readable">wszyscy.</string> - <string name="notice_room_name_changed">%1$s zmieniÅ‚(a) nazwÄ™ pokoju na: %2$s</string> - <string name="notice_ended_call">%s zakoÅ„czyÅ‚(a) rozmowÄ™.</string> - <string name="notice_room_name_removed">%1$s usunÄ…Å‚(-ęła) nazwÄ™ pokoju</string> - <string name="notice_room_topic_removed">%1$s usunÄ…Å‚(-ęła) temat pokoju</string> - <string name="summary_user_sent_sticker">%1$s wysÅ‚aÅ‚(a) naklejkÄ™.</string> - - <string name="notice_end_to_end">%1$s wÅ‚Ä…czyÅ‚(a) szyfrowanie end-to-end (%2$s)</string> - - <string name="notice_room_withdraw">%1$s wycofaÅ‚(a) zaproszenie %2$s</string> - <string name="notice_answered_call">%s odebraÅ‚(a) poÅ‚Ä…czenie.</string> - <string name="notice_avatar_changed_too">(awatar też zostaÅ‚ zmieniony)</string> - - <string name="room_displayname_invite_from">Zaproszenie od %s</string> - <string name="room_displayname_room_invite">Zaproszenie do pokoju</string> - <string name="room_displayname_two_members">%1$s i %2$s</string> - <string name="room_displayname_empty_room">Pusty pokój</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s i jeden inny</item> - <item quantity="few">%1$s i kilku innych</item> - <item quantity="many">%1$s i %2$d innych</item> - <item quantity="other" /> - </plurals> - - <string name="notice_crypto_unable_to_decrypt">** Nie można odszyfrować: %s **</string> - <string name="notice_placed_video_call">%s wykonaÅ‚(a) rozmowÄ™ wideo.</string> - <string name="notice_placed_voice_call">%s wykonaÅ‚(a) poÅ‚Ä…czenie gÅ‚osowe.</string> - <string name="notice_made_future_room_visibility">%1$s uczyniÅ‚(a) przyszÅ‚Ä… historiÄ™ pokoju widocznÄ… dla %2$s</string> - <string name="notice_room_visibility_invited">wszyscy czÅ‚onkowie pokoju, od momentu w którym zostali zaproszeni.</string> - <string name="notice_room_visibility_joined">wszyscy czÅ‚onkowie pokoju, od momentu w którym doÅ‚Ä…czyli.</string> - <string name="notice_room_visibility_unknown">nieznane (%s).</string> - <string name="notice_requested_voip_conference">%1$s zażądaÅ‚(a) grupowego poÅ‚Ä…czenia VoIP</string> - <string name="notice_voip_started">RozpoczÄ™to grupowe poÅ‚Ä…czenie gÅ‚osowe VoIP</string> - <string name="notice_voip_finished">ZakoÅ„czono grupowe poÅ‚Ä…czenie gÅ‚osowe VoIP</string> - - <string name="notice_profile_change_redacted">%1$s zaktualizowaÅ‚ swój profil %2$s</string> - <string name="notice_room_third_party_invite">%1$s wysÅ‚aÅ‚(a) zaproszenie do %2$s aby doÅ‚Ä…czyÅ‚(a) do tego pokoju</string> - <string name="notice_room_third_party_registered_invite">%1$s zaakceptowaÅ‚(a) zaproszenie dla %2$s</string> - - <string name="notice_crypto_error_unkwown_inbound_session_id">UrzÄ…dzenie nadawcy nie wysÅ‚aÅ‚o nam kluczy do tej wiadomoÅ›ci.</string> - - <string name="could_not_redact">Nie można zredagować</string> - <string name="room_error_join_failed_empty_room">Obecnie nie jest możliwe ponowne doÅ‚Ä…czenie do pustego pokoju.</string> - - <string name="notice_event_redacted">Wiadomość usuniÄ™ta</string> - <string name="notice_event_redacted_by">Wiadomość usuniÄ™ta przez %1$s</string> - <string name="notice_event_redacted_with_reason">Wiadomość usuniÄ™ta [powód: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Wiadomość usuniÄ™ta przez %1$s [powód: %2$s]</string> - <string name="notice_room_update">%s zakutalizowaÅ‚(a) ten pokój.</string> - - <string name="initial_sync_start_importing_account">Synchronizacja poczÄ…tkowa: -\nImportowanie konta…</string> - <string name="initial_sync_start_importing_account_crypto">Synchronizacja poczÄ…tkowa: -\nImportowanie kryptografii</string> - <string name="initial_sync_start_importing_account_rooms">Synchronizacja poczÄ…tkowa: -\nImportowanie Pokoi</string> - <string name="initial_sync_start_importing_account_joined_rooms">Synchronizacja poczÄ…tkowa: -\nImportowanie doÅ‚Ä…czonych Pokoi</string> - <string name="initial_sync_start_importing_account_invited_rooms">Synchronizacja poczÄ…tkowa: -\nImportowanie zaproszonych Pokoi</string> - <string name="initial_sync_start_importing_account_left_rooms">Synchronizacja poczÄ…tkowa: -\nImportowanie opuszczonych Pokoi</string> - <string name="initial_sync_start_importing_account_groups">Synchronizacja poczÄ…tkowa: -\nImportowanie SpoÅ‚ecznoÅ›ci</string> - <string name="initial_sync_start_importing_account_data">Synchronizacja poczÄ…tkowa: -\nImportowanie danych Konta</string> - - <string name="event_status_sending_message">WysyÅ‚anie wiadomoÅ›ci…</string> - <string name="clear_timeline_send_queue">Wyczyść kolejkÄ™ wysyÅ‚ania</string> - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml deleted file mode 100644 index 5e3282a3052560a48085f1b9cb126fc4415abb02..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,269 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s enviou uma foto.</string> - <string name="notice_room_invite_no_invitee">convite de %s</string> - <string name="notice_room_invite">%1$s convidou %2$s</string> - <string name="notice_room_invite_you">%1$s convidou você</string> - <string name="notice_room_join">%1$s entrou na sala</string> - <string name="notice_room_leave">%1$s saiu da sala</string> - <string name="notice_room_reject">%1$s recusou o convite</string> - <string name="notice_room_kick">%1$s removeu %2$s</string> - <string name="notice_room_unban">%1$s removeu o banimento de %2$s</string> - <string name="notice_room_ban">%1$s baniu %2$s</string> - <string name="notice_room_withdraw">%1$s desfez o convite a %2$s</string> - <string name="notice_avatar_url_changed">%1$s alterou a foto de perfil</string> - <string name="notice_display_name_set">%1$s definiu o nome e sobrenome como %2$s</string> - <string name="notice_display_name_changed_from">%1$s alterou o nome e sobrenome de %2$s para %3$s</string> - <string name="notice_display_name_removed">%1$s removeu o nome e sobrenome (era %2$s)</string> - <string name="notice_room_topic_changed">%1$s alterou a descrição para: %2$s</string> - <string name="notice_room_name_changed">%1$s alterou o nome da sala para: %2$s</string> - <string name="notice_placed_video_call">%s iniciou uma chamada de vÃdeo.</string> - <string name="notice_placed_voice_call">%s iniciou uma chamada de voz.</string> - <string name="notice_answered_call">%s aceitou a chamada.</string> - <string name="notice_ended_call">%s encerrou a chamada.</string> - <string name="notice_made_future_room_visibility">%1$s deixou o histórico futuro da sala visÃvel para %2$s</string> - <string name="notice_room_visibility_invited">todos os participantes da sala, a partir do momento em que foram convidados.</string> - <string name="notice_room_visibility_joined">todos os participantes da sala, a partir do momento em que entraram nela.</string> - <string name="notice_room_visibility_shared">todos os participantes da sala.</string> - <string name="notice_room_visibility_world_readable">qualquer pessoa.</string> - <string name="notice_room_visibility_unknown">desconhecido (%s).</string> - <string name="notice_end_to_end">%1$s ativou a criptografia de ponta a ponta (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s deseja iniciar uma chamada em grupo</string> - <string name="notice_voip_started">Chamada em grupo iniciada</string> - <string name="notice_voip_finished">Chamada em grupo encerrada</string> - <string name="notice_avatar_changed_too">(a foto de perfil também foi alterada)</string> - <string name="notice_room_name_removed">%1$s removeu o nome da sala</string> - <string name="notice_room_topic_removed">%1$s removeu a descrição da sala</string> - <string name="notice_profile_change_redacted">%1$s atualizou o perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s enviou um convite para %2$s entrar na sala</string> - <string name="notice_room_third_party_registered_invite">%1$s aceitou o convite para %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Não foi possÃvel descriptografar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">O aparelho do remetente não nos enviou as chaves para esta mensagem.</string> - <!-- Room Screen --> - <string name="could_not_redact">Não foi possÃvel redigir</string> - <string name="unable_to_send_message">Não foi possÃvel enviar a mensagem</string> - <string name="message_failed_to_upload">O envio da imagem falhou</string> - <!-- general errors --> - <string name="network_error">Erro de conexão à internet</string> - <string name="matrix_error">Erro no servidor Matrix</string> - <!-- Home Screen --> - <!-- Last seen time --> - <!-- call events --> - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Atualmente, não é possÃvel entrar novamente em uma sala vazia.</string> - <string name="encrypted_message">Mensagem criptografada</string> - <!-- medium friendly name --> - <string name="medium_email">Endereço de e-mail</string> - <string name="medium_phone_number">Número de telefone</string> - <string name="summary_user_sent_sticker">%1$s enviou uma figurinha.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Convite de %s</string> - <string name="room_displayname_room_invite">Convite para sala</string> - <string name="room_displayname_two_members">%1$s e %2$s</string> - <string name="room_displayname_empty_room">Sala vazia</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s e 1 outro</item> - <item quantity="other">%1$s e %2$d outros</item> - </plurals> - <string name="summary_you_sent_image">Você enviou uma foto.</string> - <string name="summary_you_sent_sticker">Você enviou uma figurinha.</string> - <string name="notice_room_invite_no_invitee_by_you">Seu convite</string> - <string name="notice_room_created">%1$s criou a sala</string> - <string name="notice_room_created_by_you">Você criou a sala</string> - <string name="notice_room_invite_by_you">Você convidou %1$s</string> - <string name="notice_room_join_by_you">Você entrou na sala</string> - <string name="notice_room_leave_by_you">Você saiu da sala</string> - <string name="notice_room_reject_by_you">Você recusou o convite</string> - <string name="notice_room_kick_by_you">Você removeu %1$s</string> - <string name="notice_room_unban_by_you">Você removeu o banimento de %1$s</string> - <string name="notice_room_ban_by_you">Você baniu %1$s</string> - <string name="notice_room_withdraw_by_you">Você desfez o convite a %1$s</string> - <string name="notice_avatar_url_changed_by_you">Você alterou a sua foto de perfil</string> - <string name="notice_display_name_set_by_you">Você definiu o seu nome e sobrenome como %1$s</string> - <string name="notice_display_name_changed_from_by_you">Você alterou o seu nome e sobrenome de %1$s para %2$s</string> - <string name="notice_display_name_removed_by_you">Você removeu o seu nome e sobrenome (era %1$s)</string> - <string name="notice_room_topic_changed_by_you">Você alterou a descrição para: %1$s</string> - <string name="notice_room_avatar_changed">%1$s alterou a foto da sala</string> - <string name="notice_room_avatar_changed_by_you">Você alterou a foto da sala</string> - <string name="notice_room_name_changed_by_you">Você alterou o nome da sala para: %1$s</string> - <string name="notice_placed_video_call_by_you">Você iniciou uma chamada de vÃdeo.</string> - <string name="notice_placed_voice_call_by_you">Você iniciou uma chamada de voz.</string> - <string name="notice_call_candidates">%s enviou dados para configurar a chamada.</string> - <string name="notice_call_candidates_by_you">Você enviou dados para configurar a chamada.</string> - <string name="notice_answered_call_by_you">Você aceitou a chamada.</string> - <string name="notice_ended_call_by_you">Você encerrou a chamada.</string> - <string name="notice_made_future_room_visibility_by_you">Você deixou o histórico futuro da sala visÃvel para %1$s</string> - <string name="notice_end_to_end_by_you">Você ativou a criptografia de ponta a ponta (%1$s)</string> - <string name="notice_room_update">%s atualizou esta sala.</string> - <string name="notice_room_update_by_you">Você atualizou esta sala.</string> - <string name="notice_requested_voip_conference_by_you">Você solicitou uma chamada em grupo</string> - <string name="notice_room_name_removed_by_you">Você removeu o nome da sala</string> - <string name="notice_room_topic_removed_by_you">Você removeu a descrição da sala</string> - <string name="notice_room_avatar_removed">%1$s removeu a foto da sala</string> - <string name="notice_room_avatar_removed_by_you">Você removeu a foto da sala</string> - <string name="notice_event_redacted">Mensagem apagada</string> - <string name="notice_event_redacted_by">Mensagem apagada por %1$s</string> - <string name="notice_event_redacted_with_reason">Mensagem apagada [motivo: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Mensagem apagada por %1$s [motivo: %2$s]</string> - <string name="notice_profile_change_redacted_by_you">Você atualizou o seu perfil %1$s</string> - <string name="notice_room_third_party_invite_by_you">Você enviou um convite para %1$s entrar na sala</string> - <string name="notice_room_third_party_revoked_invite">%1$s cancelou o convite a %2$s para entrar na sala</string> - <string name="notice_room_third_party_revoked_invite_by_you">Você cancelou o convite a %1$s para entrar na sala</string> - <string name="notice_room_third_party_registered_invite_by_you">Você aceitou o convite para %1$s</string> - <string name="notice_widget_added">%1$s adicionou o widget %2$s</string> - <string name="notice_widget_added_by_you">Você adicionou o widget %1$s</string> - <string name="notice_widget_removed">%1$s removeu o widget %2$s</string> - <string name="notice_widget_removed_by_you">Você removeu o widget %1$s</string> - <string name="notice_widget_modified">%1$s editou o widget %2$s</string> - <string name="notice_widget_modified_by_you">Você editou o widget %1$s</string> - <string name="power_level_admin">Administrador</string> - <string name="power_level_moderator">Moderador</string> - <string name="power_level_default">Padrão</string> - <string name="power_level_custom">Personalizado (%1$d)</string> - <string name="power_level_custom_no_value">Personalizado</string> - <string name="notice_power_level_changed_by_you">Você alterou o nÃvel de permissão de %1$s.</string> - <string name="notice_power_level_changed">%1$s alterou o nÃvel de permissão de %2$s.</string> - <string name="notice_power_level_diff">%1$s de %2$s para %3$s</string> - <string name="initial_sync_start_importing_account">Primeira sincronização: -\nImportando a conta…</string> - <string name="initial_sync_start_importing_account_crypto">Primeira sincronização: -\nImportando as chaves de criptografia</string> - <string name="initial_sync_start_importing_account_rooms">Primeira sincronização: -\nImportando as salas</string> - <string name="initial_sync_start_importing_account_joined_rooms">Primeira sincronização: -\nImportando as salas em que você entrou</string> - <string name="initial_sync_start_importing_account_invited_rooms">Primeira sincronização: -\nImportando as salas em que você foi convidado</string> - <string name="initial_sync_start_importing_account_left_rooms">Primeira sincronização: -\nImportando as salas em que você saiu</string> - <string name="initial_sync_start_importing_account_groups">Primeira sincronização: -\nImportando as comunidades</string> - <string name="initial_sync_start_importing_account_data">Primeira sincronização: -\nImportando os dados da conta</string> - <string name="event_status_sending_message">Enviando mensagem…</string> - <string name="clear_timeline_send_queue">Limpar a fila de envio</string> - <string name="notice_room_invite_no_invitee_with_reason">Convite de %1$s. Motivo: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">O seu convite. Motivo: %1$s</string> - <string name="notice_room_invite_with_reason">%1$s convidou %2$s. Motivo: %3$s</string> - <string name="notice_room_invite_with_reason_by_you">Você convidou %1$s. Motivo: %2$s</string> - <string name="notice_room_invite_you_with_reason">%1$s convidou você. Motivo: %2$s</string> - <string name="notice_room_join_with_reason">%1$s entrou na sala. Motivo: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Você entrou na sala. Motivo: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s saiu da sala. Motivo: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Você saiu da sala. Motivo: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s recusou o convite. Motivo: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Você recusou o convite. Motivo: %1$s</string> - <string name="notice_room_kick_with_reason">%1$s removeu %2$s. Motivo: %3$s</string> - <string name="notice_room_kick_with_reason_by_you">Você removeu %1$s. Motivo: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s removeu o banimento de %2$s. Motivo: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">Você removeu o banimento de %1$s. Motivo: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s baniu %2$s. Motivo: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">Você baniu %1$s. Motivo: %2$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s enviou um convite para %2$s entrar na sala. Motivo: %3$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Você enviou um convite para %1$s entrar na sala. Motivo: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s revogou o convite para %2$s entrar na sala. Motivo: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Você revogou o convite para %1$s entrar na sala. Motivo: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s aceitou o convite para %2$s. Motivo: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Você aceitou o convite para %1$s. Motivo: %2$s</string> - <string name="notice_room_withdraw_with_reason">%1$s desfez o convite de %2$s. Motivo: %3$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Você desfez o convite de %1$s. Motivo: %2$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s adicionou %2$s como um endereço desta sala.</item> - <item quantity="other">%1$s adicionou %2$s como endereços desta sala.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Você adicionou %1$s como um endereço desta sala.</item> - <item quantity="other">Você adicionou %1$s como endereços desta sala.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s removeu %2$s como um endereço desta sala.</item> - <item quantity="other">%1$s removeu %2$s como endereços desta sala.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Você removeu %1$s como um endereço desta sala.</item> - <item quantity="other">Você removeu %1$s como endereços desta sala.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s adicionou %2$s e removeu %3$s como endereços desta sala.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Você adicionou %1$s e removeu %2$s como endereços desta sala.</string> - <string name="notice_room_canonical_alias_set">%1$s definiu o endereço principal desta sala como %2$s.</string> - <string name="notice_room_canonical_alias_set_by_you">Você definiu o endereço principal desta sala como %1$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s removeu o endereço principal desta sala.</string> - <string name="notice_room_canonical_alias_unset_by_you">Você removeu o endereço principal desta sala.</string> - <string name="notice_room_guest_access_can_join">%1$s permitiu que convidados entrem na sala.</string> - <string name="notice_room_guest_access_can_join_by_you">Você permitiu que convidados entrem na sala.</string> - <string name="notice_room_guest_access_forbidden">%1$s impediu que convidados entrassem na sala.</string> - <string name="notice_room_guest_access_forbidden_by_you">Você impediu que convidados entrassem na sala.</string> - <string name="notice_end_to_end_ok">%1$s ativou a criptografia de ponta a ponta.</string> - <string name="notice_end_to_end_ok_by_you">Você ativou a criptografia de ponta a ponta.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s ativou a criptografia de ponta a ponta (algoritmo não reconhecido %2$s).</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Você ativou a criptografia de ponta a ponta (algoritmo não reconhecido %1$s).</string> - <string name="key_verification_request_fallback_message">%s deseja confirmar a sua chave, mas o seu aplicativo não suporta a confirmação da chave da conversa. Você precisará usar a confirmação tradicional de chaves para confirmar chaves.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Você impediu que desconhecidos entrem na sala.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s impediu que desconhecidos entrem na sala.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Você permitiu que desconhecidos entrem aqui.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s permitiu que desconhecidos entrem aqui.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Você saiu. Motivo: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s saiu. Motivo: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Você entrou. Motivo: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s entrou. Motivo: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Você cancelou o convite para %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s cancelou o convite para %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Você convidou %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s convidou %2$s</string> - <string name="notice_direct_room_update_by_you">Você atualizou esta sala.</string> - <string name="notice_direct_room_update">%s atualizou esta sala.</string> - <string name="notice_made_future_direct_room_visibility_by_you">Você definiu que as mensagens enviadas a partir do presente momento estarão disponÃveis para %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s definiu que as mensagens enviadas a partir do presente momento estarão disponÃveis para %2$s</string> - <string name="notice_direct_room_leave_by_you">Você saiu da sala</string> - <string name="notice_direct_room_leave">%1$s saiu da sala</string> - <string name="notice_direct_room_join_by_you">Você entrou</string> - <string name="notice_direct_room_join">%1$s entrou</string> - <string name="notice_direct_room_created_by_you">Você criou a sala</string> - <string name="notice_direct_room_created">%1$s criou a sala</string> - <string name="room_displayname_empty_room_was">Sala vazia (era %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s e %4$d outro</item> - <item quantity="other">%1$s, %2$s, %3$s e %4$d outros</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s e %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s e %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Todos os servidores estão proibidos de participar! Esta sala não pode mais ser usada.</string> - <string name="notice_room_server_acl_updated_no_change">Nenhuma alteração.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Servidores correspondentes aos IP literais agora estão banidos.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Servidores correspondentes aos IP literais agora estão permitidos.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Servidores correspondentes à %s foram removidos da lista de permitidos.</string> - <string name="notice_room_server_acl_updated_allowed">• Servidores correspondentes à %s agora são permitidos.</string> - <string name="notice_room_server_acl_updated_was_banned">• Servidores correspondente à %s foram removidos da lista de banidos.</string> - <string name="notice_room_server_acl_updated_banned">• Servidores correspondentes à %s foram banidos.</string> - <string name="notice_room_server_acl_updated_title_by_you">Você alterou a lista de controle de acesso (ACL) do servidor para esta sala.</string> - <string name="notice_room_server_acl_updated_title">%s alterou a lista de controle de acesso (ACL) do servidor para esta sala.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Servidores correspondentes aos IP literais estão banidos.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Servidores correspondentes aos IP literais estão permitidos.</string> - <string name="notice_room_server_acl_set_allowed">• Servidores correspondentes à %s estão permitidos.</string> - <string name="notice_room_server_acl_set_banned">• Servidores correspondentes à %s estão banidos.</string> - <string name="notice_room_server_acl_set_title_by_you">Você definiu a lista de controle de acesso (ACL) do servidor para esta sala.</string> - <string name="notice_room_server_acl_set_title">%s definiu a lista de controle de acesso (ACL) do servidor para esta sala.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Você alterou os endereços alternativos desta sala.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s alterou os endereços alternativos desta sala.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Você removeu o endereço alternativo %1$s para esta sala.</item> - <item quantity="other">Você removeu os endereços alternativos %1$s para esta sala.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s removeu o endereço alternativo %2$s para esta sala.</item> - <item quantity="other">%1$s removeu os endereços alternativos %2$s para esta sala.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Você adicionou o endereço alternativo %1$s para esta sala.</item> - <item quantity="other">Você adicionou os endereços alternativos %1$s para esta sala.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s adicionou o endereço alternativo %2$s para esta sala.</item> - <item quantity="other">%1$s adicionou os endereços alternativos %2$s para esta sala.</item> - </plurals> - <string name="notice_room_canonical_alias_no_change_by_you">Você alterou os endereços desta sala.</string> - <string name="notice_room_canonical_alias_no_change">%1$s alterou os endereços desta sala.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Você alterou os endereços principal e alternativos desta sala.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s alterou os endereços principal e alternativos desta sala.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-pt/strings.xml b/matrix-sdk-android/src/main/res/values-pt/strings.xml deleted file mode 100644 index 4bc90cf0cb7a1ad4a32d17948bb68da460763b75..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-pt/strings.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s enviou uma imagem.</string> - - <string name="notice_room_invite_no_invitee">convite de %s</string> - <string name="notice_room_invite">%1$s convidou %2$s</string> - <string name="notice_room_invite_you">%1$s convidou-o</string> - <string name="notice_room_join">%1$s entrou</string> - <string name="notice_room_leave">%1$s saiu</string> - <string name="notice_room_reject">%1$s recusou o convite</string> - <string name="notice_room_kick">%1$s expulsou %2$s</string> - <string name="notice_room_unban">%1$s des-baniu %2$s</string> - <string name="notice_room_ban">%1$s baniu %2$s</string> - <string name="notice_room_withdraw">%1$s cancelou o convite de %2$s</string> - <string name="notice_avatar_url_changed">%1$s mudou o seu avatar</string> - <string name="notice_display_name_set">%1$s definiu seu nome público como %2$s</string> - <string name="notice_display_name_changed_from">%1$s alterou seu nome público de %2$s para %3$s</string> - <string name="notice_display_name_removed">%1$s apagou o seu nome público (%2$s)</string> - <string name="notice_room_topic_changed">%1$s alterou o tópico desta sala para: %2$s</string> - <string name="notice_room_name_changed">%1$s alterou o nome desta sala para: %2$s</string> - <string name="notice_placed_video_call">%s iniciou uma chamada de vÃdeo.</string> - <string name="notice_placed_voice_call">%s iniciou uma chamada de voz.</string> - <string name="notice_answered_call">%s respondeu à chamada.</string> - <string name="notice_ended_call">%s terminou a chamada.</string> - <string name="notice_made_future_room_visibility">%1$s tornou o histórico futuro desta sala visÃvel para %2$s</string> - <string name="notice_room_visibility_invited">todas os membros que integram esta sala, a partir do momento em que foram convidados.</string> - <string name="notice_room_visibility_joined">todas os membros da sala, a partir do momento em que entraram.</string> - <string name="notice_room_visibility_shared">todas os membros da sala.</string> - <string name="notice_room_visibility_world_readable">todos.</string> - <string name="notice_room_visibility_unknown">desconhecida (%s).</string> - <string name="notice_end_to_end">%1$s ativou a criptografia ponta-a-ponta (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s solicitou uma conferência VoIP</string> - <string name="notice_voip_started">A conferência VoIP começou</string> - <string name="notice_voip_finished">A conferência VoIP terminou</string> - - <string name="notice_avatar_changed_too">(o avatar também foi alterado)</string> - <string name="notice_room_name_removed">%1$s removeu o nome da sala</string> - <string name="notice_room_topic_removed">%1$s removeu o tópico da sala</string> - <string name="notice_profile_change_redacted">%1$s atualizou o seu perfil %2$s</string> - <string name="notice_room_third_party_invite">%1$s enviou um convite para que %2$s se junte à sala</string> - <string name="notice_room_third_party_registered_invite">%1$s aceitou o convite para %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** ImpossÃvel decifrar: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">O dispositivo de quem enviou a mensagem não nos enviou as chaves para esta mensagem.</string> - - <!-- Room Screen --> - <string name="could_not_redact">Não foi possÃvel apagar</string> - <string name="unable_to_send_message">Não foi possÃvel enviar a mensagem</string> - - <string name="message_failed_to_upload">O envio da imagem falhou</string> - - <!-- general errors --> - <string name="network_error">Erro de conexão à Internet</string> - <string name="matrix_error">Erro do Matrix</string> - - <!-- Home Screen --> - - <!-- Last seen time --> - - <!-- call events --> - - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Ainda não é possÃvel voltar a entrar numa sala vazia.</string> - - <string name="encrypted_message">Mensagem cifrada</string> - - <!-- medium friendly name --> - <string name="medium_email">Endereço de e-mail</string> - <string name="medium_phone_number">Número de telefone</string> - - <!-- Room display name --> - <string name="room_displayname_invite_from">Convite de %s</string> - <string name="room_displayname_room_invite">Convite para sala</string> - <string name="room_displayname_two_members">%1$s e %2$s</string> - <string name="room_displayname_empty_room">Sala vazia</string> - - - <string name="summary_user_sent_sticker">%1$s enviou um sticker.</string> - - <string name="notice_room_update">%s fez o upgrade da sala.</string> - - <string name="notice_event_redacted">Mensagem removida</string> - <string name="notice_event_redacted_by">Mensagem removida por %1$s</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-ru/strings.xml b/matrix-sdk-android/src/main/res/values-ru/strings.xml deleted file mode 100644 index f2e0bd668f6b5c85c33099ace140ff8e21896dff..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-ru/strings.xml +++ /dev/null @@ -1,285 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%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> - <string name="notice_answered_call">%s ответил(а) на звонок.</string> - <string name="notice_ended_call">%s завершил(а) вызов.</string> - <string name="notice_made_future_room_visibility">%1$s Ñделал(а) будущую иÑторию комнаты видимой %2$s</string> - <string name="notice_room_visibility_invited">вÑем членам, Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° их приглашениÑ.</string> - <string name="notice_room_visibility_joined">вÑем членам, Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° приÑоединениÑ.</string> - <string name="notice_room_visibility_shared">вÑем членам.</string> - <string name="notice_room_visibility_world_readable">вÑем.</string> - <string name="notice_room_visibility_unknown">неизвеÑтно (%s).</string> - <string name="notice_end_to_end">%1$s включил(а) Ñквозное шифрование (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s запроÑил(а) VoIP конференцию</string> - <string name="notice_voip_started">VoIP-ÐºÐ¾Ð½Ñ„ÐµÑ€ÐµÐ½Ñ†Ð¸Ñ Ð½Ð°Ñ‡Ð°Ñ‚Ð°</string> - <string name="notice_voip_finished">VoIP-ÐºÐ¾Ð½Ñ„ÐµÑ€ÐµÐ½Ñ†Ð¸Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð°</string> - <string name="notice_avatar_changed_too">(аватар также был изменен)</string> - <string name="notice_room_name_removed">%1$s удалил(а) название комнаты</string> - <string name="notice_room_topic_removed">%1$s удалил(а) тему комнаты</string> - <string name="notice_profile_change_redacted">%1$s обновил(а) Ñвой профиль %2$s</string> - <string name="notice_room_third_party_invite">%1$s отправил(а) приглашение %2$s приÑоединитьÑÑ Ðº комнате</string> - <string name="notice_room_third_party_registered_invite">%1$s принÑл(а) приглашение от %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Ðевозможно раÑшифровать: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">УÑтройÑтво Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð½Ðµ предоÑтавило нам ключ Ð´Ð»Ñ Ñ€Ð°Ñшифровки Ñтого ÑообщениÑ.</string> - <!-- Room Screen --> - <string name="could_not_redact">Ðе удалоÑÑŒ изменить</string> - <string name="unable_to_send_message">Ðе удалоÑÑŒ отправить Ñообщение</string> - <string name="message_failed_to_upload">Ðе удалоÑÑŒ загрузить изображение</string> - <!-- general errors --> - <string name="network_error">Ð¡ÐµÑ‚ÐµÐ²Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</string> - <string name="matrix_error">Ошибка Matrix</string> - <!-- Home Screen --> - <!-- Last seen time --> - <!-- call events --> - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ вновь приÑоединитьÑÑ Ðº пуÑтой комнате.</string> - <string name="encrypted_message">Зашифрованное Ñообщение</string> - <!-- medium friendly name --> - <string name="medium_email">ÐÐ´Ñ€ÐµÑ Ñлектронной почты</string> - <string name="medium_phone_number">Ðомер телефона</string> - <string name="summary_user_sent_sticker">%1$s отправил Ñтикер.</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">ПриглашÐµÐ½Ð¸Ðµ от %s</string> - <string name="room_displayname_room_invite">Приглашение в комнату</string> - <string name="room_displayname_two_members">%1$s и %2$s</string> - <string name="room_displayname_empty_room">ПуÑÑ‚Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ð°</string> - <plurals name="room_displayname_three_and_more_members"> - <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="other"/> - </plurals> - <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="initial_sync_start_importing_account">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт учетной запиÑи…</string> - <string name="initial_sync_start_importing_account_crypto">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт криптографии</string> - <string name="initial_sync_start_importing_account_rooms">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт комнат</string> - <string name="initial_sync_start_importing_account_joined_rooms">Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð°Ñ‡Ð°Ñ‚Ð°: -\nИмпорт приÑоединенных комнат</string> - <string name="initial_sync_start_importing_account_invited_rooms">Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð°Ñ‡Ð°Ñ‚Ð°: -\nИмпорт приглашенных комнат</string> - <string name="initial_sync_start_importing_account_left_rooms">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт покинутых комнат</string> - <string name="initial_sync_start_importing_account_groups">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт ÑообщеÑтв</string> - <string name="initial_sync_start_importing_account_data">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ: -\nИмпорт данных учетной запиÑи</string> - <string name="notice_room_update">%s обновил Ñту комнату.</string> - <string name="event_status_sending_message">Отправка ÑообщениÑ…</string> - <string name="clear_timeline_send_queue">ОчиÑтить очередь отправки</string> - <string name="notice_room_third_party_revoked_invite">%1$s отозвал приглашение %2$s приÑоединитьÑÑ Ðº комнате</string> - <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_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> - <string name="notice_room_ban_with_reason">%1$s забанен %2$s. Причина: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s отправил приглашение %2$s в комнату. Причина: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s отозвал приглашение %2$s приÑоединитьÑÑ Ðº комнате. Причина: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s принÑл приглашение Ð´Ð»Ñ %2$s. Причина: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s отозвал приглашение %2$s. Причина: %3$s</string> - <string name="notice_room_created">%1$s Ñоздал(а) комнату</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s добавил(а) %2$s в качеÑтве адреÑа Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="few">%1$s добавил(а) %2$s в качеÑтве адреÑов Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">%1$s добавил(а) %2$s в качеÑтве адреÑов Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s удалил(а) Ð°Ð´Ñ€ÐµÑ %2$s Ð´Ð»Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹.</item> - <item quantity="few">%1$s удалил(а) адреÑа %2$s Ð´Ð»Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹.</item> - <item quantity="many">%1$s удалил(а) адреÑа %2$s Ð´Ð»Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s добавил(а) адреÑа %2$s и удалил(а) %3$s Ð´Ð»Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹.</string> - <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> - <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_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_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_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="few">Ð’Ñ‹ добавили %1$s в качеÑтве адреÑов Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">Ð’Ñ‹ добавили %1$s в качеÑтве адреÑов Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Ð’Ñ‹ удалили Ð°Ð´Ñ€ÐµÑ Ñтой комнаты: %1$s.</item> - <item quantity="few">Ð’Ñ‹ удалили адреÑа Ñтой комнаты: %1$s.</item> - <item quantity="many">Ð’Ñ‹ удалили адреÑа Ñтой комнаты: %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> - <string name="notice_room_avatar_changed">%1$s изменил(а) аватар комнаты</string> - <string name="notice_room_avatar_changed_by_you">Ð’Ñ‹ изменили аватар комнаты</string> - <string name="notice_call_candidates">%s отправил(а) данные Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° звонка.</string> - <string name="notice_call_candidates_by_you">Ð’Ñ‹ отправили данные Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° звонка.</string> - <string name="notice_room_avatar_removed">%1$s удалил(а) аватар комнаты</string> - <string name="notice_room_avatar_removed_by_you">Ð’Ñ‹ удалили аватар комнаты</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Ð’Ñ‹ запретили гоÑÑ‚Ñм входить в комнату.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s запретил(а) гоÑÑ‚Ñм входить в комнату.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Ð’Ñ‹ разрешили гоÑÑ‚Ñм приÑоединÑÑ‚ÑŒÑÑ Ñюда.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s разрешил(а) гоÑÑ‚Ñм приÑоединитьÑÑ Ñюда.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Ð’Ñ‹ вышли. Причина: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s вышел(-ла). Причина: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Ð’Ñ‹ вошли. Причина: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s вошел(-ла). Причина: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Ð’Ñ‹ отозвали приглашение %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s отозвал(а) приглашение %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Ð’Ñ‹ приглаÑили %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s приглаÑил(а) %2$s</string> - <string name="notice_made_future_direct_room_visibility_by_you">Ð’Ñ‹ Ñделали будущие ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼Ð¸ Ð´Ð»Ñ %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s Ñделал(а) будущие ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼Ð¸ Ð´Ð»Ñ %2$s</string> - <string name="notice_direct_room_leave_by_you">Ð’Ñ‹ покинули комнату</string> - <string name="notice_direct_room_leave">%1$s покинул(а) комнату</string> - <string name="notice_direct_room_join_by_you">Ð’Ñ‹ вошли</string> - <string name="notice_direct_room_join">%1$s вошел(ла)</string> - <string name="notice_direct_room_created_by_you">Ð’Ñ‹ Ñоздали обÑуждение</string> - <string name="notice_direct_room_created">%1$s Ñоздал(а) обÑуждение</string> - <string name="notice_direct_room_update_by_you">Ð’Ñ‹ обновили Ñту комнату.</string> - <string name="notice_direct_room_update">%s обновил(а) Ñту комнату.</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s и %4$d другой</item> - <item quantity="few">%1$s, %2$s, %3$s и %4$d других</item> - <item quantity="many">%1$s, %2$s, %3$s и %4$d другие</item> - <item quantity="other">%1$s, %2$s, %3$s и %4$d другие</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s и %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s и %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Ð’Ñем Ñерверам запрещено учаÑтвовать! Ðта комната больше не может быть иÑпользована.</string> - <string name="notice_room_server_acl_updated_no_change">Без изменений.</string> - <string name="room_displayname_empty_room_was">ПуÑÑ‚Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ð° (была %s)</string> - <string name="notice_room_server_acl_set_banned">• СоответÑтвующий Ñервер %s заблокирован.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Сервер, ÑоответÑтвующий буквальным IP-адреÑам, теперь запрещён.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Сервер, ÑоответÑтвующий буквальным IP-адреÑам, теперь разрешён.</string> - <string name="notice_room_server_acl_updated_banned">• Сервер, ÑоответÑтвующий %s, теперь запрещён.</string> - <string name="notice_room_server_acl_updated_allowed">• Сервер, ÑоответÑтвующий %s, теперь разрешён.</string> - <string name="notice_room_server_acl_updated_was_banned">• Сервер, ÑоответÑтвующий %s, был удалён из ÑпиÑка блокировки.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Сервер, ÑоответÑтвующий буквальным IP-адреÑам, запрещён.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Сервер, ÑоответÑтвующий буквальным IP-адреÑам, разрешён.</string> - <string name="notice_room_server_acl_set_allowed">• Сервер, ÑоответÑтвующий %s, разрешён.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Сервер, ÑоответÑтвующий %s, был удалён из разрешённого ÑпиÑка.</string> - <string name="notice_room_server_acl_updated_title_by_you">Ð’Ñ‹ изменили права доÑтупа Ñервера (ACL) Ð´Ð»Ñ Ñтой комнаты.</string> - <string name="notice_room_server_acl_updated_title">%s изменил права доÑтупа Ñервера (ACL) Ð´Ð»Ñ Ñтой комнаты.</string> - <string name="notice_room_server_acl_set_title_by_you">Ð’Ñ‹ наÑтроили права доÑтупа Ñервера (ACL) Ð´Ð»Ñ Ñтой комнаты.</string> - <string name="notice_room_server_acl_set_title">%s уÑтанавливает права доÑтупа Ñервера (ACL) Ð´Ð»Ñ Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_no_change_by_you">Ð’Ñ‹ изменили адреÑа Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_no_change">%1$s изменил(а) адреÑа Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Ð’Ñ‹ изменили оÑновной и альтернативный адреÑа Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s изменил(а) оÑновной и альтернативный адреÑа Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Ð’Ñ‹ изменили альтернативные адреÑа Ð´Ð»Ñ Ñтой комнаты.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s изменил(а) альтернативные адреÑа Ð´Ð»Ñ Ñтой комнаты.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Ð’Ñ‹ удалили альтернативный Ð°Ð´Ñ€ÐµÑ %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="few">Ð’Ñ‹ удалили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">Ð’Ñ‹ удалили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="other">Ð’Ñ‹ удалили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s удалил(а) альтернативный Ð°Ð´Ñ€ÐµÑ %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="few">%1$s удалил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">%1$s удалил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="other">%1$s удалил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Ð’Ñ‹ добавили альтернативный Ð°Ð´Ñ€ÐµÑ %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="few">Ð’Ñ‹ добавили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">Ð’Ñ‹ добавили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="other">Ð’Ñ‹ добавили альтернативные адреÑа %1$s Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s добавил(а) альтернативный Ð°Ð´Ñ€ÐµÑ %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="few">%1$s добавил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="many">%1$s добавил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - <item quantity="other">%1$s добавил(а) альтернативные адреÑа %2$s Ð´Ð»Ñ Ñтой комнаты.</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-si/strings_sas.xml b/matrix-sdk-android/src/main/res/values-si/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..7ea72d2a4df9b43e633f53ad5fd7918cf666185b --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-si/strings_sas.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> + <string name="verification_emoji_dog">බල්ලà·</string> + <string name="verification_emoji_cat">පූසà·</string> + <string name="verification_emoji_lion">සිංහයà·</string> + <string name="verification_emoji_horse">අà·à·Šà·€à¶ºà·</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-sk/strings.xml b/matrix-sdk-android/src/main/res/values-sk/strings.xml deleted file mode 100644 index a40654f7bf39cd922af9a677645eb42509bb3536..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-sk/strings.xml +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s poslal/a obrázok.</string> - <string name="notice_room_invite_no_invitee">Pozvanie od %s</string> - <string name="notice_room_invite">%1$s pozval/a %2$s</string> - <string name="notice_room_invite_you">%1$s vás pozval/a</string> - <string name="notice_room_join">%1$s vstúpil/a do miestnosti</string> - <string name="notice_room_leave">%1$s opustil/a miestnosÅ¥</string> - <string name="notice_room_reject">%1$s odmietol/a pozvanie</string> - <string name="notice_room_kick">%1$s vykázal/a %2$s</string> - <string name="notice_room_unban">%1$s povolil/a vstupovaÅ¥ %2$s</string> - <string name="notice_room_ban">%1$s zakázal/a vstupovaÅ¥ %2$s</string> - <string name="notice_room_withdraw">%1$s vzal/a späť pozvanie %2$s</string> - <string name="notice_avatar_url_changed">%1$s si zmenil/a obrázok v profile</string> - <string name="notice_display_name_set">%1$s si nastavil/a zobrazované meno %2$s</string> - <string name="notice_display_name_changed_from">%1$s si zmenil/a zobrazované meno %2$s na %3$s</string> - <string name="notice_display_name_removed">%1$s odstránil/a svoje zobrazované meno (%2$s)</string> - <string name="notice_room_topic_changed">%1$s zmenil/a tému na: %2$s</string> - <string name="notice_room_name_changed">%1$s zmenil/a názov miestnosti na: %2$s</string> - <string name="notice_placed_video_call">%s uskutoÄnil/a video hovor.</string> - <string name="notice_placed_voice_call">%s zatelefonoval/a.</string> - <string name="notice_answered_call">%s prijal/a hovor.</string> - <string name="notice_ended_call">%s ukonÄil/a hovor.</string> - <string name="notice_made_future_room_visibility">%1$s sprÃstupnil/a budúcu históriu miestnosti %2$s</string> - <string name="notice_room_visibility_invited">pre vÅ¡etkých Älenov, od kedy boli pozvanÃ.</string> - <string name="notice_room_visibility_joined">pre vÅ¡etkých Älenov, od kedy vstúpili.</string> - <string name="notice_room_visibility_shared">pre vÅ¡etkých Älenov.</string> - <string name="notice_room_visibility_world_readable">pre každého.</string> - <string name="notice_room_visibility_unknown">neznámym (%s).</string> - <string name="notice_end_to_end">%1$s povolil/a E2E Å¡ifrovanie (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s požiadal/a o VoIP konferenciu</string> - <string name="notice_voip_started">ZaÄala sa VoIP konferencia</string> - <string name="notice_voip_finished">SkonÄila sa VoIP konferencia</string> - <string name="notice_avatar_changed_too">(aj obrázok v profile)</string> - <string name="notice_room_name_removed">%1$s odstránil/a názov miestnosti</string> - <string name="notice_room_topic_removed">%1$s odstránil/a tému miestnosti</string> - <string name="notice_profile_change_redacted">%1$s aktualizoval/a svoj profil %2$s</string> - <string name="notice_room_third_party_invite">%1$s pozval/a %2$s vstúpiÅ¥ do miestnosti</string> - <string name="notice_room_third_party_registered_invite">%1$s prijal/a pozvanie pre %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Nie je možné deÅ¡ifrovaÅ¥: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Zo zariadenia odosieľateľa nebolo možné zÃskaÅ¥ kľúÄe potrebné na deÅ¡ifrovanie tejto správy.</string> - <string name="could_not_redact">Nie je možné vymazaÅ¥</string> - <string name="unable_to_send_message">Nie je možné odoslaÅ¥ správu</string> - <string name="message_failed_to_upload">Nepodarilo sa nahraÅ¥ obrázok</string> - <string name="network_error">Chyba siete</string> - <string name="matrix_error">Chyba Matrix</string> - <string name="room_error_join_failed_empty_room">V súÄasnosti nie je možné znovu vstúpiÅ¥ do prázdnej miestnosti.</string> - <string name="encrypted_message">Å ifrovaná správa</string> - <string name="medium_email">Emailová adresa</string> - <string name="medium_phone_number">Telefónne ÄÃslo</string> - <string name="summary_user_sent_sticker">%1$s poslal/a nálepku.</string> - <string name="room_displayname_invite_from">Pozvanie od %s</string> - <string name="room_displayname_room_invite">Pozvanie do miestnosti</string> - <string name="room_displayname_two_members">%1$s a %2$s</string> - <string name="room_displayname_empty_room">Prázdna miestnosÅ¥</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s a 1 ÄalÅ¡Ã</item> - <item quantity="few">%1$s a %2$d ÄalÅ¡Ã</item> - <item quantity="many">%1$s a %2$d ÄalÅ¡Ãch</item> - <item quantity="other"/> - </plurals> - <string name="notice_room_update">%s aktualizoval/a túto miestnosÅ¥.</string> - <string name="notice_event_redacted">Odstránená správa</string> - <string name="notice_event_redacted_by">Odstránená správa použÃvateľom %1$s</string> - <string name="notice_event_redacted_with_reason">Odstránená správa [dôvod: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Odstránená správa použÃvateľom %1$s [dôvod: %2$s]</string> - <string name="initial_sync_start_importing_account">Úvodná synchronizácia: -\nPrebieha import úÄtu…</string> - <string name="initial_sync_start_importing_account_crypto">Úvodná synchronizácia: -\nPrebieha import Å¡ifrovacÃch kľúÄov</string> - <string name="initial_sync_start_importing_account_rooms">Úvodná synchronizácia: -\nPrebieha import miestnostÃ</string> - <string name="initial_sync_start_importing_account_joined_rooms">Úvodná synchronizácia: -\nPrebieha import miestnostÃ, do ktorých ste vstúpili</string> - <string name="initial_sync_start_importing_account_invited_rooms">Úvodná synchronizácia: -\nPrebieha import pozvanÃ</string> - <string name="initial_sync_start_importing_account_left_rooms">Úvodná synchronizácia: -\nPrebieha import opustených miestnostÃ</string> - <string name="initial_sync_start_importing_account_groups">Úvodná synchronizácia: -\nPrebieha import komunÃt</string> - <string name="initial_sync_start_importing_account_data">Úvodná synchronizácia: -\nPrebieha import údajov úÄtu</string> - <string name="event_status_sending_message">Odosielanie správy…</string> - <string name="clear_timeline_send_queue">VymazaÅ¥ správy na odoslanie</string> - <string name="notice_room_third_party_revoked_invite">%1$s zamietol/a pozvanie použÃvateľa %2$s vstúpiÅ¥ do miestnosti</string> - <string name="notice_room_invite_no_invitee_with_reason">Pozvanie od %1$s. Dôvod: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s pozval/a %2$s. Dôvod: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s vás pozval/a. Dôvod: %2$s</string> - <string name="notice_room_join_with_reason">%1$s vstúpil/a do miestnosti. Dôvod: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s opustil/a miestnosÅ¥. Dôvod: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s odmietol/a pozvanie. Dôvod: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s vykázal/a %2$s. Dôvod: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s povolil/a vstupovaÅ¥ %2$s. Dôvod: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s zakázal/a vstupovaÅ¥ %2$s. Dôvod: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s pozval/a %2$s vstúpiÅ¥ do miestnosti. Dôvod: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s zamietol/a pozvanie použÃvateľa %2$s vstúpiÅ¥ do miestnosti. Dôvod: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s prijal/a pozvanie pre %2$s. Dôvod: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s vzal/a späť pozvanie %2$s. Dôvod: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s pridal/a adresu %2$s pre túto miestnosÅ¥.</item> - <item quantity="few">%1$s pridal/a adresy %2$s pre túto miestnosÅ¥.</item> - <item quantity="other">%1$s pridal/a adresy %2$s pre túto miestnosÅ¥.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s odstránil/a adresu %2$s pre túto miestnosÅ¥.</item> - <item quantity="few">%1$s odstránil/a adresy %3$s pre túto miestnosÅ¥.</item> - <item quantity="other">%1$s odstránil/a adresy %3$s pre túto miestnosÅ¥.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s pridal/a adresy %2$s a odstránil/a adresy %3$s pre túto miestnosÅ¥.</string> - <string name="notice_room_canonical_alias_set">%1$s nastavil/a hlavnú adresu tejto miestnosti %2$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s odstránil/a hlavnú adresu tejto miestnosti.</string> - <string name="notice_room_guest_access_can_join">%1$s povolil/a hosÅ¥om///návÅ¡tevnÃkom prÃstup do tejto miestnosti.</string> - <string name="summary_you_sent_image">Poslali ste obrázok.</string> - <string name="summary_you_sent_sticker">Poslali ste nálepku.</string> - <string name="notice_room_invite_no_invitee_by_you">Pozvanie od vás</string> - <string name="notice_room_created">%1$s vytvoril/a miestnosÅ¥</string> - <string name="notice_room_created_by_you">Vytvorili ste miestnosÅ¥</string> - <string name="notice_room_invite_by_you">Pozvali ste %1$s</string> - <string name="notice_room_join_by_you">Vstúpili ste do miestnosti</string> - <string name="notice_room_leave_by_you">Opustili ste miestnosÅ¥</string> - <string name="notice_room_reject_by_you">Odmietli ste pozvanie</string> - <string name="notice_room_kick_by_you">Vykázali ste %1$s</string> - <string name="notice_room_unban_by_you">Povolili ste vstupovaÅ¥ %1$s</string> - <string name="notice_room_ban_by_you">Zakázali ste vstupovaÅ¥ %1$s</string> - <string name="notice_room_withdraw_by_you">Vzali ste späť pozvanie %1$s</string> - <string name="notice_avatar_url_changed_by_you">Zmenili ste si obrázok v profile</string> - <string name="notice_display_name_set_by_you">Nastavili ste si zobrazované meno %1$s</string> - <string name="notice_display_name_changed_from_by_you">Zmenili ste si zobrazované meno %1$s na %2$s</string> - <string name="notice_display_name_removed_by_you">Odstránili ste svoje zobrazované meno %1$s</string> - <string name="notice_room_topic_changed_by_you">Zmenili ste tému na: %1$s</string> - <string name="notice_room_avatar_changed">%1$s zmenil/a obrázok miestnosti</string> - <string name="notice_room_avatar_changed_by_you">Zmenili ste obrázok miestnosti</string> - <string name="notice_room_name_changed_by_you">Zmenili ste názov miestnosti na: %1$s</string> - <string name="notice_placed_video_call_by_you">UskutoÄnili ste video hovor.</string> - <string name="notice_placed_voice_call_by_you">Zatelefonovali ste.</string> - <string name="notice_call_candidates">%s poslal údaje pre nastavenie hovoru.</string> - <string name="notice_call_candidates_by_you">Poslali ste údaje na nastavenie hovoru.</string> - <string name="notice_answered_call_by_you">Prijali ste hovor.</string> - <string name="notice_ended_call_by_you">UkonÄili ste hovor.</string> - <string name="notice_made_future_room_visibility_by_you">SprÃstupnili ste budúcu históriu miestnosti %1$s</string> - <string name="notice_end_to_end_by_you">Povolili ste E2E Å¡ifrovanie (%1$s)</string> - <string name="notice_room_update_by_you">Aktualizovali ste túto miestnosÅ¥.</string> - <string name="notice_requested_voip_conference_by_you">Požiadali ste o VoIP konferenciu</string> - <string name="notice_room_name_removed_by_you">Odstránili ste názov miestnosti</string> - <string name="notice_room_topic_removed_by_you">Odstránili ste tému miestnosti</string> - <string name="notice_room_avatar_removed">%1$s odstránil obrázok miestnosti</string> - <string name="notice_room_avatar_removed_by_you">Odstránili ste obrázok miestnosti</string> - <string name="notice_profile_change_redacted_by_you">Aktualizovali ste svoj profil %1$s</string> - <string name="notice_room_third_party_invite_by_you">Pozvali ste %1$s vstúpiÅ¥ do miestnosti</string> - <string name="notice_room_third_party_revoked_invite_by_you">Zamietli ste pozvanie použÃvateľa %1$s vstúpiÅ¥ do miestnosti</string> - <string name="notice_room_third_party_registered_invite_by_you">Prijali ste pozvanie pre %1$s</string> - <string name="notice_widget_added">%1$s pridal/a widget %2$s</string> - <string name="notice_widget_added_by_you">Pridali ste widget %1$s</string> - <string name="notice_widget_removed">%1$s odstránil/a widget %2$s</string> - <string name="notice_widget_removed_by_you">Odstránili ste widget %1$s</string> - <string name="notice_widget_modified">%1$s upravil/a widget %2$s</string> - <string name="notice_widget_modified_by_you">Upravili ste widget %1$s</string> - <string name="power_level_admin">Správca</string> - <string name="power_level_moderator">Moderátor</string> - <string name="power_level_default">Predvolený</string> - <string name="power_level_custom">Vlastná úroveň (%1$d)</string> - <string name="power_level_custom_no_value">Vlastná úroveň</string> - <string name="notice_power_level_changed_by_you">Zmenili ste úroveň moci použÃvateľa %1$s.</string> - <string name="notice_power_level_changed">%1$s zmenil úroveň moci použÃvateľa %2$s.</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_by_you">Pozvanie od vás. Dôvod: %1$s</string> - <string name="notice_room_invite_with_reason_by_you">Pozvali ste %1$s. Dôvod: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Vstúpili ste do miestnosti. Dôvod: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Opustili ste miestnosÅ¥. Dôvod: %1$s</string> - <string name="notice_room_reject_with_reason_by_you">Odmietli ste pozvanie. Dôvod: %1$s</string> - <string name="notice_room_kick_with_reason_by_you">Vykázali ste %1$s. Dôvod: %2$s</string> - <string name="notice_room_unban_with_reason_by_you">Povolili ste vstupovaÅ¥ %1$s. Dôvod: %2$s</string> - <string name="notice_room_ban_with_reason_by_you">Zakázali ste vstupovaÅ¥ %1$s. Dôvod: %2$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Pozvali ste %1$s vstúpiÅ¥ do miestnosti. Dôvod: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Zamietli ste pozvanie použÃvateľa %1$s vstúpiÅ¥ do miestnosti. Dôvod: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Prijali ste pozvanie pre %1$s. Dôvod: %2$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Vzali ste späť pozvanie %1$s. Dôvod: %2$s</string> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Pridali ste adresu %1$s pre túto miestnosÅ¥.</item> - <item quantity="few">Pridali ste adresy %1$s pre túto miestnosÅ¥.</item> - <item quantity="other">Pridali ste adresy %1$s pre túto miestnosÅ¥.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Odstránili ste adresu %1$s pre túto miestnosÅ¥.</item> - <item quantity="few">Odstránili ste adresy %1$s pre túto miestnosÅ¥.</item> - <item quantity="other">Odstránili ste adresy %1$s pre túto miestnosÅ¥.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed_by_you">Pridali ste %1$s a odstránili adresy %2$s pre túto miestnosÅ¥.</string> - <string name="notice_room_canonical_alias_set_by_you">Nastavili ste hlavnú adresu tejto miestnosti %1$s.</string> - <string name="notice_room_canonical_alias_unset_by_you">Odstránili ste hlavnú adresu tejto miestnosti.</string> - <string name="notice_room_guest_access_can_join_by_you">Povolili ste hosÅ¥om///návÅ¡tevnÃkom prÃstup do tejto miestnosti.</string> - <string name="notice_room_guest_access_forbidden">%1$s zakázal/a hosÅ¥om///návÅ¡tevnÃkom prÃstup do tejto miestnosti.</string> - <string name="notice_room_guest_access_forbidden_by_you">Zakázali ste hosÅ¥om///návÅ¡tevnÃkom prÃstup do tejto miestnosti.</string> - <string name="notice_end_to_end_ok">%1$s povolil/a E2E Å¡ifrovanie.</string> - <string name="notice_end_to_end_ok_by_you">Povolili ste E2E Å¡ifrovanie.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s povolil/a E2E Å¡ifrovanie (Nerozpoznaný algorytmus %2$s).</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Povolili ste E2E Å¡ifrovanie (Nerozpoznaný algorytmus %1$s).</string> - <string name="key_verification_request_fallback_message">%s požaduje overenie vaÅ¡ich Å¡ifrovacÃch kľúÄov, ale váš klient nepodporuje overenie kľúÄov v konverzácii. Budete musieÅ¥ použiÅ¥ zastaralú metódu overenia.</string> - <string name="notice_room_server_acl_set_title_by_you">nastavili ste na servery pravidlá ACL tejto miestnosti.</string> - <string name="notice_room_server_acl_set_title">%s nastavil(a) na servery pravidlá ACL tejto miestnosti.</string> - <string name="notice_direct_room_update_by_you">Aktualizovali ste sem.</string> - <string name="notice_direct_room_update">%s aktualizoval(a) sem.</string> - <string name="notice_made_future_direct_room_visibility">%1$s sprÃstupnil(a) budúce správy %2$s</string> - <string name="notice_made_future_direct_room_visibility_by_you">SprÃstupnili ste budúce správy %1$s</string> - <string name="notice_direct_room_leave_by_you">Opustili ste miestnosÅ¥</string> - <string name="notice_direct_room_leave">%1$s opustil(a) miestnosÅ¥</string> - <string name="notice_direct_room_join_by_you">Vstúpili ste</string> - <string name="notice_direct_room_join">%1$s vstúpil(a)</string> - <string name="notice_direct_room_created_by_you">Vytvorili ste konverzáciu</string> - <string name="notice_direct_room_created">%1$s vytvoril(a) konverzáciu</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml deleted file mode 100644 index 0d4b2888ba016cbdd6628621c05b346308699320..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-sq/strings.xml +++ /dev/null @@ -1,257 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s dërgoi një figurë.</string> - <string name="notice_room_invite">%1$s ftoi %2$s</string> - <string name="notice_room_invite_you">%1$s ju ftoi</string> - <string name="notice_room_join">%1$s hyri në dhomë</string> - <string name="notice_room_leave">%1$s doli nga dhoma</string> - <string name="notice_room_reject">%1$s hodhi tej ftesën</string> - <string name="notice_room_kick">%1$s përzuri %2$s</string> - <string name="notice_room_ban">%1$s dëboi %2$s</string> - <string name="notice_avatar_url_changed">%1$s ndryshoi avatarin e vet</string> - <string name="notice_room_topic_changed">%1$s ndryshoi temën në: %2$s</string> - <string name="notice_room_name_changed">%1$s ndryshoi emrin e dhomës në: %2$s</string> - <string name="notice_placed_video_call">%s bëri një thirrje video.</string> - <string name="notice_placed_voice_call">%s bëri një thirrje zanore.</string> - <string name="notice_answered_call">%s iu përgjigj thirrjes.</string> - <string name="notice_ended_call">%s e përfundoi thirrjen.</string> - <string name="notice_made_future_room_visibility">%1$s e bëri historikun e ardhshëm të dhomës të dukshëm për %2$s</string> - <string name="notice_room_visibility_invited">për krejt anëtarët e dhomës, prej çastit kur janë ftuar.</string> - <string name="notice_room_visibility_joined">për krejt anëtarët e dhomës, prej çastit kur morën pjesë.</string> - <string name="notice_room_visibility_shared">krejt anëtarët e dhomës.</string> - <string name="notice_room_visibility_world_readable">cilido.</string> - <string name="notice_room_visibility_unknown">e panjohur (%s).</string> - <string name="notice_requested_voip_conference">%1$s kërkoi një konferencë VoIP</string> - <string name="notice_voip_started">Konferenca VoIP filloi</string> - <string name="notice_voip_finished">Konferenca VoIP përfundoi</string> - <string name="notice_avatar_changed_too">(u ndryshua edhe avatari)</string> - <string name="notice_room_name_removed">%1$s hoqi emrin e dhomës</string> - <string name="notice_profile_change_redacted">%1$s përditësoi profilin e tij %2$s</string> - <string name="notice_room_third_party_registered_invite">%1$s pranoi ftesën tuaj për %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** S’arrihet të shfshehtëzohet: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Pajisja e dërguesit nuk na ka dërguar kyçet për këtë mesazh.</string> - <string name="could_not_redact">S’u redaktua dot</string> - <string name="unable_to_send_message">S’arrihet të dërgohet mesazh</string> - <string name="message_failed_to_upload">Ngarkimi i figurës dështoi</string> - <string name="network_error">Gabim rrjeti</string> - <string name="matrix_error">Gabim Matrix</string> - <string name="room_error_join_failed_empty_room">Hëpërhë s’është e mundur të rihyhet në një dhomë të zbrazët.</string> - <string name="encrypted_message">Mesazh i fshehtëzuar</string> - <string name="medium_email">Adresë email</string> - <string name="medium_phone_number">Numër telefoni</string> - <string name="room_displayname_invite_from">Ftesë nga %s</string> - <string name="room_displayname_room_invite">Ftesë Dhome</string> - <string name="room_displayname_two_members">%1$s dhe %2$s</string> - <string name="room_displayname_empty_room">Dhomë e zbrazët</string> - <string name="summary_user_sent_sticker">%1$s dërgoi një ngjitës.</string> - <string name="notice_room_invite_no_invitee">Ftesë e %s</string> - <string name="notice_room_unban">%1$s hoqi dëbimin për %2$s</string> - <string name="notice_room_withdraw">%1$s tërhoqi mbrapsht ftesën për %2$s</string> - <string name="notice_display_name_set">%1$s caktoi për veten emër ekrani %2$s</string> - <string name="notice_display_name_changed_from">%1$s ndryshoi emrin e tyre në ekran nga %2$s në %3$s</string> - <string name="notice_display_name_removed">%1$s hoqi emrin e tij në ekran (%2$s)</string> - <string name="notice_end_to_end">%1$s aktivizoi fshehtëzim skaj-më-skaj (%2$s)</string> - <string name="notice_room_topic_removed">%1$s hoqi temën e dhomës</string> - <string name="notice_room_third_party_invite">%1$s dërgoi një ftesë për %2$s që të marrë pjesë në dhomë</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s dhe 1 tjetër</item> - <item quantity="other">%1$s dhe %2$d të tjerë</item> - </plurals> - <string name="notice_event_redacted">Mesazhi u hoq</string> - <string name="notice_event_redacted_by">Mesazhi u hoq nga %1$s</string> - <string name="notice_event_redacted_with_reason">Mesazh i hequr [arsye: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Mesazh i hequr nga %1$s [arsye: %2$s]</string> - <string name="notice_room_update">%s e përmirësoi këtë dhomë.</string> - <string name="initial_sync_start_importing_account">Njëkohësimi Fillestar: -\nPo importohet llogaria…</string> - <string name="initial_sync_start_importing_account_crypto">Njëkohësimi Fillestar: -\nPo importohet kriptografi</string> - <string name="initial_sync_start_importing_account_rooms">Njëkohësimi Fillestar: -\nPo importohen Dhoma</string> - <string name="initial_sync_start_importing_account_joined_rooms">Njëkohësimi Fillestar: -\nPo importohen Dhoma Ku Është Bërë Hyrje</string> - <string name="initial_sync_start_importing_account_invited_rooms">Njëkohësimi Fillestar: -\nPo importohen Dhoma Me Ftesë</string> - <string name="initial_sync_start_importing_account_left_rooms">Njëkohësimi Fillestar: -\nPo importohen Dhoma të Braktisura</string> - <string name="initial_sync_start_importing_account_groups">Njëkohësimi Fillestar: -\nPo importohen Bashkësi</string> - <string name="initial_sync_start_importing_account_data">Njëkohësimi Fillestar: -\nPo importohet të Dhëna Llogarie</string> - <string name="event_status_sending_message">Po dërgohet mesazh…</string> - <string name="clear_timeline_send_queue">Spastro radhë pritjeje</string> - <string name="notice_room_third_party_revoked_invite">%1$s shfuqizoi ftesën për %2$s për pjesëmarrje te dhoma</string> - <string name="notice_room_invite_no_invitee_with_reason">Ftesë e %1$s. Arsye: %2$s</string> - <string name="notice_room_invite_with_reason">%1$s ftoi %2$s. Arsye: %3$s</string> - <string name="notice_room_invite_you_with_reason">%1$s ju ftoi. Arsye: %2$s</string> - <string name="notice_room_join_with_reason">%1$s erdhi në dhomë. Arsye: %2$s</string> - <string name="notice_room_leave_with_reason">%1$s doli nga dhoma. Arsye: %2$s</string> - <string name="notice_room_reject_with_reason">%1$s hodhi poshtë ftesën. Arsye: %2$s</string> - <string name="notice_room_kick_with_reason">%1$s përzuri %2$s. Arsye: %3$s</string> - <string name="notice_room_unban_with_reason">%1$s hoqi dëbimin për %2$s. Arsye: %3$s</string> - <string name="notice_room_ban_with_reason">%1$s dëboi %2$s. Arsye: %3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s dërgoi një ftesë për %2$s për të ardhur në dhomë. Arsye: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s shfuqizoi ftesën për %2$s për të ardhur në dhomë. Arsye: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s pranoi ftesën për %2$s. Arsye: %3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s tërhoqi mbrapsht ftesën për %2$s. Arsye: %3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s shtoi %2$s si një adresë për këtë dhomë.</item> - <item quantity="other">%1$s shtoi %2$s si adresa për këtë dhomë.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s hoqi %2$s si adresë për këtë dhomë.</item> - <item quantity="other">%1$s hoqi %2$s si adresa për këtë dhomë.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s shtoi %2$s dhe hoqi %3$s si adresa për këtë dhomë.</string> - <string name="notice_room_canonical_alias_set">%1$s caktoi %2$s si adresë kryesore për këtë dhomë.</string> - <string name="notice_room_canonical_alias_unset">%1$s hoqi adresën kryesore për këtë dhomë.</string> - <string name="notice_room_guest_access_can_join">%1$s ka lejuar vizitorë të marrin pjesë në dhomë.</string> - <string name="notice_room_guest_access_forbidden">%1$s ka penguar vizitorë të marrin pjesë në dhomë.</string> - <string name="notice_end_to_end_ok">%1$s aktivizoi fshehtëzim skaj-më-skaj.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s aktivizoi fshehtëzim skaj-më-skaj (algoritëm i papranuar %2$s).</string> - <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 t’ju 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> - <string name="notice_direct_room_guest_access_forbidden_by_you">Keni penguar të vijnë në dhomë mysafirë.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s ka penguar të vijnë në dhomë mysafirë.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Keni lejuar të vijnë mysafirë këtu.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s ka lejuar të vijnë këtu mysafirë.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Dolët. Arsye: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s doli. Arsye: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Erdhët. Arsye: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s erdhi. Arsye: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Shfuqizuat ftesën për %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s shfuqizoi ftesën për %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Ftuat %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s ftoi %2$s</string> - <string name="notice_direct_room_update_by_you">U përmirësuat këtu.</string> - <string name="notice_direct_room_update">%s këtu u përmirësua.</string> - <string name="notice_made_future_direct_room_visibility_by_you">I bëtë mesazhet e ardhshëm të dukshëm për %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s i bëri mesazhet e ardhshëm të dukshëm për %2$s</string> - <string name="notice_direct_room_leave_by_you">Dolët nga dhoma</string> - <string name="notice_direct_room_leave">%1$s doli nga dhoma</string> - <string name="notice_direct_room_join_by_you">Erdhët</string> - <string name="notice_direct_room_join">%1$s erdhi</string> - <string name="notice_direct_room_created_by_you">Krijuat diskutimin</string> - <string name="notice_direct_room_created">%1$s krijoi diskutimin</string> - <string name="room_displayname_empty_room_was">Dhomë e zbrazët (was %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s dhe %4$d tjetër</item> - <item quantity="other">%1$s, %2$s, %3$s dhe %4$d të tjerë</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s dhe %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s dhe %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 U është penguar pjesëmarrja krejt shërbyesve! Kjo dhomë s’mund të përdoret më.</string> - <string name="notice_room_server_acl_updated_no_change">Pa ndryshim.</string> - <string name="notice_room_server_acl_updated_title_by_you">Ndryshuat ACL-ra shërbyesi për këtë dhomë.</string> - <string name="notice_room_server_acl_updated_title">%s ndryshoi ACL-ra shërbyesi për këtë dhomë.</string> - <string name="notice_room_server_acl_set_title_by_you">Ujdisët ACL-ra shërbyesi për këtë dhomë.</string> - <string name="notice_room_server_acl_set_title">%s ujdisi ACL-ra shërbyesi për këtë dhomë.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Shërbyes që kanë përputhje me %s u hoqën nga lista e të lejuarve.</string> - <string name="notice_room_server_acl_updated_allowed">• Shërbyesit që kanë përputhje me %s tani janë të lejuar.</string> - <string name="notice_room_server_acl_updated_was_banned">• Shërbyesit që kanë përputhje me %s u hoqën nga lista e ndalimeve.</string> - <string name="notice_room_server_acl_updated_banned">• Shërbyesit që kanë përputhje me %s tani janë të ndaluar.</string> - <string name="notice_room_server_acl_set_allowed">• Shërbyesit që kanë përputhje me %s janë të ndaluar.</string> - <string name="notice_room_server_acl_set_banned">• Shërbyesit që kanë përputhje me %s janë të ndaluar.</string> - <string name="notice_room_canonical_alias_no_change_by_you">Ndryshuat adresat për këtë dhomë.</string> - <string name="notice_room_canonical_alias_no_change">%1$s ndryshoi adresat për këtë dhomë.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Ndryshuat adresat kryesore dhe alternative për këtë dhomë.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s ndryshoi adresat kryesore dhe alternative për këtë dhomë.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Ndryshuat adresat alternative për këtë dhomë.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s ndryshoi adresat alternative për këtë dhomë.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Hoqët adresën alternative %1$s për këtë dhomë.</item> - <item quantity="other">Hoqët adresat alternative %1$s për këtë dhomë.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s hoqët adresën alternative %2$s për këtë dhomë.</item> - <item quantity="other">%1$s hoqët adresat alternative %2$s për këtë dhomë.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Shtuat adresën alternative %1$s për këtë dhomë.</item> - <item quantity="other">Shtuat adresat alternative %1$s për këtë dhomë.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s shtoi adresën alternative %2$s për këtë dhomë.</item> - <item quantity="other">%1$s shtoi adresat alternative %2$s për këtë dhomë.</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-sr/strings_sas.xml b/matrix-sdk-android/src/main/res/values-sr/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..04da7a11f0858ab8ae83ef5ec544438851e811c2 --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-sr/strings_sas.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> + <string name="verification_emoji_dog">паÑ</string> + <string name="verification_emoji_cat">мачка</string> + <string name="verification_emoji_lion">лав</string> + <string name="verification_emoji_horse">коњ</string> + <string name="verification_emoji_unicorn">једнорог</string> + <string name="verification_emoji_pig">праÑе</string> + <string name="verification_emoji_elephant">Ñлон</string> + <string name="verification_emoji_rabbit">зец</string> + <string name="verification_emoji_panda">панда</string> + <string name="verification_emoji_rooster">петао</string> + <string name="verification_emoji_penguin">пингвин</string> + <string name="verification_emoji_turtle">корњача</string> + <string name="verification_emoji_fish">риба</string> + <string name="verification_emoji_octopus">октопод</string> + <string name="verification_emoji_butterfly">лептир</string> + <string name="verification_emoji_flower">цвет</string> + <string name="verification_emoji_tree">дрво</string> + <string name="verification_emoji_cactus">кактуÑ</string> + <string name="verification_emoji_mushroom">печурка</string> + <string name="verification_emoji_globe">глобуÑ</string> + <string name="verification_emoji_moon">меÑец</string> + <string name="verification_emoji_cloud">облак</string> + <string name="verification_emoji_fire">ватра</string> + <string name="verification_emoji_banana">банана</string> + <string name="verification_emoji_apple">јабука</string> + <string name="verification_emoji_strawberry">јагода</string> + <string name="verification_emoji_corn">кукуруз</string> + <string name="verification_emoji_pizza">пица</string> + <string name="verification_emoji_cake">торта</string> + <string name="verification_emoji_heart">Ñрце</string> + <string name="verification_emoji_smiley">Ñмајли</string> + <string name="verification_emoji_robot">робот</string> + <string name="verification_emoji_hat">шешир</string> + <string name="verification_emoji_glasses">наочаре</string> + <string name="verification_emoji_spanner">кључ</string> + <string name="verification_emoji_santa">деда Мраз</string> + <string name="verification_emoji_thumbs_up">палчић горе</string> + <string name="verification_emoji_umbrella">кишобран</string> + <string name="verification_emoji_hourglass">пешчаник</string> + <string name="verification_emoji_clock">Ñат</string> + <string name="verification_emoji_gift">поклон</string> + <string name="verification_emoji_light_bulb">Ñијалица</string> + <string name="verification_emoji_book">књига</string> + <string name="verification_emoji_pencil">оловка</string> + <string name="verification_emoji_paperclip">Ñпајалица</string> + <string name="verification_emoji_scissors">маказе</string> + <string name="verification_emoji_lock">катанац</string> + <string name="verification_emoji_key">кључ</string> + <string name="verification_emoji_hammer">чекић</string> + <string name="verification_emoji_telephone">телефон</string> + <string name="verification_emoji_flag">заÑтава</string> + <string name="verification_emoji_train">воз</string> + <string name="verification_emoji_bicycle">бицикл</string> + <string name="verification_emoji_aeroplane">авион</string> + <string name="verification_emoji_rocket">ракета</string> + <string name="verification_emoji_trophy">пехар</string> + <string name="verification_emoji_ball">лопта</string> + <string name="verification_emoji_guitar">гитара</string> + <string name="verification_emoji_trumpet">труба</string> + <string name="verification_emoji_bell">звоно</string> + <string name="verification_emoji_anchor">Ñидро</string> + <string name="verification_emoji_headphones">Ñлушалице</string> + <string name="verification_emoji_folder">фаÑцикла</string> + <string name="verification_emoji_pin">чиода</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-sv/strings.xml b/matrix-sdk-android/src/main/res/values-sv/strings.xml deleted file mode 100644 index 91d874591f5e033d41c115c3799798ccd0f48e97..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-sv/strings.xml +++ /dev/null @@ -1,261 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s skickade en bild.</string> - <string name="summary_you_sent_image">Du skickade en bild.</string> - <string name="summary_user_sent_sticker">%1$s skickade en dekal.</string> - <string name="summary_you_sent_sticker">Du skickade en dekal.</string> - <string name="notice_room_invite_no_invitee">Inbjudan frÃ¥n %s</string> - <string name="notice_room_invite_no_invitee_by_you">Inbjudan frÃ¥n dig</string> - <string name="notice_room_created">%1$s skapade rummet</string> - <string name="notice_room_created_by_you">Du skapade rummet</string> - <string name="notice_room_invite">%1$s bjöd in %2$s</string> - <string name="notice_room_invite_by_you">Du bjöd in %1$s</string> - <string name="notice_room_invite_you">%1$s bjöd in dig</string> - <string name="notice_room_join">%1$s gick med i rummet</string> - <string name="notice_room_join_by_you">Du gick med i rummet</string> - <string name="notice_room_leave">%1$s lämnade rummet</string> - <string name="notice_room_leave_by_you">Du lämnade rummet</string> - <string name="notice_room_reject">%1$s avböjde inbjudan</string> - <string name="notice_room_reject_by_you">Du avböjde inbjudan</string> - <string name="notice_room_kick">%1$s kickade %2$s</string> - <string name="notice_room_kick_by_you">Du kickade %1$s</string> - <string name="notice_room_unban">%1$s avbannade %2$s</string> - <string name="notice_room_unban_by_you">Du avbannade %1$s</string> - <string name="notice_room_ban">%1$s avbannade %2$s</string> - <string name="notice_room_ban_by_you">Du bannade %1$s</string> - <string name="notice_room_withdraw">%1$s drog tillbaka inbjudan för %2$s</string> - <string name="notice_room_withdraw_by_you">Du drog tillbaka inbjudan för %1$s</string> - <string name="notice_avatar_url_changed">%1$s bytte sin avatar</string> - <string name="notice_avatar_url_changed_by_you">Du bytte din avatar</string> - <string name="notice_display_name_set">%1$s satte sitt visningsnamn till %2$s</string> - <string name="notice_display_name_set_by_you">Du satte ditt visningsnamn till %1$s</string> - <string name="notice_display_name_changed_from">%1$s bytte sitt visningsnamn frÃ¥n %2$s till %3$s</string> - <string name="notice_display_name_changed_from_by_you">Du bytte ditt visningsnamn frÃ¥n %1$s till %2$s</string> - <string name="notice_display_name_removed">%1$s tog bort sitt visningsnamn (det var %2$s)</string> - <string name="notice_display_name_removed_by_you">Du tog bort ditt visningsnamn (det var %1$s)</string> - <string name="notice_room_topic_changed">%1$s bytte ämnet till: %2$s</string> - <string name="notice_room_topic_changed_by_you">Du bytte ämnet till: %1$s</string> - <string name="notice_room_avatar_changed">%1$s bytte rummets avatar</string> - <string name="notice_room_avatar_changed_by_you">Du bytte rummets avatar</string> - <string name="notice_room_name_changed">%1$s bytte rummets namn till: %2$s</string> - <string name="notice_room_name_changed_by_you">Du bytte rummets namnet till: %1$s</string> - <string name="notice_placed_video_call">%s startade ett videosamtal.</string> - <string name="notice_placed_video_call_by_you">Du startade ett videosamtal.</string> - <string name="notice_placed_voice_call">%s startade ett röstsamtal.</string> - <string name="notice_placed_voice_call_by_you">Du startade ett röstsamtal.</string> - <string name="notice_call_candidates">%s skickade data för att sätta upp samtalet.</string> - <string name="notice_call_candidates_by_you">Du skickade data för att sätta upp samtalet.</string> - <string name="notice_answered_call">%s svarade pÃ¥ samtalet.</string> - <string name="notice_answered_call_by_you">Du svarade pÃ¥ samtalet.</string> - <string name="notice_ended_call">%s avslutade samtalet.</string> - <string name="notice_ended_call_by_you">Du avslutade samtalet.</string> - <string name="notice_made_future_room_visibility">%1$s gjorde framtida rumshistorik synlig för %2$s</string> - <string name="notice_made_future_room_visibility_by_you">Du gjorde framtida rumshistorik synlig för %1$s</string> - <string name="notice_room_visibility_invited">alla rumsmedlemmar, frÃ¥n tiden de bjöds in.</string> - <string name="notice_room_visibility_joined">alla rumsmedlemmar, frÃ¥n tiden de gick med.</string> - <string name="notice_room_visibility_shared">alla rumsmedlemmar.</string> - <string name="notice_room_visibility_world_readable">vem som helst.</string> - <string name="notice_room_visibility_unknown">okänt (%s).</string> - <string name="notice_end_to_end">%1$s aktiverade totalsträckskryptering (%2$s)</string> - <string name="notice_end_to_end_by_you">Du aktiverade totalsträckskryptering (%1$s)</string> - <string name="notice_room_update">%s uppgraderade det här rummet.</string> - <string name="notice_room_update_by_you">Du uppgraderade det här rummet.</string> - <string name="notice_requested_voip_conference">%1$s begärde ett VoIP-gruppsamtal</string> - <string name="notice_requested_voip_conference_by_you">Du begärde ett VoIP-gruppsamtal</string> - <string name="notice_voip_started">VoIP-gruppsamtal startat</string> - <string name="notice_voip_finished">VoIP-gruppsamtal avslutat</string> - <string name="notice_avatar_changed_too">(avataren blev även bytt)</string> - <string name="notice_room_name_removed">%1$s tog bort rummets namn</string> - <string name="notice_room_name_removed_by_you">Du tog bort rummets namn</string> - <string name="notice_room_topic_removed">%1$s tog bort rummets ämne</string> - <string name="notice_room_topic_removed_by_you">Du tog bort rummets ämne</string> - <string name="notice_room_avatar_removed">%1$s tog bort rummets avatar</string> - <string name="notice_room_avatar_removed_by_you">Du tog bort rummets avatar</string> - <string name="notice_event_redacted">Meddelande borttaget</string> - <string name="notice_event_redacted_by">Meddelande borttaget av %1$s</string> - <string name="notice_event_redacted_with_reason">Meddelande borttaget [anledning: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Meddelande borttaget av %1$s [anledning: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s uppdaterade sim profil %2$s</string> - <string name="notice_profile_change_redacted_by_you">Du uppdaterade din profil %1$s</string> - <string name="notice_room_third_party_invite">%1$s bjöd in %2$s att gÃ¥ med i rummet</string> - <string name="notice_room_third_party_invite_by_you">Du bjöd in %1$s att gÃ¥ med i rummet</string> - <string name="notice_room_third_party_revoked_invite">%1$s drog tillbaka inbjudan för %2$s att gÃ¥ med i rummet</string> - <string name="notice_room_third_party_revoked_invite_by_you">Du drog tillbaka inbjudan för %1$s att gÃ¥ med i rummet</string> - <string name="notice_room_third_party_registered_invite">%1$s accepterade inbjudan för %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">Du accepterade inbjudan för %1$s</string> - <string name="notice_widget_added">%1$s lade till %2$s-widget</string> - <string name="notice_widget_added_by_you">Du lade till %1$s-widget</string> - <string name="notice_widget_removed">%1$s tog bort %2$s-widget</string> - <string name="notice_widget_removed_by_you">Du tog bort %1$s-widget</string> - <string name="notice_widget_modified">%1$s modifierade %2$s-widget</string> - <string name="notice_widget_modified_by_you">Du modifierade %1$s-widget</string> - <string name="power_level_admin">Admin</string> - <string name="power_level_moderator">Moderator</string> - <string name="power_level_default">Standard</string> - <string name="power_level_custom">Anpassad (%1$d)</string> - <string name="power_level_custom_no_value">Anpassad</string> - <string name="notice_power_level_changed_by_you">Du ändrade behörighetsnivÃ¥ för %1$s.</string> - <string name="notice_power_level_changed">%1$s ändrade behörighetsnivÃ¥ för %2$s.</string> - <string name="notice_power_level_diff">%1$s frÃ¥n %2$s till %3$s</string> - <string name="notice_crypto_unable_to_decrypt">** Kan inte avkryptera: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">Avsändarens enhet har inte gett oss nycklarna för det här meddelandet.</string> - <string name="could_not_redact">Kunde inte dölja</string> - <string name="unable_to_send_message">Kunde inte skicka meddelandet</string> - <string name="message_failed_to_upload">Misslyckades att ladda upp bilden</string> - <string name="network_error">Nätverksfel</string> - <string name="matrix_error">Matrixfel</string> - <string name="room_error_join_failed_empty_room">Det gÃ¥r för närvarande inte att gÃ¥ med i ett tomt rum igen.</string> - <string name="encrypted_message">Krypterat meddelande</string> - <string name="medium_email">E-postadress</string> - <string name="medium_phone_number">Telefonnummer</string> - <string name="room_displayname_invite_from">Inbjudan frÃ¥n %s</string> - <string name="room_displayname_room_invite">Rumsinbjudan</string> - <string name="room_displayname_two_members">%1$s och %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s och en till</item> - <item quantity="other">%1$s och %2$d till</item> - </plurals> - <string name="room_displayname_empty_room">Tomt rum</string> - <string name="initial_sync_start_importing_account">Inledande synk: -\nImporterar konto…</string> - <string name="initial_sync_start_importing_account_crypto">Inledande synk: -\nImporterar krypto</string> - <string name="initial_sync_start_importing_account_rooms">Inledande synk: -\nImporterar rum</string> - <string name="initial_sync_start_importing_account_joined_rooms">Inledande synk: -\nImporterar anslutna rum</string> - <string name="initial_sync_start_importing_account_invited_rooms">Inledande synk: -\nImporterar inbjudna rum</string> - <string name="initial_sync_start_importing_account_left_rooms">Inledande synk: -\nImporterar lämnade rum</string> - <string name="initial_sync_start_importing_account_groups">Inledande synk: -\nImporterar gemenskaper</string> - <string name="initial_sync_start_importing_account_data">Inledande synk: -\nImporterar kontodata</string> - <string name="event_status_sending_message">Skickar meddelande…</string> - <string name="clear_timeline_send_queue">Rensa sändningskö</string> - <string name="notice_room_invite_no_invitee_with_reason">Inbjudan frÃ¥n %1$s. Anledning: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Inbjudan frÃ¥n dig. Anledning: %1$s</string> - <string name="notice_room_invite_with_reason">%1$s bjöd in %2$s. Anledning: %3$s</string> - <string name="notice_room_invite_with_reason_by_you">Du bjöd in %1$s. Anledning: %2$s</string> - <string name="notice_room_invite_you_with_reason">%1$s bjöd in dig. Anledning: %2$s</string> - <string name="notice_room_join_with_reason">%1$s gick med i rummet. Anledning: %2$s</string> - <string name="notice_room_join_with_reason_by_you">Du gick med i rummet. Anledning: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s lämnade rummet. Anledning: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Du lämnade rummet. Anledning: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s avböjde inbjudan. Anledning: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">Du avböjde inbjudan. Anledning: %1$s</string> - <string name="notice_room_kick_with_reason">%1$s kickade %2$s. Anledning: %3$s</string> - <string name="notice_room_kick_with_reason_by_you">Du kickade %1$s. Anledning: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s avbannade %2$s. Anledning: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">Du avbannade %1$s. Anledning: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s bannade %2$s. Anledning: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">Du bannade %1$s. Anledning: %2$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s bjöd in %2$s att gÃ¥ med i rummet. Anledning: %3$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">Du bjöd in %1$s att gÃ¥ med i rummet. Anledning: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s drog tillbaka inbjudan för %2$s att gÃ¥ med i rummet. Anledning: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Du drog tillbaka inbjudan för %1$s att gÃ¥ med i rummet. Anledning: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s accepterade inbjudan för %2$s. Anledning: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">Du accepterade inbjudan för %1$s. Anledning: %2$s</string> - <string name="notice_room_withdraw_with_reason">%1$s drog tillbaka inbjudan för %2$s. Anledning: %3$s</string> - <string name="notice_room_withdraw_with_reason_by_you">Du drog tillbaka inbjudan för %1$s. Anledning: %2$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s lade till %2$s som en adress för det här rummet.</item> - <item quantity="other">%1$s lade till %2$s som adresser för det här rummet.</item> - </plurals> - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">Du lade till %1$s som en adress för det här rummet.</item> - <item quantity="other">Du lade till %1$s som adresser för det här rummet.</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s tog bort %2$s som en adress för det här rummet.</item> - <item quantity="other">%1$s tog bort %2$s som adresser för det här rummet.</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">Du tog bort %1$s som en adress för det här rummet.</item> - <item quantity="other">Du tog bort %1$s som adresser för det här rummet.</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s lade till %2$s och tog bort %3$s som adresser för det här rummet.</string> - <string name="notice_room_aliases_added_and_removed_by_you">Du lade till %1$s och tog bort %2$s som adresser för det här rummet.</string> - <string name="notice_room_canonical_alias_set">%1$s satta huvudadressen för det här rummet till %2$s.</string> - <string name="notice_room_canonical_alias_set_by_you">Du satta huvudadressen för det här rummet till %1$s.</string> - <string name="notice_room_canonical_alias_unset">%1$s tog bort huvudadressen för det här rummet.</string> - <string name="notice_room_canonical_alias_unset_by_you">Du tog bort huvudadressen för det här rummet.</string> - <string name="notice_room_guest_access_can_join">%1$s tillät gäster att gÃ¥ med i rummet.</string> - <string name="notice_room_guest_access_can_join_by_you">Du tillät gäster att gÃ¥ med i rummet.</string> - <string name="notice_room_guest_access_forbidden">%1$s hindrade gäster frÃ¥n att gÃ¥ med i rummet.</string> - <string name="notice_room_guest_access_forbidden_by_you">Du hindrade gäster frÃ¥n att gÃ¥ med i rummet.</string> - <string name="notice_end_to_end_ok">%1$s aktiverade totalsträckskryptering.</string> - <string name="notice_end_to_end_ok_by_you">Du aktiverade totalsträckskryptering.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s aktiverade totalsträckskryptering (okänd algoritm %2$s).</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Du aktiverade totalsträckskryptering (okänd algoritm %1$s).</string> - <string name="key_verification_request_fallback_message">%s begär att verifiera din nyckel, men din klient stöder inte nyckelverifiering i chatten. Du behöver använda legacynyckelverifiering för att verifiera nycklar.</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">Du hindrade gäster frÃ¥n att gÃ¥ med i rummet.</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s hindrade gäster frÃ¥n att gÃ¥ med i rummet.</string> - <string name="notice_direct_room_guest_access_can_join_by_you">Du tillät gäster att gÃ¥ med här.</string> - <string name="notice_direct_room_guest_access_can_join">%1$s tillät gäster att gÃ¥ med här.</string> - <string name="notice_direct_room_leave_with_reason_by_you">Du lämnade. Anledning: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s Lämnade. Anledning: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">Du gick med. Anledning: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s gick med. Anledning: %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">Du drog tillbaka inbjudan för %1$s</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s drog tillbaka inbjudan för %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">Du bjöd in %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s bjöd in %2$s</string> - <string name="notice_direct_room_update_by_you">Du uppgraderade här.</string> - <string name="notice_direct_room_update">%s uppgraderade här.</string> - <string name="notice_made_future_direct_room_visibility_by_you">Du gjorde framtida meddelanden synliga för %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s gjorde framtida meddelanden synliga för %2$s</string> - <string name="notice_direct_room_leave_by_you">Du lämnade rummet</string> - <string name="notice_direct_room_leave">%1$s lämnade rummet</string> - <string name="notice_direct_room_join_by_you">Du gick med</string> - <string name="notice_direct_room_join">%1$s gick med</string> - <string name="notice_direct_room_created_by_you">Du skapade diskussionen</string> - <string name="notice_direct_room_created">%1$s skapade diskussionen</string> - <string name="notice_room_canonical_alias_no_change_by_you">Du ändrade adresserna för det här rummet.</string> - <string name="notice_room_canonical_alias_no_change">%1$s ändrade adresserna för det här rummet.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Du ändrade huvudadressen och de alternativa adresserna för det här rummet.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s ändrade huvudadressen och de alternativa adresserna för det här rummet.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Du ändrade de alternativa adresserna för det här rummet.</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s ändrade de alternativa adresserna för det här rummet.</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">Du tog bort den alternativa adressen %1$s för det här rummet.</item> - <item quantity="other">Du tog bort de alternativa adresserna %1$s för det här rummet.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s tog bort den alternativa adressen %2$s för det här rummet.</item> - <item quantity="other">%1$s tog bort de alternativa adresserna %2$s för det här rummet.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">Du lade till den alternativa adressen %1$s för det här rummet.</item> - <item quantity="other">Du lade till de alternativa adresserna %1$s för det här rummet.</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s lade till den alternativa adressen %2$s för det här rummet.</item> - <item quantity="other">%1$s lade till de alternativa adresserna %2$s för det här rummet.</item> - </plurals> - <string name="room_displayname_empty_room_was">Tomt rum (var %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s och %4$d till</item> - <item quantity="other">%1$s, %2$s, %3$s och %4$d till</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s och %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s och %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 Alla servrar har bannats frÃ¥n att delta! Det här rummet kan inte användas längre.</string> - <string name="notice_room_server_acl_updated_no_change">Ingen ändring.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Servrar som matchar IP-adresser är nu bannade.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Servrar som matchar IP-adresser är nu tillÃ¥tna.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Servrar som matchar %s togs bort frÃ¥n tillÃ¥telselistan.</string> - <string name="notice_room_server_acl_updated_allowed">• Servrar som matchar %s är nu tillÃ¥tna.</string> - <string name="notice_room_server_acl_updated_was_banned">• Servrar som matchar %s togs bort frÃ¥n bannlistan.</string> - <string name="notice_room_server_acl_updated_banned">• Servrar som matchar %s är nu bannade.</string> - <string name="notice_room_server_acl_updated_title_by_you">Du ändrade server-ACLer för det här rummet.</string> - <string name="notice_room_server_acl_updated_title">%s ändrade server-ACLer för det här rummet.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Servrar som matchar IP-adresser är tillÃ¥tna.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Servrar som matchar IP-adresser är bannade.</string> - <string name="notice_room_server_acl_set_allowed">• Servrar som matchar %s är tillÃ¥tna.</string> - <string name="notice_room_server_acl_set_banned">• Servrar som matchar %s är bannade.</string> - <string name="notice_room_server_acl_set_title_by_you">Du satte server-ACLer för det här rummet.</string> - <string name="notice_room_server_acl_set_title">%s satte server-ACLer för det här rummet.</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-szl/strings.xml b/matrix-sdk-android/src/main/res/values-szl/strings.xml deleted file mode 100644 index a6b3daec9354f9ae75cdf8d94a67446c6227dd96..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-szl/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources></resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-szl/strings_sas.xml b/matrix-sdk-android/src/main/res/values-szl/strings_sas.xml new file mode 100644 index 0000000000000000000000000000000000000000..9769ad73cec0f8c98cf311662b0dae05ab375c9c --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-szl/strings_sas.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Generated file, do not edit --> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-te/strings.xml b/matrix-sdk-android/src/main/res/values-te/strings.xml deleted file mode 100644 index 62f58c9e26f4fe90b2a88e8783e065975cc6b5f0..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-te/strings.xml +++ /dev/null @@ -1,71 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="notice_room_invite_no_invitee">%s\'s ఆహà±à°µà°¾à°¨à°‚</string> - <string name="notice_room_invite">%1$s ఆహà±à°µà°¾à°¨à°¿à°‚చారౠ%2$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\'s ఆహà±à°µà°¾à°¨à°‚</string> - <string name="notice_avatar_url_changed">%1$s వారి అవతారà±à°¨à± మారà±à°šà°¾à°°à±</string> - <string name="notice_display_name_set">%1$s వారి à°¡à°¿à°¸à±à°ªà±à°²à±‡ పేరà±à°¨à± ని సెటౠచేసారౠ%2$s</string> - <string name="notice_display_name_changed_from">%1$s వారి à°ªà±à°°à°¦à°°à±à°¶à°¨ పేరà±à°¨à± %2$s à°¨à±à°‚à°¡à°¿ %3$s మారà±à°šà°¾à°°à±</string> - <string name="notice_display_name_removed">%1$s వారి à°ªà±à°°à°¦à°°à±à°¶à°¨ పేరà±à°¨à°¿ తీసివేసారౠ(%2$s)</string> - <string name="notice_room_topic_changed">%1$s అంశం మారà±à°šà°¬à°¡à°¿à°‚ది:%2$s</string> - <string name="notice_room_name_changed">%1$s గది పెరౠమారà±à°šà°¬à°¡à°¿à°‚ది %2$s</string> - <string name="notice_placed_video_call">%s à°’à°• వీడియో కాలà±à°¨à°¿ ఉంచింది.</string> - <string name="notice_placed_voice_call">%s వాయిసౠకాలà±à°¨à°¿ ఉంచారà±.</string> - <string name="notice_answered_call">%s కాలà±à°•à°¿ సమాధానం ఇచà±à°šà°¾à°°à±.</string> - <string name="notice_ended_call">%s కాలౠమà±à°—ిసింది.</string> - <string name="notice_made_future_room_visibility">%1$s à°à°µà°¿à°·à±à°¯à°¤à± గది à°šà°°à°¿à°¤à±à°°à°¨à± %2$s à°•à°¿ కనిపించేలా చేసింది</string> - <string name="notice_room_visibility_invited">పాయింటà±à°¨à±à°‚à°¡à°¿, à°…à°¨à±à°¨à°¿ గది à°¸à°à±à°¯à±à°² వారౠఆహà±à°µà°¾à°¨à°¿à°‚చబడà±à°¡à°¾à°°à±.</string> - <string name="notice_room_visibility_joined">పాయింటౠనà±à°‚à°¡à°¿, à°…à°¨à±à°¨à°¿ à°—à°¦à±à°² à°¸à°à±à°¯à±à°² వారౠచేరారà±.</string> - <string name="notice_room_visibility_shared">à°…à°¨à±à°¨à°¿ à°—à°¦à±à°² à°¸à°à±à°¯à±à°²à±.</string> - <string name="notice_room_visibility_world_readable">ఎవరైనా.</string> - <string name="notice_room_visibility_unknown">తెలియని (%s).</string> - <string name="notice_end_to_end">%1$s à°Žà°‚à°¡à±-à°Ÿà±-à°Žà°‚à°¡à± à°Žà°¨à±à°•à±à°°à°¿à°ªà±à°·à°¨à± ఆనౠచెయà±à°¯à°¬à°¡à°¿à°‚ది (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s వి à°“ à°‡ పి సమావేశానà±à°¨à°¿ à°…à°à±à°¯à°°à±à°¥à°¿à°‚చారà±</string> - <string name="notice_voip_started">వి à°“ à°‡ పి సమావేశం à°ªà±à°°à°¾à°°à°‚à°à°®à±†à±–ంది</string> - <string name="notice_voip_finished">వి à°“ à°‡ పి సమావేశం à°®à±à°—ిసింది</string> - - <string name="notice_avatar_changed_too">(అవతారౠమారà±à°šà°¬à°¡à°¿à°‚ది)</string> - <string name="notice_room_name_removed">%1$s గది పేరౠతొలగించబడింది</string> - <string name="notice_room_topic_removed">%1$s గది అంశానà±à°¨à°¿ తీసివేసారà±</string> - <string name="notice_profile_change_redacted">%1$s వారి à°ªà±à°°à±Šà°«à±†à±–లౠనవీకరించబడింది %2$s</string> - <string name="notice_room_third_party_invite">%1$s గదిలో చేరడానికి %2$s కౠఆహà±à°µà°¾à°¨à°¾à°¨à±à°¨à°¿ పంపారà±</string> - <string name="notice_room_third_party_registered_invite">%2$sకోసం %1$s ఆహà±à°µà°¾à°¨à°¾à°¨à±à°¨à°¿ అంగీకరించారà±</string> - - <string name="notice_crypto_unable_to_decrypt">** à°µà±à°¯à°•à±à°¤à±€à°•à°°à°¿à°‚చడానికి సాధà±à°¯à°‚ కాలేదà±: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">à°ˆ సందేశానికి పంపేవారి పరికరం మాకౠకీలనౠపంపలేదà±.</string> - - <string name="could_not_redact">గది à°¸à±à°•à±à°°à±€à°¨à±</string> - <string name="unable_to_send_message">సందేశం పంపడం సాధà±à°¯à°‚ కాలేదà±</string> - - <string name="message_failed_to_upload">à°šà°¿à°¤à±à°°à°¾à°¨à±à°¨à°¿ à°…à°ªà±à°²à±‹à°¡à± చేయడంలో విఫలమైంది</string> - - <string name="network_error">సాధారణ లోపాలà±</string> - <string name="matrix_error">మాటà±à°°à°¿à°•à±à°¸à± లోపం</string> - - <string name="room_error_join_failed_empty_room">మళà±à°²à±€ ఖాళీ గది ని చేరడానికి à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ ఇది సాధà±à°¯à°‚ కాదà±.</string> - - <string name="encrypted_message">à°Žà°¨à±à°•à±à°°à°¿à°ªà±à°Ÿà±†à°¡à± సందేశం</string> - - <string name="medium_email">ఇమెయిలౠచిరà±à°¨à°¾à°®à°¾</string> - <string name="medium_phone_number">ఫోనౠనంబరà±</string> - - - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s à°’à°• à°šà°¿à°¤à±à°°à°‚ పంపారà±.</string> - - <string name="notice_room_invite_you">%1$s మిమà±à°®à°²à±à°¨à°¿ ఆహà±à°µà°¾à°¨à°¿à°‚చారà±</string> - <string name="notice_room_join">%1$s చేరారà±</string> - - <string name="room_displayname_invite_from">%s à°¨à±à°‚à°¡à°¿ ఆహà±à°µà°¾à°¨à°¿à°‚à°šà±</string> - <string name="room_displayname_two_members">%1$s మరియౠ%2$s</string> - <string name="room_displayname_room_invite">గదికి ఆహà±à°µà°¾à°¨à°‚</string> - <string name="room_displayname_empty_room">ఖాళీ గది</string> - - -</resources> diff --git a/matrix-sdk-android/src/main/res/values-th/strings.xml b/matrix-sdk-android/src/main/res/values-th/strings.xml deleted file mode 100644 index 3abd948f77ebb778569ad4e7c61ddcba9c645ab4..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-th/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-uk/strings.xml b/matrix-sdk-android/src/main/res/values-uk/strings.xml deleted file mode 100644 index 0f45a7182ca6d16111ad3e07db35fa1197d6857e..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-uk/strings.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s надіÑлав(ла) зображеннÑ.</string> - <string name="notice_room_invite_no_invitee">%s запрошеннÑ</string> - <string name="notice_room_invite">%1$s запроÑив(ла) %2$s</string> - <string name="encrypted_message">Зашифроване повідомленнÑ</string> - <!-- Room display name --> - <string name="room_displayname_invite_from">Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð²Ñ–Ð´ %s</string> - <string name="room_displayname_room_invite">Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ кімнати</string> - <string name="room_displayname_two_members">%1$s Ñ– %2$s</string> - <string name="room_displayname_empty_room">ÐŸÐ¾Ñ€Ð¾Ð¶Ð½Ñ ÐºÑ–Ð¼Ð½Ð°Ñ‚Ð°</string> - <string name="summary_user_sent_sticker">%1$s надіÑлав(ла) наліпку.</string> - <string name="notice_room_invite_you">%1$s запроÑив(ла) ВаÑ</string> - <string name="notice_room_join">%1$s приєднуєтьÑÑ</string> - <string name="notice_room_leave">%1$s покидає кімнату</string> - <string name="notice_room_reject">%1$s відхилив(ла) запрошеннÑ</string> - <string name="notice_room_kick">%1$s копнув(ла) %2$s</string> - <string name="notice_room_unban">%1$s розблокував(ла) %2$s</string> - <string name="notice_room_ban">%1$s заблокував(ла) %2$s</string> - <string name="notice_room_withdraw">%1$s відкликав(ла) Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ %2$s</string> - <string name="notice_avatar_url_changed">%1$s змінює Ñвій аватар</string> - <string name="notice_display_name_set">%1$s вÑтановлюють Ñобі назву %2$s</string> - <string name="notice_display_name_changed_from">%1$s змінює Ñвоє Ñ–Ð¼â€™Ñ Ð· %2$s на %3$s</string> - <string name="notice_display_name_removed">%1$s прибрав(ла) Ñвоє Ñ–Ð¼â€™Ñ (%2$s)</string> - <string name="notice_room_topic_changed">%1$s змінює тему на: %2$s</string> - <string name="notice_room_name_changed">%1$s змінив(ла) назву кімнати на: %2$s</string> - <string name="notice_placed_video_call">%s розпочав(ла) відеодзвінок.</string> - <string name="notice_placed_voice_call">%s розпочав(ла) голоÑовий дзвінок.</string> - <string name="notice_answered_call">%s відповів(ла) на дзвінок.</string> - <string name="notice_ended_call">%s завершує дзвінок.</string> - <string name="notice_made_future_room_visibility">%1$s зробив(ла) майбутню Ñ–Ñторію кімнати видимою Ð´Ð»Ñ %2$s</string> - <string name="notice_room_visibility_invited">уÑÑ–Ñ… Ñпіврозмовників, з моменту Ñ—Ñ… запрошеннÑ.</string> - <string name="notice_room_visibility_joined">уÑÑ–Ñ… Ñпіврозмовників, з моменту Ñ—Ñ… приєднаннÑ.</string> - <string name="notice_room_visibility_shared">уÑÑ–Ñ… Ñпіврозмовників.</string> - <string name="notice_room_visibility_world_readable">будь-кого.</string> - <string name="notice_room_visibility_unknown">невідомо (%s).</string> - <string name="notice_end_to_end">%1$s увімкнув(ла) наÑкрізне ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s запроÑив(ла) VoIP конференцію</string> - <string name="notice_voip_started">VoIP ÐºÐ¾Ð½Ñ„ÐµÑ€ÐµÐ½Ñ†Ñ–Ñ Ñ€Ð¾Ð·Ð¿Ð¾Ñ‡Ð°Ð»Ð°ÑÑŒ</string> - <string name="notice_voip_finished">VoIP ÐºÐ¾Ð½Ñ„ÐµÑ€ÐµÐ½Ñ†Ñ–Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ð»Ð°ÑÑŒ</string> - <string name="notice_avatar_changed_too">(аватар також змінено)</string> - <string name="notice_room_name_removed">%1$s прибрав(ла) назву кімнати</string> - <string name="notice_room_topic_removed">%1$s прибрав(ла) тему кімнати</string> - <string name="notice_profile_change_redacted">%1$s оновив(ла) Ñвій профіль %2$s</string> - <string name="notice_room_third_party_invite">%1$s надіÑлав(ла) Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ %2$s приєднатиÑÑ Ð´Ð¾ кімнати</string> - <string name="notice_room_third_party_registered_invite">%1$s прийнÑв(ла) Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ñƒ %2$s</string> - <string name="notice_crypto_unable_to_decrypt">** Ðеможливо розшифрувати: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">ПриÑтрій відправника не надіÑлав нам ключ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ повідомленнÑ.</string> - <string name="could_not_redact">Ðеможливо відредагувати</string> - <string name="unable_to_send_message">Ðе вдалоÑÑ Ð½Ð°Ð´Ñ–Ñлати повідомленнÑ</string> - <string name="message_failed_to_upload">Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ зображеннÑ</string> - <string name="network_error">Помилка мережі</string> - <string name="matrix_error">Помилка Matrix</string> - <string name="room_error_join_failed_empty_room">Ðаразі неможливо переприєднатиÑÑ Ð´Ð¾ порожньої кімнати.</string> - <string name="medium_email">ÐдреÑа електронної пошти</string> - <string name="medium_phone_number">Ðомер телефону</string> - <plurals name="room_displayname_three_and_more_members"> - <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="other"/> - </plurals> - <string name="notice_room_update">%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="initial_sync_start_importing_account_data">Початкове налаштуваннÑ: -\nÐ†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… облікового запиÑу</string> - <string name="notice_direct_room_leave_with_reason_by_you">Ви покинули. Причина: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s покидає. Причина: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">Ви покинули кімнату. Причина: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s покидає кімнату. Причина: %2$s</string> - <string name="notice_direct_room_leave">%1$s покидає кімнату</string> - <string name="notice_direct_room_leave_by_you">Ви покинули кімнату</string> - <string name="notice_room_leave_by_you">Ви покинули кімнату</string> - <string name="notice_room_canonical_alias_no_change_by_you">Ви змінили адреÑи цієї кімнати.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">Ви змінили оÑновну та альтернативну адреÑи цієї кімнати.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">Ви змінили альтернативні адреÑи Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— кімнати.</string> - <string name="notice_power_level_changed_by_you">Ви змінили рівень доÑтупу на %1$s.</string> - <string name="notice_room_server_acl_updated_title_by_you">Ви змінили Ñерверні ÑпиÑки контролю доÑтупу Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— кімнати.</string> - <string name="notice_room_name_changed_by_you">Ви змінили назву кімнати на: %1$s</string> - <string name="notice_room_avatar_changed_by_you">Ви змінили Ñвітлину кімнати</string> - <string name="notice_room_topic_changed_by_you">Ви змінили тему на: %1$s</string> - <string name="notice_display_name_changed_from_by_you">Ви змінили показуване ім\'Ñ Ð· %1$s на %2$s</string> - <string name="notice_avatar_url_changed_by_you">Ви змінили Ñвітлину профілю</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-vls/strings.xml b/matrix-sdk-android/src/main/res/values-vls/strings.xml deleted file mode 100644 index f0f2287a8d7f3cf44cfa6a55d435adf9d4f8f597..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-vls/strings.xml +++ /dev/null @@ -1,103 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s èt e fotootje gesteurd.</string> - <string name="summary_user_sent_sticker">%1$s èt e sticker gesteurd.</string> - - <string name="notice_room_invite_no_invitee">Uutnodigienge van %s</string> - <string name="notice_room_invite">%1$s èt %2$s uutgenodigd</string> - <string name="notice_room_invite_you">%1$s èt joun uitgenodigd</string> - <string name="notice_room_join">%1$s neemt nu deel an ’t gesprek</string> - <string name="notice_room_leave">%1$s èt ’t gesprek verloatn</string> - <string name="notice_room_reject">%1$s èt d’uitnodigienge geweigerd</string> - <string name="notice_room_kick">%1$s èt %2$s uut ’t gesprek verwyderd</string> - <string name="notice_room_unban">%1$s èt %2$s ountbann</string> - <string name="notice_room_ban">%1$s èt %2$s verbann</string> - <string name="notice_room_withdraw">%1$s èt d’uutnodigienge van %2$s ingetrokkn</string> - <string name="notice_avatar_url_changed">%1$s èt zyn/heur avatar angepast</string> - <string name="notice_display_name_set">%1$s èt zyn/heur noame angepast noa %2$s</string> - <string name="notice_display_name_changed_from">%1$s èt zyn/heur noame angepast van %2$s noa %3$s</string> - <string name="notice_display_name_removed">%1$s èt zyn/heur noame verwyderd (%2$s)</string> - <string name="notice_room_topic_changed">%1$s èt ’t ounderwerp veranderd noa: %2$s</string> - <string name="notice_room_name_changed">%1$s èt de gespreksnoame veranderd noa: %2$s</string> - <string name="notice_placed_video_call">%s èt e video-iproep gemakt.</string> - <string name="notice_placed_voice_call">%s èt e sproakiproep gemakt.</string> - <string name="notice_answered_call">%s èt den iproep beantwoord.</string> - <string name="notice_ended_call">%s èt ipgehangn.</string> - <string name="notice_made_future_room_visibility">%1$s èt de toekomstige gespreksgeschiedenisse zichtboar gemakt vo %2$s</string> - <string name="notice_room_visibility_invited">alle deelnemers an ’t gesprek, vanaf ’t punt dan ze zyn uutgenodigd.</string> - <string name="notice_room_visibility_joined">alle deelnemers an ’t gesprek, vanaf ’t punt dan ze zyn toegetreedn.</string> - <string name="notice_room_visibility_shared">alle deelnemers an ’t gesprek.</string> - <string name="notice_room_visibility_world_readable">iedereen.</string> - <string name="notice_room_visibility_unknown">ounbekend (%s).</string> - <string name="notice_end_to_end">%1$s èt eind-tout-eind-versleutelienge angezet (%2$s)</string> - - <string name="notice_requested_voip_conference">%1$s èt e VoIP-vergoaderienge angevroagd</string> - <string name="notice_voip_started">VoIP-vergoaderienge begunn</string> - <string name="notice_voip_finished">VoIP-vergoaderienge gestopt</string> - - <string name="notice_avatar_changed_too">(avatar es ook veranderd)</string> - <string name="notice_room_name_removed">%1$s èt de gespreksnoame verwyderd</string> - <string name="notice_room_topic_removed">%1$s èt ’t gespreksounderwerp verwyderd</string> - <string name="notice_event_redacted">Bericht verwyderd</string> - <string name="notice_event_redacted_by">Bericht verwyderd deur %1$s</string> - <string name="notice_event_redacted_with_reason">Bericht verwyderd [reden: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Bericht verwyderd deur %1$s [reden: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s èt zyn/heur profiel %2$s bygewerkt</string> - <string name="notice_room_third_party_invite">%1$s èt een uutnodigienge noa %2$s gesteurd vo ’t gesprek toe te treedn</string> - <string name="notice_room_third_party_registered_invite">%1$s èt d’uutnodigienge vo %2$s anveird</string> - - <string name="notice_crypto_unable_to_decrypt">** Kun nie ountsleuteln: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">’t Toestel van den afzender èt geen sleutels vo da bericht hier gesteurd.</string> - - <string name="could_not_redact">Kosteg nie verwyderd wordn</string> - <string name="unable_to_send_message">Kosteg ’t bericht nie verzendn</string> - - <string name="message_failed_to_upload">Iploadn van ’t fotootje es mislukt</string> - - <string name="network_error">Netwerkfout</string> - <string name="matrix_error">Matrix-fout</string> - - <string name="room_error_join_failed_empty_room">’t Es vo de moment nie meuglik van e leeg gesprek were toe te treedn.</string> - - <string name="encrypted_message">Versleuteld bericht</string> - - <string name="medium_email">E-mailadresse</string> - <string name="medium_phone_number">Telefongnumero</string> - - <string name="room_displayname_invite_from">Uutnodigienge van %s</string> - <string name="room_displayname_room_invite">Gespreksuutnodigienge</string> - - <string name="room_displayname_two_members">%1$s en %2$s</string> - - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s en 1 andere</item> - <item quantity="other">%1$s en %2$d anderen</item> - </plurals> - - <string name="room_displayname_empty_room">Leeg gesprek</string> - - <string name="initial_sync_start_importing_account">Initiële synchronisoasje: -\nAccount wor geïmporteerd…</string> - <string name="initial_sync_start_importing_account_crypto">Initiële synchronisoasje: -\nCrypto wor geïmporteerd</string> - <string name="initial_sync_start_importing_account_rooms">Initiële synchronisoasje: -\nGesprekkn wordn geïmporteerd</string> - <string name="initial_sync_start_importing_account_joined_rooms">Initiële synchronisoasje: -\nDeelgenoomn gesprekken wordn geïmporteerd</string> - <string name="initial_sync_start_importing_account_invited_rooms">Initiële synchronisoasje: -\nUutgenodigde gesprekkn wordn geïmporteerd</string> - <string name="initial_sync_start_importing_account_left_rooms">Initiële synchronisoasje: -\nVerloatn gesprekkn wordn geïmporteerd</string> - <string name="initial_sync_start_importing_account_groups">Initiële synchronisoasje: -\nGemeenschappn wordn geïmporteerd</string> - <string name="initial_sync_start_importing_account_data">Initiële synchronisoasje: -\nAccountgegeevns wordn geïmporteerd</string> - - <string name="notice_room_update">%s èt da gesprek hier ipgewoardeerd.</string> - - <string name="event_status_sending_message">Bericht wor verstuurd…</string> - <string name="clear_timeline_send_queue">Uutgoande wachtreeke leegn</string> - - <string name="notice_room_third_party_revoked_invite">%1$s èt d’uutnodigienge vo %2$s vo ’t gesprek toe te treedn ingetrokkn</string> -</resources> diff --git a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml deleted file mode 100644 index 5c5da36c2696a2528fe2fc5f92ac270d1e34f5b3..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,211 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_user_sent_image">%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_avatar_url_changed">%1$s æ›´æ¢äº†ä»–们的头åƒ</string> - <string name="notice_display_name_set">%1$s 将他们的昵称设置为 %2$s</string> - <string name="notice_display_name_changed_from">%1$s 把他们的昵称从 %2$s 改为 %3$s</string> - <string name="notice_display_name_removed">%1$s 移除了他们的昵称 (%2$s)</string> - <string name="notice_room_topic_changed">%1$s 把主题改为: %2$s</string> - <string name="notice_room_name_changed">%1$s 把èŠå¤©å®¤å称改为: %2$s</string> - <string name="notice_placed_video_call">%s å‘起了一次视频通è¯ã€‚</string> - <string name="notice_placed_voice_call">%s å‘起了一次è¯éŸ³é€šè¯ã€‚</string> - <string name="notice_answered_call">%s 已接å¬é€šè¯ã€‚</string> - <string name="notice_ended_call">%s 已结æŸé€šè¯ã€‚</string> - <string name="notice_room_visibility_invited">所有èŠå¤©å®¤æˆå‘˜ï¼Œä»Žä»–们被邀请开始。</string> - <string name="notice_room_visibility_joined">所有èŠå¤©å®¤æˆå‘˜ï¼Œä»Žä»–ä»¬åŠ å…¥å¼€å§‹ã€‚</string> - <string name="notice_room_visibility_shared">所有èŠå¤©å®¤æˆå‘˜ã€‚</string> - <string name="notice_room_visibility_world_readable">任何人。</string> - <string name="notice_room_visibility_unknown">未知(%s)。</string> - <string name="notice_end_to_end">%1$s å¼€å¯äº†ç«¯åˆ°ç«¯åŠ 密(%2$s)</string> - <string name="notice_requested_voip_conference">%1$s 请求了一次 VoIP 会议</string> - <string name="notice_voip_started">VoIP 会议已开始</string> - <string name="notice_voip_finished">VoIP 会议已结æŸ</string> - <string name="notice_avatar_changed_too">(头åƒä¹Ÿè¢«æ›´æ”¹ï¼‰</string> - <string name="notice_room_name_removed">%1$s 移除了èŠå¤©å®¤å称</string> - <string name="notice_room_topic_removed">%1$s 移除了èŠå¤©å®¤ä¸»é¢˜</string> - <string name="notice_crypto_unable_to_decrypt">** æ— æ³•è§£å¯†ï¼š%s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">å‘é€è€…的设备没有å‘我们å‘é€æ¤æ¶ˆæ¯çš„密钥。</string> - <string name="unable_to_send_message">æ— æ³•å‘é€æ¶ˆæ¯</string> - <string name="message_failed_to_upload">ä¸Šä¼ å›¾åƒå¤±è´¥</string> - <string name="network_error">网络错误</string> - <string name="matrix_error">Matrix 错误</string> - <string name="room_error_join_failed_empty_room">ç›®å‰æ— 法é‡æ–°åŠ 入一个空的èŠå¤©å®¤ã€‚</string> - <string name="encrypted_message">å·²åŠ å¯†æ¶ˆæ¯</string> - <string name="medium_email">电å邮箱地å€</string> - <string name="medium_phone_number">手机å·ç </string> - <string name="notice_room_withdraw">%1$s 撤回了对 %2$s 的邀请</string> - <string name="notice_made_future_room_visibility">%1$s 让未æ¥çš„èŠå¤©å®¤åŽ†å²è®°å½•å¯¹ %2$s å¯è§</string> - <string name="notice_profile_change_redacted">%1$s 更新了他的个人档案 %2$s</string> - <string name="notice_room_third_party_invite">%1$s å‘ %2$s å‘é€äº†åŠ å…¥èŠå¤©å®¤çš„邀请</string> - <string name="notice_room_third_party_registered_invite">%1$s 接å—了 %2$s 的邀请</string> - <string name="could_not_redact">æ— æ³•æ’¤å›ž</string> - <string name="summary_message">%1$s:%2$s</string> - <string name="summary_user_sent_sticker">%1$s å‘é€äº†ä¸€å¼ 贴纸。</string> - <string name="room_displayname_empty_room">空èŠå¤©å®¤</string> - <string name="room_displayname_invite_from">æ¥è‡ª %s 的邀请</string> - <string name="room_displayname_room_invite">èŠå¤©å®¤é‚€è¯·</string> - <string name="room_displayname_two_members">%1$s å’Œ %2$s</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$s 与其他 %2$d ä½</item> - </plurals> - <string name="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="initial_sync_start_importing_account">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥è´¦å·â€¦</string> - <string name="initial_sync_start_importing_account_crypto">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥åŠ 密数æ®</string> - <string name="initial_sync_start_importing_account_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_joined_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥å·²åŠ 入的èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_invited_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥å·²é‚€è¯·çš„èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_left_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥å·²ç¦»å¼€çš„èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_groups">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥ç¤¾åŒº</string> - <string name="initial_sync_start_importing_account_data">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨å¯¼å…¥è´¦å·æ•°æ®</string> - <string name="notice_room_update">%s å‡çº§äº†æ¤èŠå¤©å®¤ã€‚</string> - <string name="event_status_sending_message">æ£åœ¨å‘é€æ¶ˆæ¯â€¦</string> - <string name="clear_timeline_send_queue">清除æ£åœ¨å‘é€é˜Ÿåˆ—</string> - <string name="notice_room_third_party_revoked_invite">%1$s 撤回了对 %2$s åŠ å…¥èŠå¤©å®¤çš„邀请</string> - <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_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> - <string name="notice_room_ban_with_reason">%1$s å°ç¦äº† %2$s。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s å·²å‘é€é‚€è¯·ç»™ %2$s æ¥åŠ å…¥èŠå¤©å®¤ã€‚ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s 撤销了 %2$s åŠ å…¥èŠå¤©å®¤çš„邀請。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s æŽ¥å— %2$s 的邀請。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s 撤回了对 %2$s 的邀请。ç†ç”±ï¼š%3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="other">%1$s 新增了 %2$s 为æ¤èŠå¤©å®¤çš„地å€ã€‚</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="other">%1$s 移除了æ¤èŠå¤©å®¤çš„ %3$s 地å€ã€‚</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s 为æ¤èŠå¤©å®¤æ–°å¢žäº† %2$s 并移除 %3$s 地å€ã€‚</string> - <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> - <string name="notice_room_created">%1$s 创建了这个èŠå¤©å®¤</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">您请求了 VoIP 会议</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="other">您新增了 %1$s 为æ¤èŠå¤©å®¤çš„地å€ã€‚</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <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> - <string name="notice_direct_room_leave_with_reason_by_you">您已离开。ç†ç”±ï¼š%1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s 已离开。ç†ç”±ï¼š%2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">æ‚¨å·²åŠ å…¥ã€‚ç†ç”±ï¼š%1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s å·²åŠ å…¥ã€‚ç†ç”±ï¼š%2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">您撤回了对 %1$s 的邀请</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s 撤回了对 %2$s 的邀请</string> - <string name="notice_direct_room_third_party_invite_by_you">您邀请了 %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s 邀请了 %2$s</string> - <string name="notice_direct_room_update_by_you">您在æ¤å¤„å‡çº§ã€‚</string> - <string name="notice_direct_room_update">%s 在æ¤å¤„å‡çº§ã€‚</string> - <string name="notice_made_future_direct_room_visibility_by_you">您使未æ¥çš„消æ¯å¯¹ %1$s å¯è§</string> - <string name="notice_made_future_direct_room_visibility">%1$s 使未æ¥çš„消æ¯å¯¹ %2$s å¯è§</string> - <string name="notice_direct_room_leave_by_you">您离开了èŠå¤©å®¤</string> - <string name="notice_direct_room_leave">%1$s 离开了èŠå¤©å®¤</string> - <string name="notice_direct_room_join_by_you">æ‚¨å·²åŠ å…¥</string> - <string name="notice_direct_room_join">%1$s å·²åŠ å…¥</string> - <string name="notice_direct_room_created_by_you">您创建了讨论</string> - <string name="notice_direct_room_created">%1$s 创建了讨论</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">ä½ å·²é˜»æ¢å®¢äººåŠ 入房间。</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s已阻æ¢å®¢äººåŠ 入房间。</string> - <string name="notice_direct_room_guest_access_can_join_by_you">ä½ å·²å…è®¸å®¢äººåŠ å…¥è¿™é‡Œã€‚</string> - <string name="notice_direct_room_guest_access_can_join">%1$s å·²å…è®¸å®¢äººåŠ å…¥è¿™é‡Œã€‚</string> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml deleted file mode 100644 index 5038e8aab2a12395019c11c03c7130c709450108..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,251 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s:%2$s</string> - <string name="summary_user_sent_image">%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> - <string name="notice_answered_call">%s 回覆了通話。</string> - <string name="notice_ended_call">%s çµæŸé€šè©±ã€‚</string> - <string name="notice_made_future_room_visibility">%1$s 讓房間未來å¯è®“ %2$s 看到æ·å²ç´€éŒ„</string> - <string name="notice_room_visibility_invited">所有的房間æˆå“¡ï¼Œå¾žä»–們被邀請的時間開始。</string> - <string name="notice_room_visibility_joined">所有的房間æˆå“¡ï¼Œå¾žä»–å€‘åŠ å…¥çš„æ™‚é–“é–‹å§‹ã€‚</string> - <string name="notice_room_visibility_shared">所有的房間æˆå“¡ã€‚</string> - <string name="notice_room_visibility_world_readable">任何人。</string> - <string name="notice_room_visibility_unknown">未知 (%s)。</string> - <string name="notice_end_to_end">%1$s 開啟了端å°ç«¯åŠ 密 (%2$s)</string> - <string name="notice_requested_voip_conference">%1$s 請求了 VoIP 會è°é€šè©±</string> - <string name="notice_voip_started">VoIP 會è°é€šè©±å·²é–‹å§‹</string> - <string name="notice_voip_finished">VoIP 會è°é€šè©±å·²çµæŸ</string> - <string name="notice_avatar_changed_too">(大é 貼也變更了)</string> - <string name="notice_room_name_removed">%1$s 移除了房間å稱</string> - <string name="notice_room_topic_removed">%1$s 移除了房間主題</string> - <string name="notice_profile_change_redacted">%1$s 更新了他們的基本資料 %2$s</string> - <string name="notice_room_third_party_invite">%1$s 傳é€åŠ 入房間的邀請給 %2$s</string> - <string name="notice_room_third_party_registered_invite">%1$s æŽ¥å— %2$s 的邀請</string> - <string name="notice_crypto_unable_to_decrypt">** 無法解密:%s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">傳é€è€…çš„è£ç½®ä¸¦æœªåœ¨æ¤è¨Šæ¯å‚³é€ä»–們的金鑰。</string> - <string name="could_not_redact">無法編輯</string> - <string name="unable_to_send_message">無法傳é€è¨Šæ¯</string> - <string name="message_failed_to_upload">上傳圖片失敗</string> - <string name="network_error">網路錯誤</string> - <string name="matrix_error">Matrix 錯誤</string> - <string name="room_error_join_failed_empty_room">ç›®å‰ç„¡æ³•é‡æ–°åŠ 入空房間。</string> - <string name="encrypted_message">å·²åŠ å¯†çš„è¨Šæ¯</string> - <string name="medium_email">é›»å郵件</string> - <string name="medium_phone_number">電話號碼</string> - <string name="summary_user_sent_sticker">%1$s 傳é€äº†ä¸€å¼µè²¼åœ–。</string> - <string name="room_displayname_invite_from">來自%s 的邀請</string> - <string name="room_displayname_room_invite">èŠå¤©å®¤é‚€è«‹</string> - <string name="room_displayname_two_members">%1$s å’Œ %2$s</string> - <string name="room_displayname_empty_room">空èŠå¤©å®¤</string> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="other">%1$s å’Œ 和其他 %2$d 個人</item> - </plurals> - <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="initial_sync_start_importing_account">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥å¸³è™Ÿâ€¦â€¦</string> - <string name="initial_sync_start_importing_account_crypto">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥ crypto</string> - <string name="initial_sync_start_importing_account_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_joined_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥å·²åŠ 入的èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_invited_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥å·²é‚€è«‹çš„èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_left_rooms">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥å·²é›¢é–‹çš„èŠå¤©å®¤</string> - <string name="initial_sync_start_importing_account_groups">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥ç¤¾ç¾¤</string> - <string name="initial_sync_start_importing_account_data">åˆå§‹åŒ–åŒæ¥ï¼š -\næ£åœ¨åŒ¯å…¥å¸³è™Ÿè³‡æ–™</string> - <string name="notice_room_update">%s å·²å‡ç´šæ¤èŠå¤©å®¤ã€‚</string> - <string name="event_status_sending_message">æ£åœ¨å‚³é€è¨Šæ¯â€¦â€¦</string> - <string name="clear_timeline_send_queue">清除傳é€ä½‡åˆ—</string> - <string name="notice_room_third_party_revoked_invite">%1$s 撤銷了 %2$s åŠ å…¥èŠå¤©å®¤çš„邀請</string> - <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_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> - <string name="notice_room_ban_with_reason">%1$s å°éŽ–了 %2$s。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s 已傳é€é‚€è«‹çµ¦ %2$s ä¾†åŠ å…¥èŠå¤©å®¤ã€‚ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s 撤銷了 %2$s åŠ å…¥èŠå¤©å®¤çš„邀請。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s æŽ¥å— %2$s 的邀請。ç†ç”±ï¼š%3$s</string> - <string name="notice_room_withdraw_with_reason">%1$s æ’¤å›žäº†å° %2$s 的邀請。ç†ç”±ï¼š%3$s</string> - <plurals name="notice_room_aliases_added"> - <item quantity="other">%1$s 新增了 %2$s 為æ¤èŠå¤©å®¤çš„地å€ã€‚</item> - </plurals> - <plurals name="notice_room_aliases_removed"> - <item quantity="other">%1$s 移除了æ¤èŠå¤©å®¤çš„ %2$s 地å€ã€‚</item> - </plurals> - <string name="notice_room_aliases_added_and_removed">%1$s 為æ¤èŠå¤©å®¤æ–°å¢ž %2$s 並移除 %3$s 地å€ã€‚</string> - <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> - <string name="notice_room_created">%1$s 建立了èŠå¤©å®¤</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">您請求了 VoIP 會è°</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="other">您為æ¤èŠå¤©å®¤æ–°å¢žäº† %1$s 作為地å€ã€‚</item> - </plurals> - <plurals name="notice_room_aliases_removed_by_you"> - <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> - <string name="notice_direct_room_guest_access_forbidden_by_you">您已é¿å…è¨ªå®¢åŠ å…¥æ¤èŠå¤©å®¤ã€‚</string> - <string name="notice_direct_room_guest_access_forbidden">%1$s å·²é¿å…è¨ªå®¢åŠ å…¥æ¤èŠå¤©å®¤ã€‚</string> - <string name="notice_direct_room_guest_access_can_join_by_you">您已å…è¨±è¨ªå®¢åŠ å…¥é€™è£¡ã€‚</string> - <string name="notice_direct_room_guest_access_can_join">%1$s å·²å…è¨±è¨ªå®¢åŠ å…¥é€™è£¡ã€‚</string> - <string name="notice_direct_room_leave_with_reason_by_you">您已離開。ç†ç”±ï¼š%1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s 已離開。ç†ç”±ï¼š%2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">æ‚¨å·²åŠ å…¥ã€‚ç†ç”±ï¼š%1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s å·²åŠ å…¥ã€‚ç†ç”±ï¼š%2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">æ‚¨å·²æ’¤éŠ·å° %1$s 的邀請</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s å·²æ’¤éŠ·å° %2$s 的邀請</string> - <string name="notice_direct_room_third_party_invite_by_you">您已邀請了 %1$s</string> - <string name="notice_direct_room_third_party_invite">%1$s 邀請了 %2$s</string> - <string name="notice_direct_room_update_by_you">您已在æ¤å‡ç´šã€‚</string> - <string name="notice_direct_room_update">%s 已在æ¤å‡ç´šã€‚</string> - <string name="notice_made_future_direct_room_visibility_by_you">您讓未來的訊æ¯å° %1$s å¯è¦‹</string> - <string name="notice_made_future_direct_room_visibility">%1$s 已讓未來的訊æ¯å° %2$s å¯è¦‹</string> - <string name="notice_direct_room_leave_by_you">您已離開èŠå¤©å®¤</string> - <string name="notice_direct_room_leave">%1$s 已離開èŠå¤©å®¤</string> - <string name="notice_direct_room_join_by_you">æ‚¨å·²åŠ å…¥</string> - <string name="notice_direct_room_join">%1$s å·²åŠ å…¥</string> - <string name="notice_direct_room_created_by_you">您已建立æ¤è¨Žè«–</string> - <string name="notice_direct_room_created">%1$s 已建立æ¤è¨Žè«–</string> - <string name="room_displayname_empty_room_was">空的èŠå¤©å®¤ï¼ˆæ›¾ç‚º %s)</string> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="other">%1$s, %2$s, %3$s 與 %4$d 個其他</item> - </plurals> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s 與 %4$s</string> - <string name="room_displayname_3_members">%1$s, %2$s 與 %3$s</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 ç¦æ¢æ‰€æœ‰ä¼ºæœå™¨åƒèˆ‡ï¼ç„¡æ³•å†ä½¿ç”¨æ¤èŠå¤©å®¤ã€‚</string> - <string name="notice_room_server_acl_updated_no_change">無變更。</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• ç¦æ¢ä¼ºæœå™¨ç¬¦åˆ IP æ–‡å—。</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• å…許伺æœå™¨ç¬¦åˆ IP æ–‡å—。</string> - <string name="notice_room_server_acl_updated_was_allowed">• 伺æœå™¨ç¬¦åˆ %s 已從å…許清單ä¸ç§»é™¤ã€‚</string> - <string name="notice_room_server_acl_updated_allowed">• å…許伺æœå™¨ç¬¦åˆ %s。</string> - <string name="notice_room_server_acl_updated_was_banned">• 伺æœå™¨ç¬¦åˆ %s 已從ç¦æ¢æ¸…å–®ä¸ç§»é™¤ã€‚</string> - <string name="notice_room_server_acl_updated_banned">• ç¾åœ¨ç¦æ¢ä¼ºæœå™¨ç¬¦åˆ %s。</string> - <string name="notice_room_server_acl_updated_title_by_you">您為æ¤èŠå¤©å®¤è®Šæ›´äº†ä¼ºæœå™¨ ACL。</string> - <string name="notice_room_server_acl_updated_title">%s 為æ¤èŠå¤©æ˜¯è®Šæ›´äº†ä¼ºæœå™¨ ACL。</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• ç¦æ¢ä¼ºæœå™¨ç¬¦åˆ IP æ–‡å—。</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• å…許伺æœå™¨ç¬¦åˆ IP æ–‡å—。</string> - <string name="notice_room_server_acl_set_allowed">• å·²å…許伺æœå™¨ç¬¦åˆ %s。</string> - <string name="notice_room_server_acl_set_banned">• å·²ç¦æ¢ä¼ºæœå™¨ç¬¦åˆ %s。</string> - <string name="notice_room_server_acl_set_title_by_you">您為æ¤èŠå¤©æ˜¯è¨å®šäº†ä¼ºæœå™¨ ACL。</string> - <string name="notice_room_server_acl_set_title">%s 為æ¤èŠå¤©æ˜¯è¨å®šäº†ä¼ºæœå™¨ ACL。</string> - <string name="notice_room_canonical_alias_no_change_by_you">您變更了æ¤èŠå¤©å®¤çš„地å€ã€‚</string> - <string name="notice_room_canonical_alias_no_change">%1$s 變更了æ¤èŠå¤©å®¤çš„地å€ã€‚</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">您為æ¤èŠå¤©å®¤è®Šæ›´äº†ä¸»è¦åŠå‚™ç”¨åœ°å€ã€‚</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s 為æ¤èŠå¤©å®¤è®Šæ›´äº†ä¸»è¦åŠå‚™ç”¨åœ°å€ã€‚</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">您為æ¤èŠå¤©å®¤è®Šæ›´äº†å‚™ç”¨åœ°å€ã€‚</string> - <string name="notice_room_canonical_alias_alternative_changed">%1$s 變更了æ¤èŠå¤©å®¤çš„備用地å€ã€‚</string> - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="other">您為æ¤èŠå¤©å®¤ç§»é™¤äº†å‚™ç”¨åœ°å€ %1$s。</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="other">%1$s 已為æ¤èŠå¤©å®¤ç§»é™¤å‚™ç”¨åœ°å€ %2$s。</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="other">您為æ¤èŠå¤©å®¤æ–°å¢žäº†å‚™ç”¨åœ°å€ %1$s。</item> - </plurals> - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="other">%1$s 已為æ¤èŠå¤©å®¤æ–°å¢žäº†å‚™ç”¨åœ°å€ %2$s。</item> - </plurals> -</resources> \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml deleted file mode 100644 index 7a0fe1d7352bf35adddf3a2977fb382e5c5538da..0000000000000000000000000000000000000000 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ /dev/null @@ -1,308 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="summary_message">%1$s: %2$s</string> - <string name="summary_user_sent_image">%1$s sent an image.</string> - <string name="summary_you_sent_image">You sent an image.</string> - <string name="summary_user_sent_sticker">%1$s sent a sticker.</string> - <string name="summary_you_sent_sticker">You sent a sticker.</string> - - <string name="notice_room_invite_no_invitee">%s\'s invitation</string> - <string name="notice_room_invite_no_invitee_by_you">Your invitation</string> - <string name="notice_room_created">%1$s created the room</string> - <string name="notice_room_created_by_you">You created the room</string> - <string name="notice_direct_room_created">%1$s created the discussion</string> - <string name="notice_direct_room_created_by_you">You created the discussion</string> - <string name="notice_room_invite">%1$s invited %2$s</string> - <string name="notice_room_invite_by_you">You invited %1$s</string> - <string name="notice_room_invite_you">%1$s invited you</string> - <string name="notice_room_join">%1$s joined the room</string> - <string name="notice_room_join_by_you">You joined the room</string> - <string name="notice_direct_room_join">%1$s joined</string> - <string name="notice_direct_room_join_by_you">You joined</string> - <string name="notice_room_leave">%1$s left the room</string> - <string name="notice_room_leave_by_you">You left the room</string> - <string name="notice_direct_room_leave">%1$s left the room</string> - <string name="notice_direct_room_leave_by_you">You left the room</string> - <string name="notice_room_reject">%1$s rejected the invitation</string> - <string name="notice_room_reject_by_you">You rejected the invitation</string> - <string name="notice_room_kick">%1$s kicked %2$s</string> - <string name="notice_room_kick_by_you">You kicked %1$s</string> - <string name="notice_room_unban">%1$s unbanned %2$s</string> - <string name="notice_room_unban_by_you">You unbanned %1$s</string> - <string name="notice_room_ban">%1$s banned %2$s</string> - <string name="notice_room_ban_by_you">You banned %1$s</string> - <string name="notice_room_withdraw">%1$s withdrew %2$s\'s invitation</string> - <string name="notice_room_withdraw_by_you">You withdrew %1$s\'s invitation</string> - <string name="notice_avatar_url_changed">%1$s changed their avatar</string> - <string name="notice_avatar_url_changed_by_you">You changed your avatar</string> - <string name="notice_display_name_set">%1$s set their display name to %2$s</string> - <string name="notice_display_name_set_by_you">You set your display name to %1$s</string> - <string name="notice_display_name_changed_from">%1$s changed their display name from %2$s to %3$s</string> - <string name="notice_display_name_changed_from_by_you">You changed your display name from %1$s to %2$s</string> - <string name="notice_display_name_removed">%1$s removed their display name (it was %2$s)</string> - <string name="notice_display_name_removed_by_you">You removed your display name (it was %1$s)</string> - <string name="notice_room_topic_changed">%1$s changed the topic to: %2$s</string> - <string name="notice_room_topic_changed_by_you">You changed the topic to: %1$s</string> - <string name="notice_room_avatar_changed">%1$s changed the room avatar</string> - <string name="notice_room_avatar_changed_by_you">You changed the room avatar</string> - <string name="notice_room_name_changed">%1$s changed the room name to: %2$s</string> - <string name="notice_room_name_changed_by_you">You changed the room name to: %1$s</string> - <string name="notice_placed_video_call">%s placed a video call.</string> - <string name="notice_placed_video_call_by_you">You placed a video call.</string> - <string name="notice_placed_voice_call">%s placed a voice call.</string> - <string name="notice_placed_voice_call_by_you">You placed a voice call.</string> - <string name="notice_call_candidates">%s sent data to setup the call.</string> - <string name="notice_call_candidates_by_you">You sent data to setup the call.</string> - <string name="notice_answered_call">%s answered the call.</string> - <string name="notice_answered_call_by_you">You answered the call.</string> - <string name="notice_ended_call">%s ended the call.</string> - <string name="notice_ended_call_by_you">You ended the call.</string> - <string name="notice_made_future_room_visibility">%1$s made future room history visible to %2$s</string> - <string name="notice_made_future_room_visibility_by_you">You made future room history visible to %1$s</string> - <string name="notice_made_future_direct_room_visibility">%1$s made future messages visible to %2$s</string> - <string name="notice_made_future_direct_room_visibility_by_you">You made future messages visible to %1$s</string> - <string name="notice_room_visibility_invited">all room members, from the point they are invited.</string> - <string name="notice_room_visibility_joined">all room members, from the point they joined.</string> - <string name="notice_room_visibility_shared">all room members.</string> - <string name="notice_room_visibility_world_readable">anyone.</string> - <string name="notice_room_visibility_unknown">unknown (%s).</string> - <string name="notice_end_to_end">%1$s turned on end-to-end encryption (%2$s)</string> - <string name="notice_end_to_end_by_you">You turned on end-to-end encryption (%1$s)</string> - <string name="notice_room_update">%s upgraded this room.</string> - <string name="notice_room_update_by_you">You upgraded this room.</string> - <string name="notice_direct_room_update">%s upgraded here.</string> - <string name="notice_direct_room_update_by_you">You upgraded here.</string> - <string name="notice_room_server_acl_set_title">%s set the server ACLs for this room.</string> - <string name="notice_room_server_acl_set_title_by_you">You set the server ACLs for this room.</string> - <string name="notice_room_server_acl_set_banned">• Server matching %s are banned.</string> - <string name="notice_room_server_acl_set_allowed">• Server matching %s are allowed.</string> - <string name="notice_room_server_acl_set_ip_literals_allowed">• Server matching IP literals are allowed.</string> - <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Server matching IP literals are banned.</string> - - <string name="notice_room_server_acl_updated_title">%s changed the server ACLs for this room.</string> - <string name="notice_room_server_acl_updated_title_by_you">You changed the server ACLs for this room.</string> - <string name="notice_room_server_acl_updated_banned">• Server matching %s are now banned.</string> - <string name="notice_room_server_acl_updated_was_banned">• Server matching %s were removed from the ban list.</string> - <string name="notice_room_server_acl_updated_allowed">• Server matching %s are now allowed.</string> - <string name="notice_room_server_acl_updated_was_allowed">• Server matching %s were removed from the allowed list.</string> - <string name="notice_room_server_acl_updated_ip_literals_allowed">• Server matching IP literals are now allowed.</string> - <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Server matching IP literals are now banned.</string> - <string name="notice_room_server_acl_updated_no_change">No change.</string> - <string name="notice_room_server_acl_allow_is_empty">🎉 All servers are banned from participating! This room can no longer be used.</string> - - <string name="notice_requested_voip_conference">%1$s requested a VoIP conference</string> - <string name="notice_requested_voip_conference_by_you">You requested a VoIP conference</string> - <string name="notice_voip_started">VoIP conference started</string> - <string name="notice_voip_finished">VoIP conference finished</string> - - <string name="notice_avatar_changed_too">(avatar was changed too)</string> - <string name="notice_room_name_removed">%1$s removed the room name</string> - <string name="notice_room_name_removed_by_you">You removed the room name</string> - <string name="notice_room_topic_removed">%1$s removed the room topic</string> - <string name="notice_room_topic_removed_by_you">You removed the room topic</string> - <string name="notice_room_avatar_removed">%1$s removed the room avatar</string> - <string name="notice_room_avatar_removed_by_you">You removed the room avatar</string> - <string name="notice_event_redacted">Message removed</string> - <string name="notice_event_redacted_by">Message removed by %1$s</string> - <string name="notice_event_redacted_with_reason">Message removed [reason: %1$s]</string> - <string name="notice_event_redacted_by_with_reason">Message removed by %1$s [reason: %2$s]</string> - <string name="notice_profile_change_redacted">%1$s updated their profile %2$s</string> - <string name="notice_profile_change_redacted_by_you">You updated your profile %1$s</string> - <string name="notice_room_third_party_invite">%1$s sent an invitation to %2$s to join the room</string> - <string name="notice_room_third_party_invite_by_you">You sent an invitation to %1$s to join the room</string> - <string name="notice_direct_room_third_party_invite">%1$s invited %2$s</string> - <string name="notice_direct_room_third_party_invite_by_you">You invited %1$s</string> - <string name="notice_room_third_party_revoked_invite">%1$s revoked the invitation for %2$s to join the room</string> - <string name="notice_room_third_party_revoked_invite_by_you">You revoked the invitation for %1$s to join the room</string> - <string name="notice_direct_room_third_party_revoked_invite">%1$s revoked the invitation for %2$s</string> - <string name="notice_direct_room_third_party_revoked_invite_by_you">You revoked the invitation for %1$s</string> - <string name="notice_room_third_party_registered_invite">%1$s accepted the invitation for %2$s</string> - <string name="notice_room_third_party_registered_invite_by_you">You accepted the invitation for %1$s</string> - - <string name="notice_widget_added">%1$s added %2$s widget</string> - <string name="notice_widget_added_by_you">You added %1$s widget</string> - <string name="notice_widget_removed">%1$s removed %2$s widget</string> - <string name="notice_widget_removed_by_you">You removed %1$s widget</string> - <string name="notice_widget_modified">%1$s modified %2$s widget</string> - <string name="notice_widget_modified_by_you">You modified %1$s widget</string> - - <string name="power_level_admin">Admin</string> - <string name="power_level_moderator">Moderator</string> - <string name="power_level_default">Default</string> - <string name="power_level_custom">Custom (%1$d)</string> - <string name="power_level_custom_no_value">Custom</string> - - <!-- parameter will be a comma separated list of values of notice_power_level_diff --> - <string name="notice_power_level_changed_by_you">You changed the power level of %1$s.</string> - <!-- First parameter will be a userId or display name, second one will be a comma separated list of values of notice_power_level_diff --> - <string name="notice_power_level_changed">%1$s changed the power level of %2$s.</string> - <!-- First parameter will be a userId or display name, the two last ones will be value of power_level_* --> - <string name="notice_power_level_diff">%1$s from %2$s to %3$s</string> - - <string name="notice_crypto_unable_to_decrypt">** Unable to decrypt: %s **</string> - <string name="notice_crypto_error_unkwown_inbound_session_id">The sender\'s device has not sent us the keys for this message.</string> - - <!-- Messages --> - - <!-- Room Screen --> - <string name="could_not_redact">Could not redact</string> - <string name="unable_to_send_message">Unable to send message</string> - - <string name="message_failed_to_upload">Failed to upload image</string> - - <!-- general errors --> - <string name="network_error">Network error</string> - <string name="matrix_error">Matrix error</string> - - <!-- Home Screen --> - - <!-- Last seen time --> - - <!-- call events --> - - <!-- room error messages --> - <string name="room_error_join_failed_empty_room">It is not currently possible to re-join an empty room.</string> - - <string name="encrypted_message">Encrypted message</string> - - <!-- medium friendly name --> - <string name="medium_email">Email address</string> - <string name="medium_phone_number">Phone number</string> - - <!-- Room display name --> - <string name="room_displayname_invite_from">Invite from %s</string> - <string name="room_displayname_room_invite">Room Invite</string> - - <!-- The 2 parameters will be members' name --> - <string name="room_displayname_two_members">%1$s and %2$s</string> - <!-- The 3 parameters will be members' name --> - <string name="room_displayname_3_members">%1$s, %2$s and %3$s</string> - <!-- The 4 parameters will be members' name --> - <string name="room_displayname_4_members">%1$s, %2$s, %3$s and %4$s</string> - <!-- The 3 first parameters will be members' name --> - <plurals name="room_displayname_four_and_more_members"> - <item quantity="one">%1$s, %2$s, %3$s and %4$d other</item> - <item quantity="other">%1$s, %2$s, %3$s and %4$d others</item> - </plurals> - <plurals name="room_displayname_three_and_more_members"> - <item quantity="one">%1$s and 1 other</item> - <item quantity="other">%1$s and %2$d others</item> - </plurals> - - <string name="room_displayname_empty_room">Empty room</string> - <string name="room_displayname_empty_room_was">Empty room (was %s)</string> - - <string name="initial_sync_start_importing_account">Initial Sync:\nImporting account…</string> - <string name="initial_sync_start_importing_account_crypto">Initial Sync:\nImporting crypto</string> - <string name="initial_sync_start_importing_account_rooms">Initial Sync:\nImporting Rooms</string> - <string name="initial_sync_start_importing_account_joined_rooms">Initial Sync:\nImporting Joined Rooms</string> - <string name="initial_sync_start_importing_account_invited_rooms">Initial Sync:\nImporting Invited Rooms</string> - <string name="initial_sync_start_importing_account_left_rooms">Initial Sync:\nImporting Left Rooms</string> - <string name="initial_sync_start_importing_account_groups">Initial Sync:\nImporting Communities</string> - <string name="initial_sync_start_importing_account_data">Initial Sync:\nImporting Account Data</string> - - <string name="event_status_sending_message">Sending message…</string> - <string name="clear_timeline_send_queue">Clear sending queue</string> - - <string name="notice_room_invite_no_invitee_with_reason">%1$s\'s invitation. Reason: %2$s</string> - <string name="notice_room_invite_no_invitee_with_reason_by_you">Your invitation. Reason: %1$s</string> - <string name="notice_room_invite_with_reason">%1$s invited %2$s. Reason: %3$s</string> - <string name="notice_room_invite_with_reason_by_you">You invited %1$s. Reason: %2$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 the room. Reason: %2$s</string> - <string name="notice_room_join_with_reason_by_you">You joined the room. Reason: %1$s</string> - <string name="notice_direct_room_join_with_reason">%1$s joined. Reason: %2$s</string> - <string name="notice_direct_room_join_with_reason_by_you">You joined. Reason: %1$s</string> - <string name="notice_room_leave_with_reason">%1$s left the room. Reason: %2$s</string> - <string name="notice_room_leave_with_reason_by_you">You left the room. Reason: %1$s</string> - <string name="notice_direct_room_leave_with_reason">%1$s left. Reason: %2$s</string> - <string name="notice_direct_room_leave_with_reason_by_you">You left. Reason: %1$s</string> - <string name="notice_room_reject_with_reason">%1$s rejected the invitation. Reason: %2$s</string> - <string name="notice_room_reject_with_reason_by_you">You rejected the invitation. Reason: %1$s</string> - <string name="notice_room_kick_with_reason">%1$s kicked %2$s. Reason: %3$s</string> - <string name="notice_room_kick_with_reason_by_you">You kicked %1$s. Reason: %2$s</string> - <string name="notice_room_unban_with_reason">%1$s unbanned %2$s. Reason: %3$s</string> - <string name="notice_room_unban_with_reason_by_you">You unbanned %1$s. Reason: %2$s</string> - <string name="notice_room_ban_with_reason">%1$s banned %2$s. Reason: %3$s</string> - <string name="notice_room_ban_with_reason_by_you">You banned %1$s. Reason: %2$s</string> - <string name="notice_room_third_party_invite_with_reason">%1$s sent an invitation to %2$s to join the room. Reason: %3$s</string> - <string name="notice_room_third_party_invite_with_reason_by_you">You sent an invitation to %1$s to join the room. Reason: %2$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason">%1$s revoked the invitation for %2$s to join the room. Reason: %3$s</string> - <string name="notice_room_third_party_revoked_invite_with_reason_by_you">You revoked the invitation for %1$s to join the room. Reason: %2$s</string> - <string name="notice_room_third_party_registered_invite_with_reason">%1$s accepted the invitation for %2$s. Reason: %3$s</string> - <string name="notice_room_third_party_registered_invite_with_reason_by_you">You accepted the invitation for %1$s. Reason: %2$s</string> - <string name="notice_room_withdraw_with_reason">%1$s withdrew %2$s\'s invitation. Reason: %3$s</string> - <string name="notice_room_withdraw_with_reason_by_you">You withdrew %1$s\'s invitation. Reason: %2$s</string> - - <plurals name="notice_room_aliases_added"> - <item quantity="one">%1$s added %2$s as an address for this room.</item> - <item quantity="other">%1$s added %2$s as addresses for this room.</item> - </plurals> - - <plurals name="notice_room_aliases_added_by_you"> - <item quantity="one">You added %1$s as an address for this room.</item> - <item quantity="other">You added %1$s as addresses for this room.</item> - </plurals> - - <plurals name="notice_room_aliases_removed"> - <item quantity="one">%1$s removed %2$s as an address for this room.</item> - <item quantity="other">%1$s removed %2$s as addresses for this room.</item> - </plurals> - - <plurals name="notice_room_aliases_removed_by_you"> - <item quantity="one">You removed %1$s as an address for this room.</item> - <item quantity="other">You removed %1$s as addresses for this room.</item> - </plurals> - - <string name="notice_room_aliases_added_and_removed">%1$s added %2$s and removed %3$s as addresses for this room.</string> - <string name="notice_room_aliases_added_and_removed_by_you">You added %1$s and removed %2$s as addresses for this room.</string> - - <string name="notice_room_canonical_alias_set">"%1$s set the main address for this room to %2$s."</string> - <string name="notice_room_canonical_alias_set_by_you">"You set the main address for this room to %1$s."</string> - <string name="notice_room_canonical_alias_unset">"%1$s removed the main address for this room."</string> - <string name="notice_room_canonical_alias_unset_by_you">"You removed the main address for this room."</string> - - <plurals name="notice_room_canonical_alias_alternative_added"> - <item quantity="one">%1$s added the alternative address %2$s for this room.</item> - <item quantity="other">%1$s added the alternative addresses %2$s for this room.</item> - </plurals> - - <plurals name="notice_room_canonical_alias_alternative_added_by_you"> - <item quantity="one">You added the alternative address %1$s for this room.</item> - <item quantity="other">You added the alternative addresses %1$s for this room.</item> - </plurals> - - <plurals name="notice_room_canonical_alias_alternative_removed"> - <item quantity="one">%1$s removed the alternative address %2$s for this room.</item> - <item quantity="other">%1$s removed the alternative addresses %2$s for this room.</item> - </plurals> - - <plurals name="notice_room_canonical_alias_alternative_removed_by_you"> - <item quantity="one">You removed the alternative address %1$s for this room.</item> - <item quantity="other">You removed the alternative addresses %1$s for this room.</item> - </plurals> - - <string name="notice_room_canonical_alias_alternative_changed">%1$s changed the alternative addresses for this room.</string> - <string name="notice_room_canonical_alias_alternative_changed_by_you">You changed the alternative addresses for this room.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed">%1$s changed the main and alternative addresses for this room.</string> - <string name="notice_room_canonical_alias_main_and_alternative_changed_by_you">You changed the main and alternative addresses for this room.</string> - <string name="notice_room_canonical_alias_no_change">%1$s changed the addresses for this room.</string> - <string name="notice_room_canonical_alias_no_change_by_you">You changed the addresses for this room.</string> - - <string name="notice_room_guest_access_can_join">"%1$s has allowed guests to join the room."</string> - <string name="notice_room_guest_access_can_join_by_you">"You have allowed guests to join the room."</string> - <string name="notice_direct_room_guest_access_can_join">"%1$s has allowed guests to join here."</string> - <string name="notice_direct_room_guest_access_can_join_by_you">"You have allowed guests to join here."</string> - <string name="notice_room_guest_access_forbidden">"%1$s has prevented guests from joining the room."</string> - <string name="notice_room_guest_access_forbidden_by_you">"You have prevented guests from joining the room."</string> - <string name="notice_direct_room_guest_access_forbidden">"%1$s has prevented guests from joining the room."</string> - <string name="notice_direct_room_guest_access_forbidden_by_you">"You have prevented guests from joining the room."</string> - - <string name="notice_end_to_end_ok">%1$s turned on end-to-end encryption.</string> - <string name="notice_end_to_end_ok_by_you">You turned on end-to-end encryption.</string> - <string name="notice_end_to_end_unknown_algorithm">%1$s turned on end-to-end encryption (unrecognised algorithm %2$s).</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">You turned on end-to-end encryption (unrecognised algorithm %1$s).</string> - - <string name="key_verification_request_fallback_message">%s is requesting to verify your key, but your client does not support in-chat key verification. You will need to use legacy key verification to verify keys.</string> - -</resources>