From 9dc21cd6329f2c690cfbdd9ebd139d6ff4145a16 Mon Sep 17 00:00:00 2001 From: Benoit Marty <benoit@matrix.org> Date: Mon, 30 Nov 2020 15:51:56 +0100 Subject: [PATCH] Import SDK 1.0.11 from Element Android --- matrix-sdk-android/build.gradle | 6 +- .../android/sdk/account/ChangePasswordTest.kt | 4 +- .../sdk/account/DeactivateAccountTest.kt | 4 +- .../android/sdk/common/CommonTestHelper.kt | 9 + .../android/sdk/common/CryptoTestHelper.kt | 4 +- .../internal/crypto/verification/SASTest.kt | 6 +- .../sdk/session/search/SearchMessagesTest.kt | 53 ++-- .../interceptors/FormattedJsonHttpLogger.kt | 8 +- .../android/sdk/api/auth/login/LoginWizard.kt | 2 +- .../android/sdk/api/extensions/Strings.kt | 5 + .../sdk/api/pushrules/PushRuleService.kt | 10 +- .../matrix/android/sdk/api/raw/RawService.kt | 11 +- .../sdk/api/session/account/AccountService.kt | 7 +- .../verification/VerificationService.kt | 2 +- .../sdk/api/session/events/model/EventType.kt | 1 + .../android/sdk/api/session/group/Group.kt | 6 +- .../api/session/identity/IdentityService.kt | 20 ++ .../session/identity/IdentityServiceError.kt | 1 + .../api/session/permalinks/MatrixLinkify.kt | 16 +- .../api/session/permalinks/PermalinkParser.kt | 5 +- .../sdk/api/session/room/RoomService.kt | 17 ++ .../session/room/crypto/RoomCryptoService.kt | 4 +- .../session/room/failure/CreateRoomFailure.kt | 5 + .../room/model/RoomServerAclContent.kt | 59 +++++ .../room/model/create/CreateRoomParams.kt | 21 +- .../room/notification/RoomPushRuleService.kt | 4 +- .../room/reporting/ReportingService.kt | 5 +- .../sdk/api/session/room/send/DraftService.kt | 6 +- .../sdk/api/session/room/tags/TagsService.kt | 7 +- .../sdk/api/session/search/SearchService.kt | 21 +- .../sdk/api/session/user/UserService.kt | 5 + .../sdk/internal/crypto/CryptoModule.kt | 1 + .../internal/crypto/DefaultCryptoService.kt | 12 +- .../sdk/internal/crypto/EventDecryptor.kt | 18 +- .../crypto/IncomingGossipingRequestManager.kt | 7 +- .../sdk/internal/crypto/MXOlmDevice.kt | 2 +- .../sdk/internal/crypto/SendGossipWorker.kt | 2 +- .../EnsureOlmSessionsForDevicesAction.kt | 4 +- .../algorithms/megolm/MXMegolmDecryption.kt | 22 +- .../algorithms/megolm/MXMegolmEncryption.kt | 33 ++- .../DefaultCrossSigningService.kt | 3 +- .../crypto/crosssigning/UpdateTrustWorker.kt | 4 +- .../crypto/store/db/RealmCryptoStore.kt | 21 +- .../DefaultVerificationService.kt | 15 +- .../SASDefaultVerificationTransaction.kt | 5 +- .../sdk/internal/database/RealmKeysUtils.kt | 3 +- .../SessionRealmConfigurationFactory.kt | 1 + .../internal/network/TimeOutInterceptor.kt | 3 + .../sdk/internal/raw/DefaultRawService.kt | 29 +-- .../session/account/DefaultAccountService.kt | 23 +- .../internal/session/group/DefaultGroup.kt | 11 +- .../internal/session/group/GroupFactory.kt | 5 +- .../identity/DefaultIdentityService.kt | 17 ++ .../session/identity/IdentityModule.kt | 5 + .../session/identity/data/IdentityData.kt | 3 +- .../session/identity/data/IdentityStore.kt | 2 + .../session/identity/db/IdentityDataEntity.kt | 3 +- .../identity/db/IdentityDataEntityQuery.kt | 7 + .../session/identity/db/IdentityMapper.kt | 3 +- .../session/identity/db/RealmIdentityStore.kt | 8 + .../db/RealmIdentityStoreMigration.kt | 43 ++++ .../notification/DefaultPushRuleService.kt | 34 +-- .../session/profile/DefaultProfileService.kt | 12 +- .../sdk/internal/session/room/DefaultRoom.kt | 12 +- .../session/room/DefaultRoomService.kt | 30 +++ .../session/room/RoomAvatarResolver.kt | 15 +- .../room/alias/GetRoomIdByAliasTask.kt | 13 +- .../session/room/create/CreateRoomBody.kt | 4 +- .../room/create/CreateRoomBodyBuilder.kt | 2 +- .../session/room/create/CreateRoomTask.kt | 46 +++- .../session/room/draft/DefaultDraftService.kt | 14 +- .../membership/RoomDisplayNameResolver.kt | 49 +++- .../room/membership/RoomMemberHelper.kt | 4 +- .../DefaultRoomPushRuleService.kt | 15 +- .../room/reporting/DefaultReportingService.kt | 14 +- .../session/room/state/DefaultStateService.kt | 29 +-- .../session/room/tags/DefaultTagsService.kt | 21 +- .../room/timeline/DefaultTimelineService.kt | 2 +- .../session/search/DefaultSearchService.kt | 45 ++-- .../session/sync/CryptoSyncHandler.kt | 12 +- .../session/sync/RoomTypingUsersHandler.kt | 2 +- .../sdk/internal/session/sync/SyncAPI.kt | 13 +- .../sdk/internal/session/sync/SyncTask.kt | 12 +- .../typing/DefaultTypingUsersTracker.kt | 2 +- .../session/user/DefaultUserService.kt | 31 +++ .../sdk/internal/session/user/UserStore.kt | 19 ++ .../internal/session/widgets/WidgetManager.kt | 2 +- .../android/sdk/internal/util/StringUtils.kt | 2 - .../src/main/res/values-ca/strings.xml | 234 ++++++++++++++---- .../src/main/res/values-es/strings.xml | 104 ++++---- .../src/main/res/values-fa/strings.xml | 4 +- .../src/main/res/values-hu/strings.xml | 44 +++- .../src/main/res/values-it/strings_sas.xml | 68 +++++ .../src/main/res/values-ja/strings.xml | 44 ++-- .../src/main/res/values/strings.xml | 28 ++- 95 files changed, 1082 insertions(+), 549 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt create mode 100644 matrix-sdk-android/src/main/res/values-it/strings_sas.xml diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 6ca21262..3d81c0db 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:6.1.0" + classpath "io.realm:realm-gradle-plugin:10.0.0" } } @@ -25,7 +25,7 @@ android { minSdkVersion 21 targetSdkVersion 29 versionCode 1 - versionName "1.0.10" + versionName "1.0.11" // Multidex is useful for tests multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -149,7 +149,7 @@ dependencies { implementation 'androidx.exifinterface:exifinterface:1.3.0' // Database - implementation 'com.github.Zhuinden:realm-monarchy:0.5.1' + implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' kapt 'dk.ilios:realmfieldnameshelper:1.1.1' // Work diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt index ec5477f9..103b638c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt @@ -43,8 +43,8 @@ class ChangePasswordTest : InstrumentedTest { val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false)) // Change password - commonTestHelper.doSync<Unit> { - session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD, it) + commonTestHelper.runBlockingTest { + session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD) } // Try to login with the previous password, it will fail 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 a6fbfd9b..9996eef0 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 @@ -43,8 +43,8 @@ class DeactivateAccountTest : InstrumentedTest { val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false)) // Deactivate the account - commonTestHelper.doSync<Unit> { - session.deactivateAccount(TestConstants.PASSWORD, false, it) + commonTestHelper.runBlockingTest { + session.deactivateAccount(TestConstants.PASSWORD, false) } // Try to login on the previous account, it will fail (M_USER_DEACTIVATED) 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 cbe4cca8..0e7088a6 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 @@ -40,6 +40,7 @@ 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 @@ -343,6 +344,14 @@ class CommonTestHelper(context: Context) { await(latch, timeout) } + fun <T> runBlockingTest(timeout: Long = TestConstants.timeOutMillis, block: suspend () -> T): T { + return runBlocking { + withTimeout(timeout) { + block() + } + } + } + // Transform a method with a MatrixCallback to a synchronous method inline fun <reified T> doSync(timeout: Long? = TestConstants.timeOutMillis, block: (MatrixCallback<T>) -> Unit): T { val lock = CountDownLatch(1) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 1a9165ad..cbb22daf 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -68,8 +68,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { if (encryptedRoom) { val room = aliceSession.getRoom(roomId)!! - mTestHelper.doSync<Unit> { - room.enableEncryption(callback = it) + mTestHelper.runBlockingTest { + room.enableEncryption() } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index 75c7ad4d..a81f503e 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -555,7 +555,7 @@ class SASTest : InstrumentedTest { mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull() + val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() requestID = prAlicePOV?.transactionId Log.v("TEST", "== alicePOV is $prAlicePOV") prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId @@ -566,7 +566,7 @@ class SASTest : InstrumentedTest { mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull() + val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() Log.v("TEST", "== prBobPOV is $prBobPOV") prBobPOV?.transactionId == requestID } @@ -581,7 +581,7 @@ class SASTest : InstrumentedTest { // wait for alice to get the ready mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull() + val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() Log.v("TEST", "== prAlicePOV is $prAlicePOV") prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null } 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 7db159cd..ae300c93 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 @@ -71,38 +71,27 @@ class SearchMessagesTest : InstrumentedTest { commonTestHelper.await(lock) lock = CountDownLatch(1) - aliceSession - .searchService() - .search( - searchTerm = "lore", - limit = 10, - includeProfile = true, - afterLimit = 0, - beforeLimit = 10, - orderByRecent = true, - nextBatch = null, - roomId = aliceRoomId, - callback = object : MatrixCallback<SearchResult> { - override fun onSuccess(data: SearchResult) { - super.onSuccess(data) - assertTrue(data.results?.size == 2) - assertTrue( - data.results - ?.all { - (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() - }.orFalse() - ) - lock.countDown() - } - - override fun onFailure(failure: Throwable) { - super.onFailure(failure) - fail(failure.localizedMessage) - lock.countDown() - } - } - ) - lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS) + val data = commonTestHelper.runBlockingTest { + aliceSession + .searchService() + .search( + searchTerm = "lore", + limit = 10, + includeProfile = true, + afterLimit = 0, + beforeLimit = 10, + orderByRecent = true, + nextBatch = null, + roomId = aliceRoomId + ) + } + assertTrue(data.results?.size == 2) + assertTrue( + data.results + ?.all { + (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + }.orFalse() + ) aliceTimeline.removeAllListeners() cryptoTestData.cleanUp(commonTestHelper) diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt index 5c03e8a8..630f6f1e 100644 --- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt +++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt @@ -66,9 +66,9 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger { } private fun logJson(formattedJson: String) { - val arr = formattedJson.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - for (s in arr) { - Timber.v(s) - } + formattedJson + .lines() + .dropLastWhile { it.isEmpty() } + .forEach { Timber.v(it) } } } 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 645fb55b..48705ee7 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 @@ -27,7 +27,7 @@ interface LoginWizard { * @param password the password field * @param deviceName the initial device name * @param callback the matrix callback on which you'll receive the result of authentication. - * @return return a [Cancelable] + * @return a [Cancelable] */ fun login(login: String, password: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt index a17e65b8..e264843e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt @@ -22,3 +22,8 @@ fun CharSequence.ensurePrefix(prefix: CharSequence): CharSequence { else -> "$prefix$this" } } + +/** + * Append a new line and then the provided string + */ +fun StringBuilder.appendNl(str: String) = append("\n").append(str) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt index 880a7be9..4da16626 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt @@ -15,11 +15,9 @@ */ package org.matrix.android.sdk.api.pushrules -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.pushrules.rest.RuleSet import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.util.Cancelable interface PushRuleService { /** @@ -29,13 +27,13 @@ interface PushRuleService { fun getPushRules(scope: String = RuleScope.GLOBAL): RuleSet - fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable + suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean) - fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable + suspend fun addPushRule(kind: RuleKind, pushRule: PushRule) - fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable + suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule) - fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable + suspend fun removePushRule(kind: RuleKind, pushRule: PushRule) fun addPushRuleListener(listener: PushRuleListener) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt index 4e24a170..19549a33 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.raw -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * Useful methods to fetch raw data from the server. The access token will not be used to fetched the data */ @@ -26,17 +23,15 @@ interface RawService { /** * Get a URL, either from cache or from the remote server, depending on the cache strategy */ - fun getUrl(url: String, - rawCacheStrategy: RawCacheStrategy, - matrixCallback: MatrixCallback<String>): Cancelable + suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String /** * Specific case for the well-known file. Cache validity is 8 hours */ - fun getWellknown(userId: String, matrixCallback: MatrixCallback<String>): Cancelable + suspend fun getWellknown(userId: String): String /** * Clear all the cache data */ - fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable + suspend fun clearCache() } 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 b05f0036..8915202f 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 @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.account -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to manage the account. It's implemented at the session level. */ @@ -28,7 +25,7 @@ interface AccountService { * @param password Current password. * @param newPassword New password */ - fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable + suspend fun changePassword(password: String, newPassword: String) /** * Deactivate the account. @@ -46,5 +43,5 @@ interface AccountService { * @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 */ - fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable + suspend fun deactivateAccount(password: String, eraseAllData: Boolean) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt index 65c6a1f1..2413786e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt @@ -41,7 +41,7 @@ interface VerificationService { fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction? - fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>? + fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest> fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest? 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 82dea81a..0a7f3ff0 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 @@ -56,6 +56,7 @@ object EventType { const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups" const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events" const val STATE_ROOM_ENCRYPTION = "m.room.encryption" + const val STATE_ROOM_SERVER_ACL = "m.room.server_acl" // Call Events const val CALL_INVITE = "m.call.invite" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt index a4186b5a..25c69e50 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.group -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to interact within a group. */ @@ -28,8 +25,7 @@ interface Group { /** * This methods allows you to refresh data about this group. It will be reflected on the GroupSummary. * The SDK also takes care of refreshing group data every hour. - * @param callback : the matrix callback to be notified of success or failure * @return a Cancelable to be able to cancel requests. */ - fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable + suspend fun fetchGroupData() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt index 537104a0..aedb8137 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt @@ -92,9 +92,29 @@ interface IdentityService { /** * Search MatrixId of users providing email and phone numbers + * Note the the user consent has to be set to true, or it will throw a UserConsentNotProvided failure + * Application has to explicitly ask for the user consent, and the answer can be stored using [setUserConsent] + * Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details. */ fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable + /** + * Return the current user consent for the current identity server, which has been stored using [setUserConsent]. + * If [setUserConsent] has not been called, the returned value will be false. + * Note that if the identity server is changed, the user consent is reset to false. + * @return the value stored using [setUserConsent] or false if [setUserConsent] has never been called, or if the identity server + * has been changed + */ + fun getUserConsent(): Boolean + + /** + * Set the user consent to the provided value. Application MUST explicitly ask for the user consent to send their private data + * (email and phone numbers) to the identity server. + * Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details. + * @param newValue true if the user explicitly give their consent, false if the user wants to revoke their consent. + */ + fun setUserConsent(newValue: Boolean) + /** * Get the status of the current user's threePid * A lookup will be performed, but also pending binding state will be restored diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt index 72bb72cc..42fdb976 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt @@ -24,6 +24,7 @@ sealed class IdentityServiceError : Failure.FeatureFailure() { object NoIdentityServerConfigured : IdentityServiceError() object TermsNotSignedException : IdentityServiceError() object BulkLookupSha256NotSupported : IdentityServiceError() + object UserConsentNotProvided : IdentityServiceError() object BindingError : IdentityServiceError() object NoCurrentBindingError : IdentityServiceError() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt index 7f264c62..5e9f3e1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.permalinks import android.text.Spannable +import org.matrix.android.sdk.api.MatrixPatterns /** * MatrixLinkify take a piece of text and turns all of the @@ -35,7 +36,7 @@ object MatrixLinkify { * I disable it because it mess up with pills, and even with pills, it does not work correctly: * The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to */ - /* + // sanity checks if (spannable.isEmpty()) { return false @@ -48,14 +49,21 @@ object MatrixLinkify { val startPos = match.range.first if (startPos == 0 || text[startPos - 1] != '/') { val endPos = match.range.last + 1 - val url = text.substring(match.range) + var url = text.substring(match.range) + if (MatrixPatterns.isUserId(url) + || MatrixPatterns.isRoomAlias(url) + || MatrixPatterns.isRoomId(url) + || MatrixPatterns.isGroupId(url) + || MatrixPatterns.isEventId(url)) { + url = PermalinkService.MATRIX_TO_URL_BASE + url + } val span = MatrixPermalinkSpan(url, callback) spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } } } return hasMatch - */ - return false + +// return false } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index dc47c81a..347a3bb5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -44,13 +44,12 @@ object PermalinkParser { if (fragment.isNullOrEmpty()) { return PermalinkData.FallbackLink(uri) } - val indexOfQuery = fragment.indexOf("?") - val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment + val safeFragment = fragment.substringBefore('?') val viaQueryParameters = fragment.getViaParameters() // we are limiting to 2 params val params = safeFragment - .split(MatrixPatterns.SEP_REGEX.toRegex()) + .split(MatrixPatterns.SEP_REGEX) .filter { it.isNotEmpty() } .take(2) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index b772225f..f30037e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.room import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.util.Cancelable @@ -141,4 +142,20 @@ interface RoomService { * - the power level of the users are not taken into account. Normally in a DM, the 2 members are admins of the room */ fun getExistingDirectRoomWithUser(otherUserId: String): String? + + /** + * Get a room member for the tuple {userId,roomId} + * @param userId the userId to look for. + * @param roomId the roomId to look for. + * @return the room member or null + */ + fun getRoomMember(userId: String, roomId: String): RoomMemberSummary? + + /** + * Observe a live room member for the tuple {userId,roomId} + * @param userId the userId to look for. + * @param roomId the roomId to look for. + * @return a LiveData of the optional found room member + */ + fun getRoomMemberLive(userId: String, roomId: String): LiveData<Optional<RoomMemberSummary>> } 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 e7e6bacc..1251fd98 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 @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.session.room.crypto -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM interface RoomCryptoService { @@ -30,6 +29,5 @@ interface RoomCryptoService { /** * Enable encryption of the room */ - fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM, - callback: MatrixCallback<Unit>) + suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt index b0df2963..b4e2dc64 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt @@ -22,4 +22,9 @@ import org.matrix.android.sdk.api.failure.MatrixError sealed class CreateRoomFailure : Failure.FeatureFailure() { object CreatedWithTimeout : CreateRoomFailure() data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure() + sealed class RoomAliasError : CreateRoomFailure() { + object AliasEmpty : RoomAliasError() + object AliasNotAvailable : RoomAliasError() + object AliasInvalid : RoomAliasError() + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt new file mode 100644 index 00000000..92078054 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt @@ -0,0 +1,59 @@ +/* + * 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 + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing the EventType.STATE_ROOM_SERVER_ACL state event content + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl + */ +@JsonClass(generateAdapter = true) +data class RoomServerAclContent( + /** + * True to allow server names that are IP address literals. False to deny. + * Defaults to true if missing or otherwise not a boolean. + * This is strongly recommended to be set to false as servers running with IP literal names are strongly + * discouraged in order to require legitimate homeservers to be backed by a valid registered domain name. + */ + @Json(name = "allow_ip_literals") + val allowIpLiterals: Boolean = true, + + /** + * The server names to allow in the room, excluding any port information. Wildcards may be used to cover + * a wider range of hosts, where * matches zero or more characters and ? matches exactly one character. + * + * This defaults to an empty list when not provided, effectively disallowing every server. + */ + @Json(name = "allow") + val allowList: List<String> = emptyList(), + + /** + * The server names to disallow in the room, excluding any port information. Wildcards may be used to cover + * a wider range of hosts, where * matches zero or more characters and ? matches exactly one character. + * + * This defaults to an empty list when not provided. + */ + @Json(name = "deny") + val denyList: List<String> = emptyList() + +) { + companion object { + const val ALL = "*" + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt index 892a8657..80e3741a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/CreateRoomParams.kt @@ -94,7 +94,22 @@ class CreateRoomParams { * The server will clobber the following keys: creator. * Future versions of the specification may allow the server to clobber other keys. */ - var creationContent: Any? = null + val creationContent = mutableMapOf<String, Any>() + + /** + * Set to true to disable federation of this room. + * Default: false + */ + var disableFederation = false + set(value) { + field = value + if (value) { + creationContent[CREATION_CONTENT_KEY_M_FEDERATE] = false + } else { + // This is the default value, we remove the field + creationContent.remove(CREATION_CONTENT_KEY_M_FEDERATE) + } + } /** * The power level content to override in the default power level event @@ -120,4 +135,8 @@ class CreateRoomParams { fun enableEncryption() { algorithm = MXCRYPTO_ALGORITHM_MEGOLM } + + companion object { + private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate" + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt index 32d60335..eb822c68 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt @@ -17,12 +17,10 @@ package org.matrix.android.sdk.api.session.room.notification import androidx.lifecycle.LiveData -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable interface RoomPushRuleService { fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> - fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable + suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt index 0ccdfd1d..a444e234 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.room.reporting -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to report content of an event. */ @@ -28,5 +25,5 @@ interface ReportingService { * Report content * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid */ - fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable + suspend fun reportContent(eventId: String, score: Int, reason: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt index 116a60e3..a9481d71 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt @@ -17,8 +17,6 @@ package org.matrix.android.sdk.api.session.room.send import androidx.lifecycle.LiveData -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional interface DraftService { @@ -26,12 +24,12 @@ interface DraftService { /** * Save or update a draft to the room */ - fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable + suspend fun saveDraft(draft: UserDraft) /** * Delete the last draft, basically just after sending the message */ - fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable + suspend fun deleteDraft() /** * Return the current draft or null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt index 3278c640..69fde61f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.room.tags -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to handle tags of a room. It's implemented at the room level. */ @@ -26,10 +23,10 @@ interface TagsService { /** * Add a tag to a room */ - fun addTag(tag: String, order: Double?, callback: MatrixCallback<Unit>): Cancelable + suspend fun addTag(tag: String, order: Double?) /** * Remove tag from a room */ - fun deleteTag(tag: String, callback: MatrixCallback<Unit>): Cancelable + suspend fun deleteTag(tag: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt index ef2eec43..bc1c9e57 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.search -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to search messages in rooms. */ @@ -35,15 +32,13 @@ interface SearchService { * @param beforeLimit how many events before the result are returned. * @param afterLimit how many events after the result are returned. * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned. - * @param callback Callback to get the search result */ - fun search(searchTerm: String, - roomId: String, - nextBatch: String?, - orderByRecent: Boolean, - limit: Int, - beforeLimit: Int, - afterLimit: Int, - includeProfile: Boolean, - callback: MatrixCallback<SearchResult>): Cancelable + suspend fun search(searchTerm: String, + roomId: String, + nextBatch: String?, + orderByRecent: Boolean, + limit: Int, + beforeLimit: Int, + afterLimit: Int, + includeProfile: Boolean): SearchResult } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt index 2cfc4b73..ab85f979 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt @@ -35,6 +35,11 @@ interface UserService { */ fun getUser(userId: String): User? + /** + * Try to resolve user from known users, or using profile api + */ + fun resolveUser(userId: String, callback: MatrixCallback<User>) + /** * Search list of users on server directory. * @param search the searched term 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 a8580bab..a786ebd4 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 @@ -123,6 +123,7 @@ internal abstract class CryptoModule { } .name("crypto_store.realm") .modules(RealmCryptoStoreModule()) + .allowWritesOnUiThread(true) .schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION) .migration(realmCryptoStoreMigration) .build() 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 d3a3fd9f..ebd809f7 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 @@ -767,9 +767,9 @@ internal class DefaultCryptoService @Inject constructor( */ private fun onRoomKeyEvent(event: Event) { val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return - Timber.v("## CRYPTO | GOSSIP onRoomKeyEvent() : type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>") + Timber.i("## CRYPTO | onRoomKeyEvent() from: ${event.senderId} type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>") if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) { - Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : missing fields") + Timber.e("## CRYPTO | onRoomKeyEvent() : missing fields") return } val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm) @@ -782,20 +782,20 @@ internal class DefaultCryptoService @Inject constructor( private fun onKeyWithHeldReceived(event: Event) { val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also { - Timber.e("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields") + Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields") } - Timber.d("## CRYPTO | onKeyWithHeldReceived() received : content <$withHeldContent>") + Timber.i("## CRYPTO | onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>") val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm) if (alg is IMXWithHeldExtension) { alg.onRoomKeyWithHeldEvent(withHeldContent) } else { - Timber.e("## CRYPTO | onKeyWithHeldReceived() : Unable to handle WithHeldContent for ${withHeldContent.algorithm}") + Timber.e("## CRYPTO | onKeyWithHeldReceived() from:${event.senderId}: Unable to handle WithHeldContent for ${withHeldContent.algorithm}") return } } private fun onSecretSendReceived(event: Event) { - Timber.i("## CRYPTO | GOSSIP onSecretSend() : onSecretSendReceived ${event.content?.get("sender_key")}") + Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}") if (!event.isEncrypted()) { // secret send messages must be encrypted Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event") 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 38488f1c..92b77288 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 @@ -119,7 +119,7 @@ internal class EventDecryptor @Inject constructor( markOlmSessionForUnwedging(event.senderId ?: "", it) } ?: run { - Timber.v("## CRYPTO | markOlmSessionForUnwedging() : Failed to find sender crypto device") + Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging") } } } @@ -137,16 +137,18 @@ internal class EventDecryptor @Inject constructor( val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0 val now = System.currentTimeMillis() if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) { - Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another") + Timber.w("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another") return } - Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}") + Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}") lastNewSessionForcedDates.setObject(senderId, deviceKey, now) // offload this from crypto thread (?) cryptoCoroutineScope.launch(coroutineDispatchers.computation) { - ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true) + val ensured = ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true) + + Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}") // Now send a blank message on that session so the other side knows about it. // (The keyshare request is sent in the clear so that won't do) @@ -159,10 +161,14 @@ internal class EventDecryptor @Inject constructor( val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) val sendToDeviceMap = MXUsersDevicesMap<Any>() sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload) - Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}") + Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}") withContext(coroutineDispatchers.io) { val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - sendToDeviceTask.execute(sendToDeviceParams) + try { + sendToDeviceTask.execute(sendToDeviceParams) + } catch (failure: Throwable) { + Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}") + } } } } 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 97ae0b9d..4f94a27b 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 @@ -54,6 +54,7 @@ internal class IncomingGossipingRequestManager @Inject constructor( private val cryptoCoroutineScope: CoroutineScope) { private val executor = Executors.newSingleThreadExecutor() + // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations // we received in the current sync. private val receivedGossipingRequests = ArrayList<IncomingShareRequestCommon>() @@ -103,11 +104,11 @@ internal class IncomingGossipingRequestManager @Inject constructor( * @param event the announcement event. */ fun onGossipingRequestEvent(event: Event) { - Timber.v("## CRYPTO | GOSSIP onGossipingRequestEvent type ${event.type} from user ${event.senderId}") val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>() + Timber.i("## CRYPTO | GOSSIP onGossipingRequestEvent received type ${event.type} from user:${event.senderId}, content:$roomKeyShare") // val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it } when (roomKeyShare?.action) { - GossipingToDeviceObject.ACTION_SHARE_REQUEST -> { + GossipingToDeviceObject.ACTION_SHARE_REQUEST -> { if (event.getClearType() == EventType.REQUEST_SECRET) { IncomingSecretShareRequest.fromEvent(event)?.let { if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) { @@ -324,7 +325,7 @@ internal class IncomingGossipingRequestManager @Inject constructor( val isDeviceLocallyVerified = cryptoStore.getUserDevice(userId, deviceId)?.trustLevel?.isLocallyVerified() when (secretName) { - MASTER_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.master + MASTER_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.master SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 1a4d1136..c952602d 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -760,7 +760,7 @@ internal class MXOlmDevice @Inject constructor( return session } } else { - Timber.v("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") + Timber.w("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index f0a34139..bcaa16f3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -100,7 +100,7 @@ internal class SendGossipWorker(context: Context, requestId = params.requestId, state = GossipingRequestState.FAILED_TO_ACCEPTED ) - Timber.e("no session with this device, probably because there were no one-time keys.") + Timber.e("no session with this device $requestingDeviceId, probably because there were no one-time keys.") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index b05f2cd5..95b99c54 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -69,7 +69,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor( // // That should eventually resolve itself, but it's poor form. - Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") + Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) @@ -90,7 +90,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor( oneTimeKey = key } if (oneTimeKey == null) { - Timber.v("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + " for device " + userId + " : " + deviceId) continue } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index e0116fae..787d16de 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -243,8 +243,7 @@ internal class MXMegolmDecryption(private val userId: String, return } if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) { - Timber.v("## CRYPTO | onRoomKeyEvent(), forward adding key : roomId ${roomKeyContent.roomId}" + - " sessionId ${roomKeyContent.sessionId} sessionKey ${roomKeyContent.sessionKey}") + Timber.i("## CRYPTO | onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}") val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>() ?: return @@ -273,9 +272,7 @@ internal class MXMegolmDecryption(private val userId: String, keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key } else { - Timber.v("## CRYPTO | onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId - + " sessionKey " + roomKeyContent.sessionKey) // from " + event); - + Timber.i("## CRYPTO | onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}") if (null == senderKey) { Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)") return @@ -285,7 +282,7 @@ internal class MXMegolmDecryption(private val userId: String, keysClaimed = event.getKeysClaimed().toMutableMap() } - Timber.e("## CRYPTO | onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}") + Timber.i("## CRYPTO | onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}") val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId, roomKeyContent.sessionKey, roomKeyContent.roomId, @@ -349,10 +346,10 @@ internal class MXMegolmDecryption(private val userId: String, if (olmSessionResult?.sessionId == null) { // no session with this device, probably because there // were no one-time keys. + Timber.e("no session with this device $deviceId, probably because there were no one-time keys.") return@mapCatching } - Timber.v("## CRYPTO | shareKeysWithDevice() : sharing keys for session" + - " ${body.senderKey}|${body.sessionId} with device $userId:$deviceId") + Timber.i("## CRYPTO | shareKeysWithDevice() : sharing session ${body.sessionId} with device $userId:$deviceId") val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY) runCatching { olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) } @@ -363,6 +360,7 @@ internal class MXMegolmDecryption(private val userId: String, }, { // TODO + Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body") } ) @@ -370,9 +368,13 @@ internal class MXMegolmDecryption(private val userId: String, val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) val sendToDeviceMap = MXUsersDevicesMap<Any>() sendToDeviceMap.setObject(userId, deviceId, encodedPayload) - Timber.v("## CRYPTO | shareKeysWithDevice() : sending to $userId:$deviceId") + Timber.i("## CRYPTO | shareKeysWithDevice() : sending ${body.sessionId} to $userId:$deviceId") val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - sendToDeviceTask.execute(sendToDeviceParams) + try { + sendToDeviceTask.execute(sendToDeviceParams) + } catch (failure: Throwable) { + Timber.e(failure, "## CRYPTO | shareKeysWithDevice() : Failed to send ${body.sessionId} to $userId:$deviceId") + } } } } 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 e55cf371..fd431ce7 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 @@ -217,8 +217,10 @@ internal class MXMegolmEncryption( Timber.v("## CRYPTO | shareUserDevicesKey() : starts") val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser) - Timber.v("## CRYPTO | shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " - + (System.currentTimeMillis() - t0) + " ms") + Timber.v( + """## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms""" + .trimMargin() + ) val contentMap = MXUsersDevicesMap<Any>() var haveTargets = false val userIds = results.userIds @@ -242,7 +244,7 @@ internal class MXMegolmEncryption( continue } - Timber.v("## CRYPTO | shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + Timber.i("## CRYPTO | shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo))) haveTargets = true } @@ -270,21 +272,22 @@ internal class MXMegolmEncryption( if (haveTargets) { t0 = System.currentTimeMillis() - Timber.v("## CRYPTO | shareUserDevicesKey() : has target") + Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target") val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) try { sendToDeviceTask.execute(sendToDeviceParams) - Timber.v("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms") + Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms") } catch (failure: Throwable) { // What to do here... Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ") } } else { - Timber.v("## CRYPTO | shareUserDevicesKey() : no need to sharekey") + Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey") } } private fun notifyKeyWithHeld(targets: List<UserDevice>, sessionId: String, senderKey: String?, code: WithHeldCode) { + Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId ") val withHeldContent = RoomKeyWithHeldContent( roomId = roomId, senderKey = senderKey, @@ -393,16 +396,16 @@ internal class MXMegolmEncryption( userId: String, deviceId: String, senderKey: String): Boolean { - Timber.d("[MXMegolmEncryption] reshareKey: $sessionId to $userId:$deviceId") + Timber.i("## Crypto process reshareKey for $sessionId to $userId:$deviceId") val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) ?: return false - .also { Timber.w("Device not found") } + .also { Timber.w("## Crypto reshareKey: Device not found") } // Get the chain index of the key we previously sent this device val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId, deviceId) ?: return false .also { // Send a room key with held notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED) - Timber.w("[MXMegolmEncryption] reshareKey : ERROR : Never share megolm with this device") + Timber.w("## Crypto reshareKey: ERROR : Never share megolm with this device") } val devicesByUser = mapOf(userId to listOf(deviceInfo)) @@ -411,9 +414,11 @@ internal class MXMegolmEncryption( olmSessionResult?.sessionId ?: // no session with this device, probably because there were no one-time keys. // ensureOlmSessionsForDevicesAction has already done the logging, so just skip it. - return false + return false.also { + Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys") + } - Timber.d("[MXMegolmEncryption] reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId") + Timber.i("[MXMegolmEncryption] reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId") val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY) @@ -425,6 +430,7 @@ internal class MXMegolmEncryption( }, { // TODO + Timber.e(it, "[MXMegolmEncryption] reshareKey: failed to get session $sessionId|$senderKey|$roomId") } ) @@ -432,13 +438,14 @@ internal class MXMegolmEncryption( val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) val sendToDeviceMap = MXUsersDevicesMap<Any>() sendToDeviceMap.setObject(userId, deviceId, encodedPayload) - Timber.v("## CRYPTO | CRYPTO | reshareKey() : sending to $userId:$deviceId") + Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId") val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) return try { sendToDeviceTask.execute(sendToDeviceParams) + Timber.i("## CRYPTO reshareKey() : successfully send <$sessionId> to $userId:$deviceId") true } catch (failure: Throwable) { - Timber.e(failure, "## CRYPTO | CRYPTO | reshareKey() : fail to send <$sessionId> to $userId:$deviceId") + Timber.e(failure, "## CRYPTO reshareKey() : fail to send <$sessionId> to $userId:$deviceId") false } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 1871dba0..bcad448e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -38,7 +38,6 @@ import org.matrix.android.sdk.internal.task.TaskThread 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.withoutPrefix import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo @@ -444,7 +443,7 @@ internal class DefaultCrossSigningService @Inject constructor( } else { // Maybe it's signed by a locally trusted device? myMasterKey.signatures?.get(userId)?.forEach { (key, value) -> - val potentialDeviceId = key.withoutPrefix("ed25519:") + val potentialDeviceId = key.removePrefix("ed25519:") val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId) if (potentialDevice != null && potentialDevice.isVerified) { // Check signature validity? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt index f28fe7d6..665d770e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt @@ -241,9 +241,9 @@ internal class UpdateTrustWorker(context: Context, private fun computeRoomShield(activeMemberUserIds: List<String>, roomSummaryEntity: RoomSummaryEntity): RoomEncryptionTrustLevel { Timber.d("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} -> $activeMemberUserIds") // The set of “all users†depends on the type of room: - // For regular / topic rooms, all users including yourself, are considered when decorating a room + // For regular / topic rooms which have more than 2 members (including yourself) are considered when decorating a room // For 1:1 and group DM rooms, all other users (i.e. excluding yourself) are considered when decorating a room - val listToCheck = if (roomSummaryEntity.isDirect) { + val listToCheck = if (roomSummaryEntity.isDirect || activeMemberUserIds.size <= 2) { activeMemberUserIds.filter { it != myUserId } } else { activeMemberUserIds 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 72274aa7..6b83c4f7 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 @@ -1679,27 +1679,24 @@ internal class RealmCryptoStore @Inject constructor( // Only keep one week history realm.where<IncomingGossipingRequestEntity>() .lessThan(IncomingGossipingRequestEntityFields.LOCAL_CREATION_TIMESTAMP, prevWeekTs) - .findAll().let { - Timber.i("## Crypto Clean up ${it.size} IncomingGossipingRequestEntity") - it.deleteAllFromRealm() - } + .findAll() + .also { Timber.i("## Crypto Clean up ${it.size} IncomingGossipingRequestEntity") } + .deleteAllFromRealm() // Clean the cancelled ones? realm.where<OutgoingGossipingRequestEntity>() .equalTo(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, OutgoingGossipingRequestState.CANCELLED.name) .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name) - .findAll().let { - Timber.i("## Crypto Clean up ${it.size} OutgoingGossipingRequestEntity") - it.deleteAllFromRealm() - } + .findAll() + .also { Timber.i("## Crypto Clean up ${it.size} OutgoingGossipingRequestEntity") } + .deleteAllFromRealm() // Only keep one week history realm.where<GossipingEventEntity>() .lessThan(GossipingEventEntityFields.AGE_LOCAL_TS, prevWeekTs) - .findAll().let { - Timber.i("## Crypto Clean up ${it.size} GossipingEventEntityFields") - it.deleteAllFromRealm() - } + .findAll() + .also { Timber.i("## Crypto Clean up ${it.size} GossipingEventEntityFields") } + .deleteAllFromRealm() // Can we do something for WithHeldSessionEntity? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt index 7f027503..a92f5c5b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt @@ -537,11 +537,10 @@ internal class DefaultVerificationService @Inject constructor( // If there is a corresponding request, we can auto accept // as we are the one requesting in first place (or we accepted the request) // I need to check if the pending request was related to this device also - val autoAccept = getExistingVerificationRequest(otherUserId)?.any { + val autoAccept = getExistingVerificationRequests(otherUserId).any { it.transactionId == startReq.transactionId && (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId) } - ?: false val tx = DefaultIncomingSASDefaultVerificationTransaction( // this, setDeviceVerificationAction, @@ -837,8 +836,8 @@ internal class DefaultVerificationService @Inject constructor( // SAS do not care for now? } - // Now transactions are udated, let's also update Requests - val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId } + // Now transactions are updated, let's also update Requests + val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == doneReq.transactionId } if (existingRequest == null) { Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}") return @@ -892,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor( private fun handleReadyReceived(senderId: String, readyReq: ValidVerificationInfoReady, transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) { - val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionId } + val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == readyReq.transactionId } if (existingRequest == null) { Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}") return @@ -1041,9 +1040,9 @@ internal class DefaultVerificationService @Inject constructor( } } - override fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>? { + override fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest> { synchronized(lock = pendingRequests) { - return pendingRequests[otherUserId] + return pendingRequests[otherUserId].orEmpty() } } @@ -1205,7 +1204,7 @@ internal class DefaultVerificationService @Inject constructor( Timber.i("## Requesting verification to user: $otherUserId with device list $otherDevices") val targetDevices = otherDevices ?: cryptoStore.getUserDevices(otherUserId) - ?.values?.map { it.deviceId } ?: emptyList() + ?.values?.map { it.deviceId }.orEmpty() val requestsForUser = pendingRequests.getOrPut(otherUserId) { mutableListOf() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt index cb254eac..22a190c6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt @@ -28,7 +28,6 @@ import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.extensions.toUnsignedInt -import org.matrix.android.sdk.internal.util.withoutPrefix import org.matrix.olm.OlmSAS import org.matrix.olm.OlmUtility import timber.log.Timber @@ -250,7 +249,7 @@ internal abstract class SASDefaultVerificationTransaction( // cannot be empty because it has been validated theirMacSafe.mac.keys.forEach { - val keyIDNoPrefix = it.withoutPrefix("ed25519:") + val keyIDNoPrefix = it.removePrefix("ed25519:") val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint() if (otherDeviceKey == null) { Timber.w("## SAS Verification: Could not find device $keyIDNoPrefix to verify") @@ -273,7 +272,7 @@ internal abstract class SASDefaultVerificationTransaction( if (otherCrossSigningMasterKeyPublic != null) { // Did the user signed his master key theirMacSafe.mac.keys.forEach { - val keyIDNoPrefix = it.withoutPrefix("ed25519:") + val keyIDNoPrefix = it.removePrefix("ed25519:") if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) { // Check the signature val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt index 6c430e85..0e3a7a2c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database import android.content.Context import android.util.Base64 import androidx.core.content.edit +import io.realm.Realm import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils import io.realm.RealmConfiguration @@ -46,7 +47,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context, private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE) private fun generateKeyForRealm(): ByteArray { - val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH) + val keyForRealm = ByteArray(Realm.ENCRYPTION_KEY_LENGTH) rng.nextBytes(keyForRealm) return keyForRealm } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt index 3324520d..244fe343 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt @@ -69,6 +69,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor( .apply { realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5)) } + .allowWritesOnUiThread(true) .modules(SessionRealmModule()) .schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION) .migration(migration) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt index 6c604f23..724ec0dc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt @@ -52,5 +52,8 @@ internal class TimeOutInterceptor @Inject constructor() : Interceptor { const val CONNECT_TIMEOUT = "CONNECT_TIMEOUT" const val READ_TIMEOUT = "READ_TIMEOUT" const val WRITE_TIMEOUT = "WRITE_TIMEOUT" + + // 1 minute + const val DEFAULT_LONG_TIMEOUT: Long = 60_000 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt index be01366e..3b0d7546 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt @@ -16,45 +16,28 @@ package org.matrix.android.sdk.internal.raw -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.raw.RawCacheStrategy import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import java.util.concurrent.TimeUnit import javax.inject.Inject internal class DefaultRawService @Inject constructor( - private val taskExecutor: TaskExecutor, private val getUrlTask: GetUrlTask, private val cleanRawCacheTask: CleanRawCacheTask ) : RawService { - override fun getUrl(url: String, - rawCacheStrategy: RawCacheStrategy, - matrixCallback: MatrixCallback<String>): Cancelable { - return getUrlTask - .configureWith(GetUrlTask.Params(url, rawCacheStrategy)) { - callback = matrixCallback - } - .executeBy(taskExecutor) + override suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String { + return getUrlTask.execute(GetUrlTask.Params(url, rawCacheStrategy)) } - override fun getWellknown(userId: String, - matrixCallback: MatrixCallback<String>): Cancelable { + override suspend fun getWellknown(userId: String): String { val homeServerDomain = userId.substringAfter(":") return getUrl( "https://$homeServerDomain/.well-known/matrix/client", - RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false), - matrixCallback + RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false) ) } - override fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable { - return cleanRawCacheTask - .configureWith(Unit) { - callback = matrixCallback - } - .executeBy(taskExecutor) + override suspend fun clearCache() { + cleanRawCacheTask.execute(Unit) } } 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 31a39d45..1165d211 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 @@ -16,30 +16,17 @@ package org.matrix.android.sdk.internal.session.account -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.account.AccountService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import javax.inject.Inject internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask, - private val deactivateAccountTask: DeactivateAccountTask, - private val taskExecutor: TaskExecutor) : AccountService { + private val deactivateAccountTask: DeactivateAccountTask) : AccountService { - override fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable { - return changePasswordTask - .configureWith(ChangePasswordTask.Params(password, newPassword)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun changePassword(password: String, newPassword: String) { + changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword)) } - override fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable { - return deactivateAccountTask - .configureWith(DeactivateAccountTask.Params(password, eraseAllData)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun deactivateAccount(password: String, eraseAllData: Boolean) { + deactivateAccountTask.execute(DeactivateAccountTask.Params(password, eraseAllData)) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt index 01b57767..4f610fd8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt @@ -16,20 +16,13 @@ package org.matrix.android.sdk.internal.session.group -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.group.Group -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultGroup(override val groupId: String, - private val taskExecutor: TaskExecutor, private val getGroupDataTask: GetGroupDataTask) : Group { - override fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable { + override suspend fun fetchGroupData() { val params = GetGroupDataTask.Params.FetchWithIds(listOf(groupId)) - return getGroupDataTask.configureWith(params) { - this.callback = callback - }.executeBy(taskExecutor) + getGroupDataTask.execute(params) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt index 31450763..653d2a69 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.group import org.matrix.android.sdk.api.session.group.Group import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.task.TaskExecutor import javax.inject.Inject internal interface GroupFactory { @@ -26,14 +25,12 @@ internal interface GroupFactory { } @SessionScope -internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask, - private val taskExecutor: TaskExecutor) : +internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask) : GroupFactory { override fun create(groupId: String): Group { return DefaultGroup( groupId = groupId, - taskExecutor = taskExecutor, getGroupDataTask = getGroupDataTask ) } 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 20f8b7f8..c6fb3415 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 @@ -55,6 +55,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.ensureProtocol import kotlinx.coroutines.withContext import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.extensions.orFalse import timber.log.Timber import javax.inject.Inject import javax.net.ssl.HttpsURLConnection @@ -243,7 +244,20 @@ internal class DefaultIdentityService @Inject constructor( )) } + override fun getUserConsent(): Boolean { + return identityStore.getIdentityData()?.userConsent.orFalse() + } + + override fun setUserConsent(newValue: Boolean) { + identityStore.setUserConsent(newValue) + } + override fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable { + if (!getUserConsent()) { + callback.onFailure(IdentityServiceError.UserConsentNotProvided) + return NoOpCancellable + } + if (threePids.isEmpty()) { callback.onSuccess(emptyList()) return NoOpCancellable @@ -255,6 +269,9 @@ internal class DefaultIdentityService @Inject constructor( } override fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable { + // Note: we do not require user consent here, because it is used for emails and phone numbers that the user has already sent + // to the home server, and not emails and phone numbers from the contact book of the user + if (threePids.isEmpty()) { callback.onSuccess(emptyMap()) return NoOpCancellable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt index 1bb57b47..7a39a333 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt @@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.session.identity.db.IdentityRealmModule import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStore import io.realm.RealmConfiguration import okhttp3.OkHttpClient +import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStoreMigration import java.io.File @Module @@ -59,6 +60,7 @@ internal abstract class IdentityModule { @SessionScope fun providesIdentityRealmConfiguration(realmKeysUtils: RealmKeysUtils, @SessionFilesDirectory directory: File, + migration: RealmIdentityStoreMigration, @UserMd5 userMd5: String): RealmConfiguration { return RealmConfiguration.Builder() .directory(directory) @@ -66,6 +68,9 @@ internal abstract class IdentityModule { .apply { realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5)) } + .schemaVersion(RealmIdentityStoreMigration.IDENTITY_STORE_SCHEMA_VERSION) + .migration(migration) + .allowWritesOnUiThread(true) .modules(IdentityRealmModule()) .build() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt index 0f04f2fe..54d35b34 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt @@ -20,5 +20,6 @@ internal data class IdentityData( val identityServerUrl: String?, val token: String?, val hashLookupPepper: String?, - val hashLookupAlgorithm: List<String> + val hashLookupAlgorithm: List<String>, + val userConsent: Boolean ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt index 3a905833..0e05224b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt @@ -27,6 +27,8 @@ internal interface IdentityStore { fun setToken(token: String?) + fun setUserConsent(consent: Boolean) + fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse) /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt index cc03465c..019289a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt @@ -23,7 +23,8 @@ internal open class IdentityDataEntity( var identityServerUrl: String? = null, var token: String? = null, var hashLookupPepper: String? = null, - var hashLookupAlgorithm: RealmList<String> = RealmList() + var hashLookupAlgorithm: RealmList<String> = RealmList(), + var userConsent: Boolean = false ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt index 062c28ea..5152e337 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt @@ -52,6 +52,13 @@ internal fun IdentityDataEntity.Companion.setToken(realm: Realm, } } +internal fun IdentityDataEntity.Companion.setUserConsent(realm: Realm, + newConsent: Boolean) { + get(realm)?.apply { + userConsent = newConsent + } +} + internal fun IdentityDataEntity.Companion.setHashDetails(realm: Realm, pepper: String, algorithms: List<String>) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt index 98207f1b..bf23c058 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt @@ -26,7 +26,8 @@ internal object IdentityMapper { identityServerUrl = entity.identityServerUrl, token = entity.token, hashLookupPepper = entity.hashLookupPepper, - hashLookupAlgorithm = entity.hashLookupAlgorithm.toList() + hashLookupAlgorithm = entity.hashLookupAlgorithm.toList(), + userConsent = entity.userConsent ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt index 0352e9b9..2fa3fc0c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt @@ -55,6 +55,14 @@ internal class RealmIdentityStore @Inject constructor( } } + override fun setUserConsent(consent: Boolean) { + Realm.getInstance(realmConfiguration).use { + it.executeTransaction { realm -> + IdentityDataEntity.setUserConsent(realm, consent) + } + } + } + override fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse) { Realm.getInstance(realmConfiguration).use { it.executeTransaction { realm -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt new file mode 100644 index 00000000..6081dbab --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.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.identity.db + +import io.realm.DynamicRealm +import io.realm.RealmMigration +import timber.log.Timber +import javax.inject.Inject + +internal class RealmIdentityStoreMigration @Inject constructor() : RealmMigration { + + companion object { + const val IDENTITY_STORE_SCHEMA_VERSION = 1L + } + + override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { + Timber.v("Migrating Realm Identity from $oldVersion to $newVersion") + + if (oldVersion <= 0) migrateTo1(realm) + } + + private fun migrateTo1(realm: DynamicRealm) { + Timber.d("Step 0 -> 1") + Timber.d("Add field userConsent (Boolean) and set the value to false") + + realm.schema.get("IdentityDataEntity") + ?.addField(IdentityDataEntityFields.USER_CONSENT, Boolean::class.java) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt index 217da269..e00d2ff2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.session.notification import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.api.pushrules.RuleSetKey @@ -24,7 +23,6 @@ import org.matrix.android.sdk.api.pushrules.getActions import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.pushrules.rest.RuleSet import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.util.Cancelable 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.query.where @@ -103,37 +101,21 @@ internal class DefaultPushRuleService @Inject constructor( ) } - override fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable { + override suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean) { // The rules will be updated, and will come back from the next sync response - return updatePushRuleEnableStatusTask - .configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) { - this.callback = callback - } - .executeBy(taskExecutor) + updatePushRuleEnableStatusTask.execute(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) } - override fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable { - return addPushRuleTask - .configureWith(AddPushRuleTask.Params(kind, pushRule)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun addPushRule(kind: RuleKind, pushRule: PushRule) { + addPushRuleTask.execute(AddPushRuleTask.Params(kind, pushRule)) } - override fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable { - return updatePushRuleActionsTask - .configureWith(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule) { + updatePushRuleActionsTask.execute(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule)) } - override fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable { - return removePushRuleTask - .configureWith(RemovePushRuleTask.Params(kind, pushRule)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun removePushRule(kind: RuleKind, pushRule: PushRule) { + removePushRuleTask.execute(RemovePushRuleTask.Params(kind, pushRule)) } override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt index 8d092772..5265e4f1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt @@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity import org.matrix.android.sdk.internal.database.model.UserThreePidEntity import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.content.FileUploader +import org.matrix.android.sdk.internal.session.user.UserStore import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.task.launchToCallback @@ -49,6 +50,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto private val finalizeAddingThreePidTask: FinalizeAddingThreePidTask, private val deleteThreePidTask: DeleteThreePidTask, private val pendingThreePidMapper: PendingThreePidMapper, + private val userStore: UserStore, private val fileUploader: FileUploader) : ProfileService { override fun getDisplayName(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable { @@ -70,17 +72,17 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto } override fun setDisplayName(userId: String, newDisplayName: String, matrixCallback: MatrixCallback<Unit>): Cancelable { - return setDisplayNameTask - .configureWith(SetDisplayNameTask.Params(userId = userId, newDisplayName = newDisplayName)) { - callback = matrixCallback - } - .executeBy(taskExecutor) + return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.io, matrixCallback) { + setDisplayNameTask.execute(SetDisplayNameTask.Params(userId = userId, newDisplayName = newDisplayName)) + userStore.updateDisplayName(userId, newDisplayName) + } } override fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback<Unit>): Cancelable { return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, matrixCallback) { val response = fileUploader.uploadFromUri(newAvatarUri, fileName, "image/jpeg") setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri)) + userStore.updateAvatar(userId, response.contentUri) } } 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 1338df68..c7bb640f 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 @@ -101,13 +101,13 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, return cryptoService.shouldEncryptForInvitedMembers(roomId) } - override fun enableEncryption(algorithm: String, callback: MatrixCallback<Unit>) { + override suspend fun enableEncryption(algorithm: String) { when { isEncrypted() -> { - callback.onFailure(IllegalStateException("Encryption is already enabled for this room")) + throw IllegalStateException("Encryption is already enabled for this room") } algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> { - callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")) + throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported") } else -> { val params = SendStateTask.Params( @@ -118,11 +118,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, "algorithm" to algorithm )) - sendStateTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + sendStateTask.execute(params) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index d49c2f12..28656463 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -17,27 +17,37 @@ package org.matrix.android.sdk.internal.session.room import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.internal.database.mapper.asDomain +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields +import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource +import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith +import org.matrix.android.sdk.internal.util.fetchCopied import javax.inject.Inject internal class DefaultRoomService @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, private val createRoomTask: CreateRoomTask, private val joinRoomTask: JoinRoomTask, private val markAllRoomsReadTask: MarkAllRoomsReadTask, @@ -118,4 +128,24 @@ internal class DefaultRoomService @Inject constructor( override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> { return roomChangeMembershipStateDataSource.getLiveStates() } + + override fun getRoomMember(userId: String, roomId: String): RoomMemberSummary? { + val roomMemberEntity = monarchy.fetchCopied { + RoomMemberHelper(it, roomId).getLastRoomMember(userId) + } + return roomMemberEntity?.asDomain() + } + + override fun getRoomMemberLive(userId: String, roomId: String): LiveData<Optional<RoomMemberSummary>> { + val liveData = monarchy.findAllMappedWithChanges( + { realm -> + RoomMemberHelper(realm, roomId).queryRoomMembersEvent() + .equalTo(RoomMemberSummaryEntityFields.USER_ID, userId) + }, + { it.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.firstOrNull().toOptional() + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt index 90ee99a9..99f9d364 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt @@ -26,6 +26,8 @@ import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import io.realm.Realm +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.query.where import javax.inject.Inject internal class RoomAvatarResolver @Inject constructor(@UserId private val userId: String) { @@ -46,11 +48,14 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId val roomMembers = RoomMemberHelper(realm, roomId) val members = roomMembers.queryActiveRoomMembersEvent().findAll() // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) - if (members.size == 1) { - res = members.firstOrNull()?.avatarUrl - } else if (members.size == 2) { - val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() - res = firstOtherMember?.avatarUrl + val isDirectRoom = RoomSummaryEntity.where(realm, roomId).findFirst()?.isDirect ?: false + if (isDirectRoom) { + if (members.size == 1) { + res = members.firstOrNull()?.avatarUrl + } else if (members.size == 2) { + val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() + res = firstOtherMember?.avatarUrl + } } return res } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt index 8b011980..58a119cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt @@ -17,6 +17,9 @@ package org.matrix.android.sdk.internal.session.room.alias import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.findByAlias @@ -24,8 +27,6 @@ import org.matrix.android.sdk.internal.di.SessionDatabase 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 io.realm.Realm -import org.greenrobot.eventbus.EventBus import javax.inject.Inject internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Optional<String>> { @@ -50,9 +51,11 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor( } else if (!params.searchOnServer) { Optional.from<String>(null) } else { - roomId = executeRequest<RoomAliasDescription>(eventBus) { - apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) - }.roomId + roomId = tryOrNull("## Failed to get roomId from alias") { + executeRequest<RoomAliasDescription>(eventBus) { + apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) + } + }?.roomId Optional.from(roomId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt index c30f11b9..13d403e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBody.kt @@ -74,8 +74,8 @@ internal data class CreateRoomBody( val invite3pids: List<ThreePidInviteBody>?, /** - * Extra keys to be added to the content of the m.room.create. - * The server will clobber the following keys: creator. + * Extra keys, such as m.federate, to be added to the content of the m.room.create event. + * The server will clobber the following keys: creator, room_version. * Future versions of the specification may allow the server to clobber other keys. */ @Json(name = "creation_content") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt index 632fcab7..79ff9db0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt @@ -81,7 +81,7 @@ internal class CreateRoomBodyBuilder @Inject constructor( topic = params.topic, invitedUserIds = params.invitedUserIds, invite3pids = invite3pids, - creationContent = params.creationContent, + creationContent = params.creationContent.takeIf { it.isNotEmpty() }, initialStates = initialStates, preset = params.preset, isDirect = params.isDirect, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 4f0aaf08..0fe9b0ba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -31,8 +31,10 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask @@ -45,6 +47,7 @@ internal interface CreateRoomTask : Task<CreateRoomParams, String> internal class DefaultCreateRoomTask @Inject constructor( private val roomAPI: RoomAPI, + @UserId private val userId: String, @SessionDatabase private val monarchy: Monarchy, private val directChatsHelper: DirectChatsHelper, private val updateUserAccountDataTask: UpdateUserAccountDataTask, @@ -61,6 +64,31 @@ internal class DefaultCreateRoomTask @Inject constructor( ?: throw IllegalStateException("You can't create a direct room without an invitedUser") } else null + if (params.preset == CreateRoomPreset.PRESET_PUBLIC_CHAT) { + if (params.roomAliasName.isNullOrEmpty()) { + throw CreateRoomFailure.RoomAliasError.AliasEmpty + } + // Check alias availability + val fullAlias = "#" + params.roomAliasName + ":" + userId.substringAfter(":") + try { + executeRequest<RoomAliasDescription>(eventBus) { + apiCall = roomAPI.getRoomIdByAlias(fullAlias) + } + } catch (throwable: Throwable) { + if (throwable is Failure.ServerError && throwable.httpCode == 404) { + // This is a 404, so the alias is available: nominal case + null + } else { + // Other error, propagate it + throw throwable + } + } + ?.let { + // Alias already exists: error case + throw CreateRoomFailure.RoomAliasError.AliasNotAvailable + } + } + val createRoomBody = createRoomBodyBuilder.build(params) val createRoomResponse = try { @@ -68,14 +96,18 @@ internal class DefaultCreateRoomTask @Inject constructor( apiCall = roomAPI.createRoom(createRoomBody) } } catch (throwable: Throwable) { - if (throwable is Failure.ServerError - && throwable.httpCode == 403 - && throwable.error.code == MatrixError.M_FORBIDDEN - && throwable.error.message.startsWith("Federation denied with")) { - throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error) - } else { - throw throwable + if (throwable is Failure.ServerError) { + if (throwable.httpCode == 403 + && throwable.error.code == MatrixError.M_FORBIDDEN + && throwable.error.message.startsWith("Federation denied with")) { + throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error) + } else if (throwable.httpCode == 400 + && throwable.error.code == MatrixError.M_UNKNOWN + && throwable.error.message == "Invalid characters in room alias") { + throw CreateRoomFailure.RoomAliasError.AliasInvalid + } } + throw throwable } val roomId = createRoomResponse.roomId // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt index 92e16a35..93fbfb4d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt @@ -19,18 +19,14 @@ package org.matrix.android.sdk.internal.session.room.draft import androidx.lifecycle.LiveData import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.session.room.send.DraftService import org.matrix.android.sdk.api.session.room.send.UserDraft -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.launchToCallback import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers internal class DefaultDraftService @AssistedInject constructor(@Assisted private val roomId: String, private val draftRepository: DraftRepository, - private val taskExecutor: TaskExecutor, private val coroutineDispatchers: MatrixCoroutineDispatchers ) : DraftService { @@ -43,14 +39,14 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private * The draft stack can contain several drafts. Depending of the draft to save, it will update the top draft, or create a new draft, * or even move an existing draft to the top of the list */ - override fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { + override suspend fun saveDraft(draft: UserDraft) { + withContext(coroutineDispatchers.main) { draftRepository.saveDraft(roomId, draft) } } - override fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { + override suspend fun deleteDraft() { + withContext(coroutineDispatchers.main) { draftRepository.deleteDraft(roomId) } } 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 7f3796c1..a7dfcfc9 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 @@ -93,6 +93,8 @@ internal class RoomDisplayNameResolver @Inject constructor( } } else if (roomEntity?.membership == Membership.JOIN) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() + val invitedCount = roomSummary?.invitedMembersCount ?: 0 + val joinedCount = roomSummary?.joinedMembersCount ?: 0 val otherMembersSubset: List<RoomMemberSummaryEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) { roomSummary.heroes.mapNotNull { userId -> roomMembers.getLastRoomMember(userId)?.takeIf { @@ -102,22 +104,49 @@ internal class RoomDisplayNameResolver @Inject constructor( } else { activeMembers.where() .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) - .limit(3) + .limit(5) .findAll() .createSnapshot() } val otherMembersCount = otherMembersSubset.count() name = when (otherMembersCount) { - 0 -> stringProvider.getString(R.string.room_displayname_empty_room) + 0 -> { + stringProvider.getString(R.string.room_displayname_empty_room) + // TODO (was xx and yyy) ... + } 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) - 2 -> stringProvider.getString(R.string.room_displayname_two_members, - resolveRoomMemberName(otherMembersSubset[0], roomMembers), - resolveRoomMemberName(otherMembersSubset[1], roomMembers) - ) - else -> stringProvider.getQuantityString(R.plurals.room_displayname_three_and_more_members, - roomMembers.getNumberOfJoinedMembers() - 1, - resolveRoomMemberName(otherMembersSubset[0], roomMembers), - roomMembers.getNumberOfJoinedMembers() - 1) + 2 -> { + stringProvider.getString(R.string.room_displayname_two_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers) + ) + } + 3 -> { + stringProvider.getString(R.string.room_displayname_3_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers) + ) + } + 4 -> { + stringProvider.getString(R.string.room_displayname_4_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers), + resolveRoomMemberName(otherMembersSubset[3], roomMembers) + ) + } + else -> { + val remainingCount = invitedCount + joinedCount - otherMembersCount + 1 + stringProvider.getQuantityString( + R.plurals.room_displayname_four_and_more_members, + remainingCount, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers), + remainingCount + ) + } } } return name ?: roomId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt index 7105a2cc..2a7c46bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.session.room.membership +import io.realm.Realm +import io.realm.RealmQuery import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity @@ -25,8 +27,6 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie 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 io.realm.Realm -import io.realm.RealmQuery /** * This class is an helper around STATE_ROOM_MEMBER events. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt index 8797b0c7..67ae55c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt @@ -21,21 +21,16 @@ import androidx.lifecycle.Transformations import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.model.PushRuleEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String, private val setRoomNotificationStateTask: SetRoomNotificationStateTask, - @SessionDatabase private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor) + @SessionDatabase private val monarchy: Monarchy) : RoomPushRuleService { @AssistedInject.Factory @@ -49,12 +44,8 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted } } - override fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable { - return setRoomNotificationStateTask - .configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) { - this.callback = matrixCallback - } - .executeBy(taskExecutor) + override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) { + setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) } private fun getPushRuleForRoom(): LiveData<RoomPushRule?> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt index 384c544e..cac87a9d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt @@ -18,14 +18,9 @@ package org.matrix.android.sdk.internal.session.room.reporting import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.reporting.ReportingService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String, - private val taskExecutor: TaskExecutor, private val reportContentTask: ReportContentTask ) : ReportingService { @@ -34,13 +29,8 @@ internal class DefaultReportingService @AssistedInject constructor(@Assisted pri fun create(roomId: String): ReportingService } - override fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable { + override suspend fun reportContent(eventId: String, score: Int, reason: String) { val params = ReportContentTask.Params(roomId, eventId, score, reason) - - return reportContentTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + reportContentTask.execute(params) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 65d375e1..3463b26c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -35,6 +35,7 @@ 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.awaitCallback internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String, private val stateEventDataSource: StateEventDataSource, @@ -132,23 +133,23 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable { return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg") - sendStateEvent( - eventType = EventType.STATE_ROOM_AVATAR, - body = mapOf("url" to response.contentUri), - callback = callback, - stateKey = null - ) + awaitCallback<Unit> { + sendStateEvent( + eventType = EventType.STATE_ROOM_AVATAR, + body = mapOf("url" to response.contentUri), + callback = it, + stateKey = null + ) + } } } override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - sendStateEvent( - eventType = EventType.STATE_ROOM_AVATAR, - body = emptyMap(), - callback = callback, - stateKey = null - ) - } + return sendStateEvent( + eventType = EventType.STATE_ROOM_AVATAR, + body = emptyMap(), + callback = callback, + stateKey = null + ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt index 932cb5d6..d6c02f0a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt @@ -18,15 +18,10 @@ package org.matrix.android.sdk.internal.session.room.tags import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.tags.TagsService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultTagsService @AssistedInject constructor( @Assisted private val roomId: String, - private val taskExecutor: TaskExecutor, private val addTagToRoomTask: AddTagToRoomTask, private val deleteTagFromRoomTask: DeleteTagFromRoomTask ) : TagsService { @@ -36,21 +31,13 @@ internal class DefaultTagsService @AssistedInject constructor( fun create(roomId: String): TagsService } - override fun addTag(tag: String, order: Double?, callback: MatrixCallback<Unit>): Cancelable { + override suspend fun addTag(tag: String, order: Double?) { val params = AddTagToRoomTask.Params(roomId, tag, order) - return addTagToRoomTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + addTagToRoomTask.execute(params) } - override fun deleteTag(tag: String, callback: MatrixCallback<Unit>): Cancelable { + override suspend fun deleteTag(tag: String) { val params = DeleteTagFromRoomTask.Params(roomId, tag) - return deleteTagFromRoomTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + deleteTagFromRoomTask.execute(params) } } 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 df2d238c..783aa53d 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 @@ -103,7 +103,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) .findAll() ?.mapNotNull { timelineEventMapper.map(it).takeIf { it.root.isImageMessage() || it.root.isVideoMessage() } } - ?: emptyList() + .orEmpty() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt index 2ba1eebe..8033b065 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt @@ -16,40 +16,31 @@ package org.matrix.android.sdk.internal.session.search -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.search.SearchService -import org.matrix.android.sdk.api.util.Cancelable import javax.inject.Inject -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultSearchService @Inject constructor( - private val taskExecutor: TaskExecutor, private val searchTask: SearchTask ) : SearchService { - override fun search(searchTerm: String, - roomId: String, - nextBatch: String?, - orderByRecent: Boolean, - limit: Int, - beforeLimit: Int, - afterLimit: Int, - includeProfile: Boolean, - callback: MatrixCallback<SearchResult>): Cancelable { - return searchTask - .configureWith(SearchTask.Params( - searchTerm = searchTerm, - roomId = roomId, - nextBatch = nextBatch, - orderByRecent = orderByRecent, - limit = limit, - beforeLimit = beforeLimit, - afterLimit = afterLimit, - includeProfile = includeProfile - )) { - this.callback = callback - }.executeBy(taskExecutor) + override suspend fun search(searchTerm: String, + roomId: String, + nextBatch: String?, + orderByRecent: Boolean, + limit: Int, + beforeLimit: Int, + afterLimit: Int, + includeProfile: Boolean): SearchResult { + return searchTask.execute(SearchTask.Params( + searchTerm = searchTerm, + roomId = roomId, + nextBatch = nextBatch, + orderByRecent = orderByRecent, + limit = limit, + beforeLimit = beforeLimit, + afterLimit = afterLimit, + includeProfile = includeProfile + )) } } 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 da28199f..fc476a3d 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 @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.internal.crypto.DefaultCryptoService 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.sync.model.SyncResponse @@ -39,6 +40,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: toDevice.events?.forEachIndexed { index, event -> initialSyncProgressService?.reportProgress(((index / total.toFloat()) * 100).toInt()) // Decrypt event if necessary + Timber.i("## CRYPTO | To device event from ${event.senderId} of type:${event.type}") decryptToDeviceEvent(event, null) if (event.getClearType() == EventType.MESSAGE && event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") { @@ -69,7 +71,12 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: result = cryptoService.decryptEvent(event, timelineId ?: "") } catch (exception: MXCryptoError) { event.mCryptoError = (exception as? MXCryptoError.Base)?.errorType // setCryptoError(exception.cryptoError) - Timber.e("## CRYPTO | Failed to decrypt to device event: ${event.mCryptoError ?: exception}") + val senderKey = event.content.toModel<OlmEventContent>()?.senderKey ?: "<unknown sender key>" + // try to find device id to ease log reading + val deviceId = cryptoService.getCryptoDeviceInfo(event.senderId!!).firstOrNull { + it.identityKey() == senderKey + }?.deviceId ?: senderKey + Timber.e("## CRYPTO | Failed to decrypt to device event from ${event.senderId}|$deviceId reason:<${event.mCryptoError ?: exception}>") } if (null != result) { @@ -80,6 +87,9 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain ) return true + } else { + // should not happen + Timber.e("## CRYPTO | ERROR NULL DECRYPTION RESULT from ${event.senderId}") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt index 1655e551..f4f3e6ce 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt @@ -28,7 +28,7 @@ internal class RoomTypingUsersHandler @Inject constructor(@UserId private val us fun handle(realm: Realm, roomId: String, ephemeralResult: RoomSyncHandler.EphemeralResult?) { val roomMemberHelper = RoomMemberHelper(realm, roomId) - val typingIds = ephemeralResult?.typingUserIds?.filter { it != userId } ?: emptyList() + val typingIds = ephemeralResult?.typingUserIds?.filter { it != userId }.orEmpty() val senderInfo = typingIds.map { userId -> val roomMemberSummaryEntity = roomMemberHelper.getLastRoomMember(userId) SenderInfo( 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 427a8896..77289f04 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 @@ -17,18 +17,21 @@ package org.matrix.android.sdk.internal.session.sync 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 import retrofit2.Call import retrofit2.http.GET -import retrofit2.http.Headers +import retrofit2.http.Header import retrofit2.http.QueryMap internal interface SyncAPI { - /** - * Set all the timeouts to 1 minute + * Set all the timeouts to 1 minute by default */ - @Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000") @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync") - fun sync(@QueryMap params: Map<String, String>): Call<SyncResponse> + fun sync(@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<SyncResponse> } 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 303bb454..b4fd6e73 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 @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.R import org.matrix.android.sdk.internal.di.UserId +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.session.filter.FilterRepository @@ -78,8 +79,13 @@ internal class DefaultSyncTask @Inject constructor( // Maybe refresh the home server capabilities data we know getHomeServerCapabilitiesTask.execute(Unit) + val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) + val syncResponse = executeRequest<SyncResponse>(eventBus) { - apiCall = syncAPI.sync(requestParams) + apiCall = syncAPI.sync( + params = requestParams, + readTimeOut = readTimeOut + ) } syncResponseHandler.handleResponse(syncResponse, token) if (isInitialSync) { @@ -87,4 +93,8 @@ internal class DefaultSyncTask @Inject constructor( } Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}") } + + companion object { + private const val TIMEOUT_MARGIN: Long = 10_000 + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/typing/DefaultTypingUsersTracker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/typing/DefaultTypingUsersTracker.kt index 2b7ff262..c5c3fc4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/typing/DefaultTypingUsersTracker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/typing/DefaultTypingUsersTracker.kt @@ -37,6 +37,6 @@ internal class DefaultTypingUsersTracker @Inject constructor() : TypingUsersTrac } override fun getTypingUsers(roomId: String): List<SenderInfo> { - return typingUsers[roomId] ?: emptyList() + return typingUsers[roomId].orEmpty() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/DefaultUserService.kt index d2eb7a14..17409569 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/DefaultUserService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/DefaultUserService.kt @@ -19,10 +19,13 @@ package org.matrix.android.sdk.internal.session.user import androidx.lifecycle.LiveData import androidx.paging.PagedList import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask import org.matrix.android.sdk.internal.session.user.accountdata.UpdateIgnoredUserIdsTask import org.matrix.android.sdk.internal.session.user.model.SearchUserTask import org.matrix.android.sdk.internal.task.TaskExecutor @@ -32,12 +35,40 @@ import javax.inject.Inject internal class DefaultUserService @Inject constructor(private val userDataSource: UserDataSource, private val searchUserTask: SearchUserTask, private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask, + private val getProfileInfoTask: GetProfileInfoTask, private val taskExecutor: TaskExecutor) : UserService { override fun getUser(userId: String): User? { return userDataSource.getUser(userId) } + override fun resolveUser(userId: String, callback: MatrixCallback<User>) { + val known = getUser(userId) + if (known != null) { + callback.onSuccess(known) + } else { + val params = GetProfileInfoTask.Params(userId) + getProfileInfoTask + .configureWith(params) { + this.callback = object : MatrixCallback<JsonDict> { + override fun onSuccess(data: JsonDict) { + callback.onSuccess( + User( + userId, + data[ProfileService.DISPLAY_NAME_KEY] as? String, + data[ProfileService.AVATAR_URL_KEY] as? String) + ) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + } + } + .executeBy(taskExecutor) + } + } + override fun getUserLive(userId: String): LiveData<Optional<User>> { return userDataSource.getUserLive(userId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserStore.kt index 5c8cbd08..c030872d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserStore.kt @@ -18,12 +18,15 @@ package org.matrix.android.sdk.internal.session.user import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.internal.database.model.UserEntity +import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.util.awaitTransaction import javax.inject.Inject internal interface UserStore { suspend fun createOrUpdate(userId: String, displayName: String? = null, avatarUrl: String? = null) + suspend fun updateAvatar(userId: String, avatarUrl: String? = null) + suspend fun updateDisplayName(userId: String, displayName: String? = null) } internal class RealmUserStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : UserStore { @@ -34,4 +37,20 @@ internal class RealmUserStore @Inject constructor(@SessionDatabase private val m it.insertOrUpdate(userEntity) } } + + override suspend fun updateAvatar(userId: String, avatarUrl: String?) { + monarchy.awaitTransaction { realm -> + UserEntity.where(realm, userId).findFirst()?.let { + it.avatarUrl = avatarUrl ?: "" + } + } + } + + override suspend fun updateDisplayName(userId: String, displayName: String?) { + monarchy.awaitTransaction { realm -> + UserEntity.where(realm, userId).findFirst()?.let { + it.displayName = displayName ?: "" + } + } + } } 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 22bdd2c6..329903f1 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 @@ -138,7 +138,7 @@ internal class WidgetManager @Inject constructor(private val integrationManager: ): LiveData<List<Widget>> { val widgetsAccountData = accountDataDataSource.getLiveAccountDataEvent(UserAccountDataTypes.TYPE_WIDGETS) return Transformations.map(widgetsAccountData) { - it.getOrNull()?.mapToWidgets(widgetTypes, excludedTypes) ?: emptyList() + it.getOrNull()?.mapToWidgets(widgetTypes, excludedTypes).orEmpty() } } 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 32997e20..ecfbe311 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 @@ -50,8 +50,6 @@ fun convertFromUTF8(s: String): String { } } -fun String.withoutPrefix(prefix: String) = if (startsWith(prefix)) substringAfter(prefix) else this - /** * Returns whether a string contains an occurrence of another, as a standalone word, regardless of case. * diff --git a/matrix-sdk-android/src/main/res/values-ca/strings.xml b/matrix-sdk-android/src/main/res/values-ca/strings.xml index 2dc2206c..8ba8c9ac 100644 --- a/matrix-sdk-android/src/main/res/values-ca/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ca/strings.xml @@ -1,79 +1,217 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?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">%1s ha sortit</string> - <string name="notice_room_join">%1s ha entrat</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 encriptat</string> - - <string name="notice_room_invite_no_invitee">la invitació de %s</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 us ha convidat</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 fet fora a %2$s</string> - - - <string name="notice_display_name_changed_from">%1$s ha canviat el seu nom visible de %2$s a %3$s</string> - <string name="notice_display_name_removed">%1$s ha eliminat el seu nom visible (%2$s)</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 contestat la trucada.</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 membres de la sala, des del punt en què són convidats.</string> - <string name="notice_room_visibility_shared">tots els membres de la sala.</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 l\'encriptació d\'extrem a extrem (%2$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 readmès a %2$s</string> - <string name="notice_room_ban">%1$s ha vetat a %2$s</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 permès a %2$s veure l\'historial que es generi a partir d\'ara</string> - <string name="notice_room_visibility_joined">tots els membres de la sala, des del punt en què hi entrin.</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">S\'ha finalitzat la conferència de veu IP</string> - - <string name="notice_avatar_changed_too">(s\'ha canviat també l\'avatar)</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 per a entrar a la sala</string> - <string name="notice_room_third_party_registered_invite">%1$s ha acceptat la invitació per a %2$s</string> - - <string name="notice_crypto_unable_to_decrypt">** No s\'ha pogut desencriptar: %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">S\'ha produït un error de xarxa</string> - <string name="matrix_error">S\'ha produït un error de Matrix</string> - - <string name="room_error_join_failed_empty_room">Actualment no es pot tornar a entrar a una sala buida.</string> - - <string name="notice_display_name_set">%1$s a canviat el seu nom visible a %2$s</string> - <string name="notice_placed_video_call">%s ha iniciat una trucada de vÃdeo.</string> - <string name="notice_placed_voice_call">%s ha iniciat una trucada de veu.</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">Convidat per %s</string> - <string name="room_displayname_room_invite">Convideu a la sala</string> + <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> - -</resources> + <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-es/strings.xml b/matrix-sdk-android/src/main/res/values-es/strings.xml index e2d09c78..3648ca3a 100644 --- a/matrix-sdk-android/src/main/res/values-es/strings.xml +++ b/matrix-sdk-android/src/main/res/values-es/strings.xml @@ -1,9 +1,7 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?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> @@ -30,61 +28,44 @@ <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 activó el cifrado de extremo a extremo (%2$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 cifrado</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> @@ -98,10 +79,8 @@ \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> @@ -111,9 +90,7 @@ <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: @@ -127,32 +104,25 @@ <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 habitación.</item> - <item quantity="other">%1$s ha quitado %2$s como alias de esta habitación.</item> + <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 habitación</string> - <string name="notice_room_created_by_you">Tu creaste la habitació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> @@ -167,8 +137,8 @@ <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 habitación</string> - <string name="notice_room_name_changed_by_you">Cambiaste el nombre de la habitación a: %1$s</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> @@ -176,40 +146,35 @@ <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">Activó el cifrado de un extremo a otro (%1$s)</string> - <string name="notice_room_update_by_you">Has mejorado esta habitación.</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 habitación</string> - <string name="notice_room_avatar_removed_by_you">Quitaste el avatar de la habitación</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">"nvitaste a %1$s. Razón: %2$s"</string> - <string name="notice_room_join_with_reason_by_you">Te uniste a la habitación. Razón: %1$s</string> - <string name="notice_room_leave_with_reason_by_you">Dejaste la habitació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> @@ -218,27 +183,42 @@ <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 agregó %2$s y eliminó %3$s como direcciones para esta sala."</string> + <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">Activó el cifrado de extremo a extremo.</string> - <string name="notice_end_to_end_unknown_algorithm_by_you">Activó el cifrado de un extremo a otro (algoritmo %1$s no reconocido).</string> - -</resources> + <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-fa/strings.xml b/matrix-sdk-android/src/main/res/values-fa/strings.xml index 042fda7d..11a786f5 100644 --- a/matrix-sdk-android/src/main/res/values-fa/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fa/strings.xml @@ -181,8 +181,8 @@ <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> + <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> diff --git a/matrix-sdk-android/src/main/res/values-hu/strings.xml b/matrix-sdk-android/src/main/res/values-hu/strings.xml index 4aade76c..49238ee8 100644 --- a/matrix-sdk-android/src/main/res/values-hu/strings.xml +++ b/matrix-sdk-android/src/main/res/values-hu/strings.xml @@ -22,10 +22,10 @@ <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 számára</string> - <string name="notice_room_visibility_invited">az összes szobatag, onnantól, hogy meg lettek hÃvva.</string> - <string name="notice_room_visibility_joined">az összes szobatag, onnantól, hogy csatlakoztak.</string> - <string name="notice_room_visibility_shared">az összes szobatag.</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> @@ -139,4 +139,40 @@ <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-it/strings_sas.xml b/matrix-sdk-android/src/main/res/values-it/strings_sas.xml new file mode 100644 index 00000000..b66c22bc --- /dev/null +++ b/matrix-sdk-android/src/main/res/values-it/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">Cane</string> + <string name="verification_emoji_cat">Gatto</string> + <string name="verification_emoji_lion">Leone</string> + <string name="verification_emoji_horse">Cavallo</string> + <string name="verification_emoji_unicorn">Unicorno</string> + <string name="verification_emoji_pig">Maiale</string> + <string name="verification_emoji_elephant">Elefante</string> + <string name="verification_emoji_rabbit">Coniglio</string> + <string name="verification_emoji_panda">Panda</string> + <string name="verification_emoji_rooster">Gallo</string> + <string name="verification_emoji_penguin">Pinguino</string> + <string name="verification_emoji_turtle">Tartaruga</string> + <string name="verification_emoji_fish">Pesce</string> + <string name="verification_emoji_octopus">Polpo</string> + <string name="verification_emoji_butterfly">Farfalla</string> + <string name="verification_emoji_flower">Fiore</string> + <string name="verification_emoji_tree">Albero</string> + <string name="verification_emoji_cactus">Cactus</string> + <string name="verification_emoji_mushroom">Fungo</string> + <string name="verification_emoji_globe">Globo</string> + <string name="verification_emoji_moon">Luna</string> + <string name="verification_emoji_cloud">Nuvola</string> + <string name="verification_emoji_fire">Fuoco</string> + <string name="verification_emoji_banana">Banana</string> + <string name="verification_emoji_apple">Mela</string> + <string name="verification_emoji_strawberry">Fragola</string> + <string name="verification_emoji_corn">Mais</string> + <string name="verification_emoji_pizza">Pizza</string> + <string name="verification_emoji_cake">Torta</string> + <string name="verification_emoji_heart">Cuore</string> + <string name="verification_emoji_smiley">Faccina sorridente</string> + <string name="verification_emoji_robot">Robot</string> + <string name="verification_emoji_hat">Cappello</string> + <string name="verification_emoji_glasses">Occhiali</string> + <string name="verification_emoji_spanner">Chiave inglese</string> + <string name="verification_emoji_santa">Babbo Natale</string> + <string name="verification_emoji_thumbs_up">Pollice alzato</string> + <string name="verification_emoji_umbrella">Ombrello</string> + <string name="verification_emoji_hourglass">Clessidra</string> + <string name="verification_emoji_clock">Orologio</string> + <string name="verification_emoji_gift">Regalo</string> + <string name="verification_emoji_light_bulb">Lampadina</string> + <string name="verification_emoji_book">Libro</string> + <string name="verification_emoji_pencil">Matita</string> + <string name="verification_emoji_paperclip">Graffetta</string> + <string name="verification_emoji_scissors">Forbici</string> + <string name="verification_emoji_lock">Lucchetto</string> + <string name="verification_emoji_key">Chiave</string> + <string name="verification_emoji_hammer">Martello</string> + <string name="verification_emoji_telephone">Telefono</string> + <string name="verification_emoji_flag">Bandiera</string> + <string name="verification_emoji_train">Treno</string> + <string name="verification_emoji_bicycle">Bicicletta</string> + <string name="verification_emoji_aeroplane">Aeroplano</string> + <string name="verification_emoji_rocket">Razzo</string> + <string name="verification_emoji_trophy">Trofeo</string> + <string name="verification_emoji_ball">Palla</string> + <string name="verification_emoji_guitar">Chitarra</string> + <string name="verification_emoji_trumpet">Trombetta</string> + <string name="verification_emoji_bell">Campana</string> + <string name="verification_emoji_anchor">Ancora</string> + <string name="verification_emoji_headphones">Cuffie</string> + <string name="verification_emoji_folder">Cartella</string> + <string name="verification_emoji_pin">Puntina</string> +</resources> diff --git a/matrix-sdk-android/src/main/res/values-ja/strings.xml b/matrix-sdk-android/src/main/res/values-ja/strings.xml index 366c7434..add19edf 100644 --- a/matrix-sdk-android/src/main/res/values-ja/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ja/strings.xml @@ -1,10 +1,8 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?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> @@ -29,11 +27,9 @@ <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> @@ -41,34 +37,50 @@ <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> - -</resources> + <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/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index 27f08326..f77cd320 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -72,6 +72,23 @@ <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> @@ -158,13 +175,22 @@ <!-- 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> -- GitLab