diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt index 95489da3fb..685a522f60 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.api.extensions import im.vector.matrix.android.api.comparators.DatedObjectComparators import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo -import java.util.Collections /* ========================================================================================== * MXDeviceInfo @@ -29,6 +28,6 @@ fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint() ?.chunked(4) ?.joinToString(separator = " ") -fun List.sortByLastSeen() { - Collections.sort(this, DatedObjectComparators.descComparator) +fun MutableList.sortByLastSeen() { + sortWith(DatedObjectComparators.descComparator) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt index a4b6d27535..fc02cf4a61 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixLinkify.kt @@ -30,9 +30,9 @@ object MatrixLinkify { * * @param spannable the text in which the matrix items has to be clickable. */ - fun addLinks(spannable: Spannable?, callback: MatrixPermalinkSpan.Callback?): Boolean { + fun addLinks(spannable: Spannable, callback: MatrixPermalinkSpan.Callback?): Boolean { // sanity checks - if (spannable.isNullOrEmpty()) { + if (spannable.isEmpty()) { return false } val text = spannable.toString() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt index 5cff69b2c1..1af77869ee 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.api.permalinks -import android.text.TextUtils import im.vector.matrix.android.api.session.events.model.Event /** @@ -48,7 +47,7 @@ object PermalinkFactory { * @return the permalink, or null in case of error */ fun createPermalink(id: String): String? { - return if (TextUtils.isEmpty(id)) { + return if (id.isEmpty()) { null } else MATRIX_TO_URL_BASE + escape(id) } @@ -71,11 +70,11 @@ object PermalinkFactory { * @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org" * @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink */ - fun getLinkedId(url: String?): String? { - val isSupported = url != null && url.startsWith(MATRIX_TO_URL_BASE) + fun getLinkedId(url: String): String? { + val isSupported = url.startsWith(MATRIX_TO_URL_BASE) return if (isSupported) { - url!!.substring(MATRIX_TO_URL_BASE.length) + url.substring(MATRIX_TO_URL_BASE.length) } else null } @@ -86,6 +85,6 @@ object PermalinkFactory { * @return the escaped id */ private fun escape(id: String): String { - return id.replace("/".toRegex(), "%2F") + return id.replace("/", "%2F") } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt index 17160aa5ec..166ec4f05f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/ContainsDisplayNameCondition.kt @@ -15,13 +15,11 @@ */ package im.vector.matrix.android.api.pushrules -import android.text.TextUtils import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.MessageContent import timber.log.Timber -import java.util.regex.Pattern class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) { @@ -34,7 +32,7 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) { } fun isSatisfied(event: Event, displayName: String): Boolean { - var message = when (event.type) { + val message = when (event.type) { EventType.MESSAGE -> { event.content.toModel() } @@ -59,20 +57,18 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) { */ fun caseInsensitiveFind(subString: String, longString: String): Boolean { // add sanity checks - if (TextUtils.isEmpty(subString) || TextUtils.isEmpty(longString)) { + if (subString.isEmpty() || longString.isEmpty()) { return false } - var res = false - try { - val pattern = Pattern.compile("(\\W|^)" + Pattern.quote(subString) + "(\\W|$)", Pattern.CASE_INSENSITIVE) - res = pattern.matcher(longString).find() + val regex = Regex("(\\W|^)" + Regex.escape(subString) + "(\\W|$)", RegexOption.IGNORE_CASE) + return regex.containsMatchIn(longString) } catch (e: Exception) { Timber.e(e, "## caseInsensitiveFind() : failed") } - return res + return false } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index c723009771..7aea73233d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.api.session.events.model -import android.text.TextUtils import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.session.crypto.MXCryptoError @@ -35,18 +34,16 @@ typealias Content = JsonDict * This methods is a facility method to map a json content to a model. */ inline fun Content?.toModel(catchError: Boolean = true): T? { - return this?.let { - val moshi = MoshiProvider.providesMoshi() - val moshiAdapter = moshi.adapter(T::class.java) - return try { - moshiAdapter.fromJsonValue(it) - } catch (e: Exception) { - if (catchError) { - Timber.e(e, "To model failed : $e") - null - } else { - throw e - } + val moshi = MoshiProvider.providesMoshi() + val moshiAdapter = moshi.adapter(T::class.java) + return try { + moshiAdapter.fromJsonValue(this) + } catch (e: Exception) { + if (catchError) { + Timber.e(e, "To model failed : $e") + null + } else { + throw e } } } @@ -55,12 +52,10 @@ inline fun Content?.toModel(catchError: Boolean = true): T? { * This methods is a facility method to map a model to a json Content */ @Suppress("UNCHECKED_CAST") -inline fun T?.toContent(): Content? { - return this?.let { - val moshi = MoshiProvider.providesMoshi() - val moshiAdapter = moshi.adapter(T::class.java) - return moshiAdapter.toJsonValue(it) as Content - } +inline fun T.toContent(): Content { + val moshi = MoshiProvider.providesMoshi() + val moshiAdapter = moshi.adapter(T::class.java) + return moshiAdapter.toJsonValue(this) as Content } /** @@ -106,7 +101,7 @@ data class Event( * @return true if this event is encrypted. */ fun isEncrypted(): Boolean { - return TextUtils.equals(type, EventType.ENCRYPTED) + return type == EventType.ENCRYPTED } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt index 196f18404c..27f7820156 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.api.session.room.model -import android.text.TextUtils import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.session.events.model.EventType -import java.util.* /** * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. @@ -45,14 +43,8 @@ data class PowerLevels( * @param userId the user id * @return the power level */ - fun getUserPowerLevel(userId: String): Int { - // sanity check - if (!TextUtils.isEmpty(userId)) { - val powerLevel = users[userId] - return powerLevel ?: usersDefault - } - - return usersDefault + fun getUserPowerLevel(userId: String): Int { + return users.getOrElse(userId) { usersDefault } } /** @@ -61,10 +53,8 @@ data class PowerLevels( * @param userId the user * @param powerLevel the new power level */ - fun setUserPowerLevel(userId: String?, powerLevel: Int) { - if (null != userId) { - users[userId] = Integer.valueOf(powerLevel) - } + fun setUserPowerLevel(userId: String, powerLevel: Int) { + users[userId] = powerLevel } /** @@ -74,8 +64,8 @@ data class PowerLevels( * @param userId the user id * @return true if the user can send the event */ - fun maySendEventOfType(eventTypeString: String, userId: String): Boolean { - return if (!TextUtils.isEmpty(eventTypeString) && !TextUtils.isEmpty(userId)) { + fun maySendEventOfType(eventTypeString: String, userId: String): Boolean { + return if (eventTypeString.isNotEmpty() && userId.isNotEmpty()) { getUserPowerLevel(userId) >= minimumPowerLevelForSendingEventAsMessage(eventTypeString) } else false } @@ -86,8 +76,8 @@ data class PowerLevels( * @param userId the user id * @return true if the user can send a room message */ - fun maySendMessage(userId: String): Boolean { - return maySendEventOfType(EventType.MESSAGE, userId) + fun maySendMessage(userId: String): Boolean { + return maySendEventOfType(EventType.MESSAGE, userId) } /** @@ -97,7 +87,7 @@ data class PowerLevels( * @param eventTypeString the type of event (in Event.EVENT_TYPE_XXX values) * @return the required minimum power level. */ - fun minimumPowerLevelForSendingEventAsMessage(eventTypeString: String?): Int { + fun minimumPowerLevelForSendingEventAsMessage(eventTypeString: String?): Int { return events[eventTypeString] ?: eventsDefault } @@ -108,7 +98,7 @@ data class PowerLevels( * @param eventTypeString the type of event (in Event.EVENT_TYPE_STATE_ values). * @return the required minimum power level. */ - fun minimumPowerLevelForSendingEventAsStateEvent(eventTypeString: String?): Int { + fun minimumPowerLevelForSendingEventAsStateEvent(eventTypeString: String?): Int { return events[eventTypeString] ?: stateDefault } @@ -118,18 +108,14 @@ data class PowerLevels( * @param key the notification key * @return the level */ - fun notificationLevel(key: String?): Int { - if (null != key && notifications.containsKey(key)) { - val valAsVoid = notifications[key] + fun notificationLevel(key: String): Int { + val valAsVoid = notifications[key] ?: return 50 - // the first implementation was a string value - return if (valAsVoid is String) { - Integer.parseInt(valAsVoid) - } else { - valAsVoid as Int - } + // the first implementation was a string value + return if (valAsVoid is String) { + valAsVoid.toInt() + } else { + valAsVoid as Int } - - return 50 } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index 35109501e4..598aab2d30 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -145,15 +145,7 @@ class CreateRoomParams { */ fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?) { // Remove the existing value if any. - if (initialStates != null && !initialStates!!.isEmpty()) { - val newInitialStates = ArrayList() - for (event in initialStates!!) { - if (event.getClearType() != EventType.STATE_HISTORY_VISIBILITY) { - newInitialStates.add(event) - } - } - initialStates = newInitialStates - } + initialStates?.removeAll { it.getClearType() == EventType.STATE_HISTORY_VISIBILITY } if (historyVisibility != null) { val contentMap = HashMap() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index 599e5fef81..bb629fd881 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -359,29 +359,16 @@ internal class DefaultCryptoService @Inject constructor( */ override fun setDevicesKnown(devices: List, callback: MatrixCallback?) { // build a devices map - val devicesIdListByUserId = HashMap>() + val devicesIdListByUserId = devices.groupBy({ it.userId }, { it.deviceId }) - for (di in devices) { - var deviceIdsList: MutableList? = devicesIdListByUserId[di.userId]?.toMutableList() - - if (null == deviceIdsList) { - deviceIdsList = ArrayList() - devicesIdListByUserId[di.userId] = deviceIdsList - } - deviceIdsList.add(di.deviceId) - } - - val userIds = devicesIdListByUserId.keys - - for (userId in userIds) { + for ((userId, deviceIds) in devicesIdListByUserId) { val storedDeviceIDs = cryptoStore.getUserDevices(userId) // sanity checks if (null != storedDeviceIDs) { var isUpdated = false - val deviceIds = devicesIdListByUserId[userId] - deviceIds?.forEach { deviceId -> + deviceIds.forEach { deviceId -> val device = storedDeviceIDs[deviceId] // assume if the device is either verified or blocked @@ -549,16 +536,10 @@ internal class DefaultCryptoService @Inject constructor( val t0 = System.currentTimeMillis() Timber.v("## encryptEventContent() starts") runCatching { - safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) - } - .fold( - { - Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms") - callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED)) - }, - { callback.onFailure(it) } - - ) + val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms") + MXEncryptEventContentResult(content, EventType.ENCRYPTED) + }.foldToCallback(callback) } else { val algorithm = getEncryptionAlgorithm(roomId) val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, @@ -776,7 +757,7 @@ internal class DefaultCryptoService @Inject constructor( GlobalScope.launch(coroutineDispatchers.main) { runCatching { exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT) - }.fold(callback::onSuccess, callback::onFailure) + }.foldToCallback(callback) } } @@ -812,8 +793,8 @@ internal class DefaultCryptoService @Inject constructor( progressListener: ProgressListener?, callback: MatrixCallback) { GlobalScope.launch(coroutineDispatchers.main) { - withContext(coroutineDispatchers.crypto) { - Try { + runCatching { + withContext(coroutineDispatchers.crypto) { Timber.v("## importRoomKeys starts") val t0 = System.currentTimeMillis() @@ -860,19 +841,14 @@ internal class DefaultCryptoService @Inject constructor( fun checkUnknownDevices(userIds: List, callback: MatrixCallback) { // force the refresh to ensure that the devices list is up-to-date GlobalScope.launch(coroutineDispatchers.crypto) { - runCatching { deviceListManager.downloadKeys(userIds, true) } - .fold( - { - val unknownDevices = getUnknownDevices(it) - if (unknownDevices.map.isEmpty()) { - callback.onSuccess(Unit) - } else { - // trigger an an unknown devices exception - callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices))) - } - }, - { callback.onFailure(it) } - ) + runCatching { + val keys = deviceListManager.downloadKeys(userIds, true) + val unknownDevices = getUnknownDevices(keys) + if (unknownDevices.map.isNotEmpty()) { + // trigger an an unknown devices exception + throw Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices)) + } + }.foldToCallback(callback) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt index be72851d97..7f2a23e4c2 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo @@ -27,7 +26,6 @@ import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.SyncTokenStore import timber.log.Timber -import java.util.* import javax.inject.Inject // Legacy name: MXDeviceList @@ -39,13 +37,12 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM private val downloadKeysForUsersTask: DownloadKeysForUsersTask) { // HS not ready for retry - private val notReadyToRetryHS = HashSet() + private val notReadyToRetryHS = mutableSetOf() init { var isUpdated = false val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap() - for (userId in deviceTrackingStatuses.keys) { - val status = deviceTrackingStatuses[userId]!! + for ((userId, status) in deviceTrackingStatuses) { if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) { // if a download was in progress when we got shut down, it isn't any more. deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD @@ -66,7 +63,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM private fun canRetryKeysDownload(userId: String): Boolean { var res = false - if (!TextUtils.isEmpty(userId) && userId.contains(":")) { + if (':' in userId) { try { synchronized(notReadyToRetryHS) { res = !notReadyToRetryHS.contains(userId.substring(userId.lastIndexOf(":") + 1)) @@ -119,27 +116,23 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param changed the user ids list which have new devices * @param left the user ids list which left a room */ - fun handleDeviceListsChanges(changed: List?, left: List?) { + fun handleDeviceListsChanges(changed: Collection, left: Collection) { var isUpdated = false val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap() - if (changed?.isNotEmpty() == true) { - for (userId in changed) { - if (deviceTrackingStatuses.containsKey(userId)) { - Timber.v("## invalidateUserDeviceList() : Marking device list outdated for $userId") - deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD - isUpdated = true - } + for (userId in changed) { + if (deviceTrackingStatuses.containsKey(userId)) { + Timber.v("## invalidateUserDeviceList() : Marking device list outdated for $userId") + deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD + isUpdated = true } } - if (left?.isNotEmpty() == true) { - for (userId in left) { - if (deviceTrackingStatuses.containsKey(userId)) { - Timber.v("## invalidateUserDeviceList() : No longer tracking device list for $userId") - deviceTrackingStatuses[userId] = TRACKING_STATUS_NOT_TRACKED - isUpdated = true - } + for (userId in left) { + if (deviceTrackingStatuses.containsKey(userId)) { + Timber.v("## invalidateUserDeviceList() : No longer tracking device list for $userId") + deviceTrackingStatuses[userId] = TRACKING_STATUS_NOT_TRACKED + isUpdated = true } } @@ -153,7 +146,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * + update */ fun invalidateAllDeviceLists() { - handleDeviceListsChanges(ArrayList(cryptoStore.getDeviceTrackingStatuses().keys), null) + handleDeviceListsChanges(cryptoStore.getDeviceTrackingStatuses().keys, emptyList()) } /** @@ -163,9 +156,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM */ private fun onKeysDownloadFailed(userIds: List) { val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap() - for (userId in userIds) { - deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD - } + userIds.associateWithTo(deviceTrackingStatuses) { TRACKING_STATUS_PENDING_DOWNLOAD } cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) } @@ -177,21 +168,15 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM */ private fun onKeysDownloadSucceed(userIds: List, failures: Map>?): MXUsersDevicesMap { if (failures != null) { - val keys = failures.keys - for (k in keys) { - val value = failures[k] - if (value!!.containsKey("status")) { - val statusCodeAsVoid = value["status"] - var statusCode = 0 - if (statusCodeAsVoid is Double) { - statusCode = statusCodeAsVoid.toInt() - } else if (statusCodeAsVoid is Int) { - statusCode = statusCodeAsVoid.toInt() - } - if (statusCode == 503) { - synchronized(notReadyToRetryHS) { - notReadyToRetryHS.add(k) - } + for ((k, value) in failures) { + val statusCode = when (val status = value["status"]) { + is Double -> status.toInt() + is Int -> status.toInt() + else -> 0 + } + if (statusCode == 503) { + synchronized(notReadyToRetryHS) { + notReadyToRetryHS.add(k) } } } @@ -228,11 +213,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM /** * Download the device keys for a list of users and stores the keys in the MXStore. * It must be called in getEncryptingThreadHandler() thread. - * The callback is called in the UI thread. * * @param userIds The users to fetch. * @param forceDownload Always download the keys even if cached. - * @param callback the asynchronous callback */ suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap { Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds") @@ -270,7 +253,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM Timber.v("## downloadKeys() : starts") val t0 = System.currentTimeMillis() val result = doKeyDownloadForUsers(downloadUsers) - Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") + Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms") result.also { it.addEntriesFromMap(stored) } @@ -303,16 +286,14 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM val devices = response.deviceKeys?.get(userId) Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") if (devices != null) { - val mutableDevices = HashMap(devices) - val deviceIds = ArrayList(mutableDevices.keys) - for (deviceId in deviceIds) { + val mutableDevices = devices.toMutableMap() + for ((deviceId, deviceInfo) in devices) { // Get the potential previously store device keys for this device val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId) - val deviceInfo = mutableDevices[deviceId] // in some race conditions (like unit tests) // the self device must be seen as verified - if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) { + if (deviceInfo.deviceId == credentials.deviceId && userId == credentials.userId) { deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED } // Validate received keys @@ -365,13 +346,13 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } // Check that the user_id and device_id in the received deviceKeys are correct - if (!TextUtils.equals(deviceKeys.userId, userId)) { - Timber.e("## validateDeviceKeys() : Mismatched user_id " + deviceKeys.userId + " from " + userId + ":" + deviceId) + if (deviceKeys.userId != userId) { + Timber.e("## validateDeviceKeys() : Mismatched user_id ${deviceKeys.userId} from $userId:$deviceId") return false } - if (!TextUtils.equals(deviceKeys.deviceId, deviceId)) { - Timber.e("## validateDeviceKeys() : Mismatched device_id " + deviceKeys.deviceId + " from " + userId + ":" + deviceId) + if (deviceKeys.deviceId != deviceId) { + Timber.e("## validateDeviceKeys() : Mismatched device_id ${deviceKeys.deviceId} from $userId:$deviceId") return false } @@ -379,21 +360,21 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM val signKey = deviceKeys.keys?.get(signKeyId) if (null == signKey) { - Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " has no ed25519 key") + Timber.e("## validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key") return false } val signatureMap = deviceKeys.signatures?.get(userId) if (null == signatureMap) { - Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " has no map for " + userId) + Timber.e("## validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId") return false } val signature = signatureMap[signKeyId] if (null == signature) { - Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " is not signed") + Timber.e("## validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} is not signed") return false } @@ -414,7 +395,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } if (null != previouslyStoredDeviceKeys) { - if (!TextUtils.equals(previouslyStoredDeviceKeys.fingerprint(), signKey)) { + if (previouslyStoredDeviceKeys.fingerprint() != signKey) { // This should only happen if the list has been MITMed; we are // best off sticking with the original keys. // @@ -424,7 +405,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) Timber.e("## validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys") - Timber.e("## validateDeviceKeys() : " + previouslyStoredDeviceKeys.keys + " -> " + deviceKeys.keys) + Timber.e("## validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}") return false } @@ -438,27 +419,18 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * This method must be called on getEncryptingThreadHandler() thread. */ suspend fun refreshOutdatedDeviceLists() { - val users = ArrayList() - val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap() - for (userId in deviceTrackingStatuses.keys) { - if (TRACKING_STATUS_PENDING_DOWNLOAD == deviceTrackingStatuses[userId]) { - users.add(userId) - } + val users = deviceTrackingStatuses.keys.filterTo(mutableListOf()) { userId -> + TRACKING_STATUS_PENDING_DOWNLOAD == deviceTrackingStatuses[userId] } - if (users.size == 0) { + if (users.isEmpty()) { return } // update the statuses - for (userId in users) { - val status = deviceTrackingStatuses[userId] - if (null != status && TRACKING_STATUS_PENDING_DOWNLOAD == status) { - deviceTrackingStatuses.put(userId, TRACKING_STATUS_DOWNLOAD_IN_PROGRESS) - } - } + users.associateWithTo(deviceTrackingStatuses) { TRACKING_STATUS_DOWNLOAD_IN_PROGRESS } cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) runCatching { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index 10a2cb00be..3c8d70f2f1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener import im.vector.matrix.android.api.session.events.model.Event @@ -25,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber -import java.util.* import javax.inject.Inject import kotlin.collections.ArrayList @@ -58,7 +56,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( when (roomKeyShare?.action) { RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event)) - else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare?.action) + else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action ${roomKeyShare?.action}") } } @@ -68,7 +66,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( * It must be called on CryptoThread */ fun processReceivedRoomKeyRequests() { - val roomKeyRequestsToProcess = ArrayList(receivedRoomKeyRequests) + val roomKeyRequestsToProcess = receivedRoomKeyRequests.toList() receivedRoomKeyRequests.clear() for (request in roomKeyRequestsToProcess) { val userId = request.userId @@ -77,7 +75,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( val roomId = body!!.roomId val alg = body.algorithm - Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId) + Timber.v("m.room_key_request from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}") if (userId == null || credentials.userId != userId) { // TODO: determine if we sent this device the keys already: in Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now") @@ -92,12 +90,12 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( continue } if (!decryptor.hasKeysForKeyRequest(request)) { - Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!) + Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session ${body.sessionId!!}") cryptoStore.deleteIncomingRoomKeyRequest(request) continue } - if (TextUtils.equals(deviceId, credentials.deviceId) && TextUtils.equals(credentials.userId, userId)) { + if (credentials.deviceId == deviceId && credentials.userId == userId) { Timber.v("## processReceivedRoomKeyRequests() : oneself device - ignored") cryptoStore.deleteIncomingRoomKeyRequest(request) continue @@ -132,7 +130,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( var receivedRoomKeyRequestCancellations: List? = null synchronized(this.receivedRoomKeyRequestCancellations) { - if (!this.receivedRoomKeyRequestCancellations.isEmpty()) { + if (this.receivedRoomKeyRequestCancellations.isNotEmpty()) { receivedRoomKeyRequestCancellations = this.receivedRoomKeyRequestCancellations.toList() this.receivedRoomKeyRequestCancellations.clear() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt index b9cb59a0f5..0aa7a15ce2 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt @@ -16,20 +16,19 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import android.util.Base64 import im.vector.matrix.android.internal.extensions.toUnsignedInt import timber.log.Timber import java.io.ByteArrayOutputStream import java.nio.charset.Charset import java.security.SecureRandom -import java.util.* import javax.crypto.Cipher import javax.crypto.Mac import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec import kotlin.experimental.and import kotlin.experimental.xor +import kotlin.math.min /** * Utility class to import/export the crypto data @@ -51,7 +50,7 @@ object MXMegolmExportEncryption { * @return the AES key */ private fun getAesKey(keyBits: ByteArray): ByteArray { - return Arrays.copyOfRange(keyBits, 0, 32) + return keyBits.copyOfRange(0, 32) } /** @@ -61,7 +60,7 @@ object MXMegolmExportEncryption { * @return the Hmac key. */ private fun getHmacKey(keyBits: ByteArray): ByteArray { - return Arrays.copyOfRange(keyBits, 32, keyBits.size) + return keyBits.copyOfRange(32, keyBits.size) } /** @@ -77,7 +76,7 @@ object MXMegolmExportEncryption { val body = unpackMegolmKeyFile(data) // check we have a version byte - if (null == body || body.size == 0) { + if (null == body || body.isEmpty()) { Timber.e("## decryptMegolmKeyFile() : Invalid file: too short") throw Exception("Invalid file: too short") } @@ -93,27 +92,27 @@ object MXMegolmExportEncryption { throw Exception("Invalid file: too short") } - if (TextUtils.isEmpty(password)) { + if (password.isEmpty()) { throw Exception("Empty password is not supported") } - val salt = Arrays.copyOfRange(body, 1, 1 + 16) - val iv = Arrays.copyOfRange(body, 17, 17 + 16) + val salt = body.copyOfRange(1, 1 + 16) + val iv = body.copyOfRange(17, 17 + 16) val iterations = (body[33].toUnsignedInt() shl 24) or (body[34].toUnsignedInt() shl 16) or (body[35].toUnsignedInt() shl 8) or body[36].toUnsignedInt() - val ciphertext = Arrays.copyOfRange(body, 37, 37 + ciphertextLength) - val hmac = Arrays.copyOfRange(body, body.size - 32, body.size) + val ciphertext = body.copyOfRange(37, 37 + ciphertextLength) + val hmac = body.copyOfRange(body.size - 32, body.size) val deriveKey = deriveKeys(salt, iterations, password) - val toVerify = Arrays.copyOfRange(body, 0, body.size - 32) + val toVerify = body.copyOfRange(0, body.size - 32) val macKey = SecretKeySpec(getHmacKey(deriveKey), "HmacSHA256") val mac = Mac.getInstance("HmacSHA256") mac.init(macKey) val digest = mac.doFinal(toVerify) - if (!Arrays.equals(hmac, digest)) { + if (!hmac.contentEquals(digest)) { Timber.e("## decryptMegolmKeyFile() : Authentication check failed: incorrect password?") throw Exception("Authentication check failed: incorrect password?") } @@ -146,7 +145,7 @@ object MXMegolmExportEncryption { @Throws(Exception::class) @JvmOverloads fun encryptMegolmKeyFile(data: String, password: String, kdf_rounds: Int = DEFAULT_ITERATION_COUNT): ByteArray { - if (TextUtils.isEmpty(password)) { + if (password.isEmpty()) { throw Exception("Empty password is not supported") } @@ -196,7 +195,7 @@ object MXMegolmExportEncryption { System.arraycopy(cipherArray, 0, resultBuffer, idx, cipherArray.size) idx += cipherArray.size - val toSign = Arrays.copyOfRange(resultBuffer, 0, idx) + val toSign = resultBuffer.copyOfRange(0, idx) val macKey = SecretKeySpec(getHmacKey(deriveKey), "HmacSHA256") val mac = Mac.getInstance("HmacSHA256") @@ -234,7 +233,7 @@ object MXMegolmExportEncryption { // start the next line after the newline lineStart = lineEnd + 1 - if (TextUtils.equals(line, HEADER_LINE)) { + if (line == HEADER_LINE) { break } } @@ -244,15 +243,13 @@ object MXMegolmExportEncryption { // look for the end line while (true) { val lineEnd = fileStr.indexOf('\n', lineStart) - val line: String - - if (lineEnd < 0) { - line = fileStr.substring(lineStart).trim() + val line = if (lineEnd < 0) { + fileStr.substring(lineStart) } else { - line = fileStr.substring(lineStart, lineEnd).trim() - } + fileStr.substring(lineStart, lineEnd) + }.trim() - if (TextUtils.equals(line, TRAILER_LINE)) { + if (line == TRAILER_LINE) { break } @@ -290,7 +287,7 @@ object MXMegolmExportEncryption { for (i in 1..nLines) { outStream.write("\n".toByteArray()) - val len = Math.min(LINE_LENGTH, data.size - o) + val len = min(LINE_LENGTH, data.size - o) outStream.write(Base64.encode(data, o, len, Base64.DEFAULT)) o += LINE_LENGTH } @@ -318,7 +315,7 @@ object MXMegolmExportEncryption { // it is simpler than the generic algorithm because the expected key length is equal to the mac key length. // noticed as dklen/hlen val prf = Mac.getInstance("HmacSHA512") - prf.init(SecretKeySpec(password.toByteArray(charset("UTF-8")), "HmacSHA512")) + prf.init(SecretKeySpec(password.toByteArray(Charsets.UTF_8), "HmacSHA512")) // 512 bits key length val key = ByteArray(64) @@ -326,8 +323,7 @@ object MXMegolmExportEncryption { // U1 = PRF(Password, Salt || INT_32_BE(i)) prf.update(salt) - val int32BE = ByteArray(4) - Arrays.fill(int32BE, 0.toByte()) + val int32BE = ByteArray(4) { 0.toByte() } int32BE[3] = 1.toByte() prf.update(int32BE) prf.doFinal(Uc, 0) @@ -346,7 +342,7 @@ object MXMegolmExportEncryption { } } - Timber.v("## deriveKeys() : " + iterations + " in " + (System.currentTimeMillis() - t0) + " ms") + Timber.v("## deriveKeys() : $iterations in ${System.currentTimeMillis() - t0} ms") return key } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 9655806b2d..68aaaf3831 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.JsonDict @@ -33,7 +32,6 @@ import im.vector.matrix.android.internal.util.convertToUTF8 import org.matrix.olm.* import timber.log.Timber import java.net.URLEncoder -import java.util.* import javax.inject.Inject // The libolm wrapper. @@ -434,7 +432,7 @@ internal class MXOlmDevice @Inject constructor( * @return the base64-encoded secret key. */ fun getSessionKey(sessionId: String): String? { - if (!TextUtils.isEmpty(sessionId)) { + if (sessionId.isNotEmpty()) { try { return outboundGroupSessionStore[sessionId]!!.sessionKey() } catch (e: Exception) { @@ -451,7 +449,7 @@ internal class MXOlmDevice @Inject constructor( * @return the current chain index. */ fun getMessageIndex(sessionId: String): Int { - return if (!TextUtils.isEmpty(sessionId)) { + return if (sessionId.isNotEmpty()) { outboundGroupSessionStore[sessionId]!!.messageIndex() } else 0 } @@ -464,7 +462,7 @@ internal class MXOlmDevice @Inject constructor( * @return ciphertext */ fun encryptGroupMessage(sessionId: String, payloadString: String): String? { - if (!TextUtils.isEmpty(sessionId) && !TextUtils.isEmpty(payloadString)) { + if (sessionId.isNotEmpty() && payloadString.isNotEmpty()) { try { return outboundGroupSessionStore[sessionId]!!.encryptMessage(payloadString) } catch (e: Exception) { @@ -523,7 +521,7 @@ internal class MXOlmDevice @Inject constructor( } try { - if (!TextUtils.equals(session.olmInboundGroupSession!!.sessionIdentifier(), sessionId)) { + if (session.olmInboundGroupSession!!.sessionIdentifier() != sessionId) { Timber.e("## addInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey") session.olmInboundGroupSession!!.releaseSession() return false @@ -573,7 +571,7 @@ internal class MXOlmDevice @Inject constructor( } try { - if (!TextUtils.equals(session.olmInboundGroupSession?.sessionIdentifier(), sessionId)) { + if (session.olmInboundGroupSession?.sessionIdentifier() != sessionId) { Timber.e("## importInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey") if (session.olmInboundGroupSession != null) session.olmInboundGroupSession!!.releaseSession() continue @@ -758,7 +756,7 @@ internal class MXOlmDevice @Inject constructor( if (session != null) { // Check that the room id matches the original one for the session. This stops // the HS pretending a message was targeting a different room. - if (!TextUtils.equals(roomId, session.roomId)) { + if (roomId != session.roomId) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt index b64904190b..f93245de12 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.session.SessionScope -import java.util.* import javax.inject.Inject @SessionScope @@ -42,11 +40,11 @@ internal class MyDeviceInfoHolder @Inject constructor( init { val keys = HashMap() - if (!TextUtils.isEmpty(olmDevice.deviceEd25519Key)) { + if (!olmDevice.deviceEd25519Key.isNullOrEmpty()) { keys["ed25519:" + credentials.deviceId] = olmDevice.deviceEd25519Key!! } - if (!TextUtils.isEmpty(olmDevice.deviceCurve25519Key)) { + if (!olmDevice.deviceCurve25519Key.isNullOrEmpty()) { keys["curve25519:" + credentials.deviceId] = olmDevice.deviceCurve25519Key!! } @@ -58,13 +56,7 @@ internal class MyDeviceInfoHolder @Inject constructor( // Add our own deviceinfo to the store val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId) - val myDevices: MutableMap - - if (null != endToEndDevicesForUser) { - myDevices = HashMap(endToEndDevicesForUser) - } else { - myDevices = HashMap() - } + val myDevices = endToEndDevicesForUser.orEmpty().toMutableMap() myDevices[myDevice.deviceId] = myDevice diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index c929c33666..e6b57d149f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -24,8 +24,9 @@ import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.JsonCanonicalizer import org.matrix.olm.OlmAccount import timber.log.Timber -import java.util.* import javax.inject.Inject +import kotlin.math.floor +import kotlin.math.min @SessionScope internal class OneTimeKeysUploader @Inject constructor( @@ -77,7 +78,7 @@ internal class OneTimeKeysUploader @Inject constructor( // If we run out of slots when generating new keys then olm will // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. - val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt() + val keyLimit = floor(maxOneTimeKeys / 2.0).toInt() if (oneTimeKeyCount != null) { uploadOTK(oneTimeKeyCount!!, keyLimit) } else { @@ -116,7 +117,7 @@ internal class OneTimeKeysUploader @Inject constructor( // If we don't need to generate any more keys then we are done. return } - val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) + val keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) olmDevice.generateOneTimeKeys(keysThisLoop) val response = uploadOneTimeKeys() if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { @@ -132,14 +133,14 @@ internal class OneTimeKeysUploader @Inject constructor( */ private suspend fun uploadOneTimeKeys(): KeysUploadResponse { val oneTimeKeys = olmDevice.getOneTimeKeys() - val oneTimeJson = HashMap() + val oneTimeJson = mutableMapOf() val curve25519Map = oneTimeKeys?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY) if (null != curve25519Map) { - for (key_id in curve25519Map.keys) { - val k = HashMap() - k["key"] = curve25519Map.getValue(key_id) + for ((key_id, value) in curve25519Map) { + val k = mutableMapOf() + k["key"] = value // the key is also signed val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, k) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt index 9c542cd446..a17a524923 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt @@ -16,13 +16,11 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber -import java.util.* import javax.inject.Inject @SessionScope @@ -62,10 +60,8 @@ internal class RoomDecryptorProvider @Inject constructor( } if (roomId != null && roomId.isNotEmpty()) { synchronized(roomDecryptors) { - if (!roomDecryptors.containsKey(roomId)) { - roomDecryptors[roomId] = HashMap() - } - val alg = roomDecryptors[roomId]?.get(algorithm) + val decryptors = roomDecryptors.getOrPut(roomId) { mutableMapOf() } + val alg = decryptors[algorithm] if (alg != null) { return alg } @@ -89,7 +85,7 @@ internal class RoomDecryptorProvider @Inject constructor( } else -> olmDecryptionFactory.create() } - if (roomId != null && !TextUtils.isEmpty(roomId)) { + if (!roomId.isNullOrEmpty()) { synchronized(roomDecryptors) { roomDecryptors[roomId]?.put(algorithm, alg) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 6206072156..0283d3c85b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.actions -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXKey @@ -24,7 +23,6 @@ import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask import timber.log.Timber -import java.util.* import javax.inject.Inject internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val olmDevice: MXOlmDevice, @@ -35,18 +33,14 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val val results = MXUsersDevicesMap() - val userIds = devicesByUser.keys - - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - - for (deviceInfo in deviceInfos!!) { + for ((userId, deviceInfos) in devicesByUser) { + for (deviceInfo in deviceInfos) { val deviceId = deviceInfo.deviceId val key = deviceInfo.identityKey() val sessionId = olmDevice.getSessionId(key!!) - if (TextUtils.isEmpty(sessionId)) { + if (sessionId.isNullOrEmpty()) { devicesWithoutSession.add(deviceInfo) } @@ -79,9 +73,8 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - for (deviceInfo in deviceInfos!!) { + for ((userId, deviceInfos) in devicesByUser) { + for (deviceInfo in deviceInfos) { var oneTimeKey: MXKey? = null val deviceIds = oneTimeKeys.getUserDeviceIds(userId) if (null != deviceIds) { @@ -116,24 +109,22 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val val signKeyId = "ed25519:$deviceId" val signature = oneTimeKey.signatureForUserId(userId, signKeyId) - if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) { + if (!signature.isNullOrEmpty() && !deviceInfo.fingerprint().isNullOrEmpty()) { var isVerified = false var errorMessage: String? = null - if (signature != null) { - try { - olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) - isVerified = true - } catch (e: Exception) { - errorMessage = e.message - } + try { + olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) + isVerified = true + } catch (e: Exception) { + errorMessage = e.message } // Check one-time key signature if (isVerified) { sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) - if (!TextUtils.isEmpty(sessionId)) { + if (!sessionId.isNullOrEmpty()) { Timber.v("## verifyKeyAndStartSession() : Started new sessionid " + sessionId + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") } else { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index 4ed1ed75f6..3fd833dfb4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -16,14 +16,11 @@ package im.vector.matrix.android.internal.crypto.actions -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import timber.log.Timber -import java.util.* import javax.inject.Inject internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val olmDevice: MXOlmDevice, @@ -36,27 +33,14 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o */ suspend fun handle(users: List): MXUsersDevicesMap { Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") - val devicesByUser = HashMap>() - - for (userId in users) { - devicesByUser[userId] = ArrayList() - + val devicesByUser = users.associateWith { userId -> val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() - for (device in devices) { - val key = device.identityKey() - - if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { - // Don't bother setting up session to ourself - continue - } - - if (device.isVerified) { - // Don't bother setting up sessions with blocked users - continue - } - - devicesByUser[userId]!!.add(device) + devices.filter { + // Don't bother setting up session to ourself + it.identityKey() != olmDevice.deviceCurve25519Key && + // Don't bother setting up sessions with blocked users + !it.isVerified } } return ensureOlmSessionsForDevicesAction.handle(devicesByUser) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt index b360cd0234..ebe219600d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.actions -import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_OLM import im.vector.matrix.android.internal.crypto.MXOlmDevice @@ -25,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.convertToUTF8 import timber.log.Timber -import java.util.* import javax.inject.Inject internal class MessageEncrypter @Inject constructor(private val credentials: Credentials, @@ -40,18 +38,12 @@ internal class MessageEncrypter @Inject constructor(private val credentials: Cre * @return the content for an m.room.encrypted event. */ fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { - val deviceInfoParticipantKey = HashMap() - val participantKeys = ArrayList() + val deviceInfoParticipantKey = deviceInfos.associateBy { it.identityKey()!! } - for (di in deviceInfos) { - participantKeys.add(di.identityKey()!!) - deviceInfoParticipantKey[di.identityKey()!!] = di - } - - val payloadJson = HashMap(payloadFields) + val payloadJson = payloadFields.toMutableMap() payloadJson["sender"] = credentials.userId - payloadJson["sender_device"] = credentials.deviceId + payloadJson["sender_device"] = credentials.deviceId!! // Include the Ed25519 key so that the recipient knows what // device this message came from. @@ -67,30 +59,24 @@ internal class MessageEncrypter @Inject constructor(private val credentials: Cre val ciphertext = HashMap() - for (deviceKey in participantKeys) { + for ((deviceKey, deviceInfo) in deviceInfoParticipantKey) { val sessionId = olmDevice.getSessionId(deviceKey) - if (!TextUtils.isEmpty(sessionId)) { + if (!sessionId.isNullOrEmpty()) { Timber.v("Using sessionid $sessionId for device $deviceKey") - val deviceInfo = deviceInfoParticipantKey[deviceKey] - payloadJson["recipient"] = deviceInfo!!.userId - - val recipientsKeysMap = HashMap() - recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!! - payloadJson["recipient_keys"] = recipientsKeysMap + payloadJson["recipient"] = deviceInfo.userId + payloadJson["recipient_keys"] = mapOf("ed25519" to deviceInfo.fingerprint()!!) val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson)) - ciphertext[deviceKey] = olmDevice.encryptMessage(deviceKey, sessionId!!, payloadString)!! + ciphertext[deviceKey] = olmDevice.encryptMessage(deviceKey, sessionId, payloadString)!! } } - val res = EncryptedMessage() - - res.algorithm = MXCRYPTO_ALGORITHM_OLM - res.senderKey = olmDevice.deviceCurve25519Key - res.cipherText = ciphertext - - return res + return EncryptedMessage( + algorithm = MXCRYPTO_ALGORITHM_OLM, + senderKey = olmDevice.deviceCurve25519Key, + cipherText = ciphertext + ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 9c14ed555a..6ffaf776b2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm -import android.text.TextUtils import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType @@ -148,7 +147,7 @@ internal class MXMegolmDecryption(private val userId: String, selfMap["deviceId"] = "*" recipients.add(selfMap) - if (!TextUtils.equals(sender, userId)) { + if (sender != userId) { val senderMap = HashMap() senderMap["userId"] = sender senderMap["deviceId"] = encryptedEventContent.deviceId!! @@ -176,17 +175,12 @@ internal class MXMegolmDecryption(private val userId: String, val encryptedEventContent = event.content.toModel() ?: return val pendingEventsKey = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}" - if (!pendingEvents.containsKey(pendingEventsKey)) { - pendingEvents[pendingEventsKey] = HashMap() - } + val timeline = pendingEvents.getOrPut(pendingEventsKey) { HashMap() } + val events = timeline.getOrPut(timelineId) { ArrayList() } - if (pendingEvents[pendingEventsKey]?.containsKey(timelineId) == false) { - pendingEvents[pendingEventsKey]?.put(timelineId, ArrayList()) - } - - if (pendingEvents[pendingEventsKey]?.get(timelineId)?.contains(event) == false) { - Timber.v("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId) - pendingEvents[pendingEventsKey]?.get(timelineId)?.add(event) + if (event !in events) { + Timber.v("## addEventToPendingList() : add Event ${event.eventId} in room id ${event.roomId}") + events.add(event) } } @@ -203,7 +197,7 @@ internal class MXMegolmDecryption(private val userId: String, var keysClaimed: MutableMap = HashMap() val forwardingCurve25519KeyChain: MutableList = ArrayList() - if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.sessionId) || TextUtils.isEmpty(roomKeyContent.sessionKey)) { + if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.sessionId.isNullOrEmpty() || roomKeyContent.sessionKey.isNullOrEmpty()) { Timber.e("## onRoomKeyEvent() : Key event is missing fields") return } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 77a8f2ec1c..897a1f0a5d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm -import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content @@ -38,7 +37,6 @@ import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.convertToUTF8 import timber.log.Timber -import java.util.* internal class MXMegolmEncryption( // The id of the room we will be sending to. @@ -85,7 +83,7 @@ internal class MXMegolmEncryption( keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!, - ArrayList(), keysClaimedMap, false) + emptyList(), keysClaimedMap, false) keysBackup.maybeBackupKeys() @@ -115,10 +113,8 @@ internal class MXMegolmEncryption( for (deviceId in deviceIds!!) { val deviceInfo = devicesInRoom.getObject(userId, deviceId) if (deviceInfo != null && null == safeSession.sharedWithDevices.getObject(userId, deviceId)) { - if (!shareMap.containsKey(userId)) { - shareMap[userId] = ArrayList() - } - shareMap[userId]!!.add(deviceInfo) + val devices = shareMap.getOrPut(userId) { ArrayList() } + devices.add(deviceInfo) } } } @@ -141,21 +137,17 @@ internal class MXMegolmEncryption( } // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) val subMap = HashMap>() - val userIds = ArrayList() var devicesCount = 0 - for (userId in devicesByUsers.keys) { - devicesByUsers[userId]?.let { - userIds.add(userId) - subMap[userId] = it - devicesCount += it.size - } + for ((userId, devices) in devicesByUsers) { + subMap[userId] = devices + devicesCount += devices.size if (devicesCount > 100) { break } } - Timber.v("## shareKey() ; userId $userIds") + Timber.v("## shareKey() ; userId ${subMap.keys}") shareUserDevicesKey(session, subMap) - val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() } + val remainingDevices = devicesByUsers - subMap.keys shareKey(session, remainingDevices) } @@ -209,8 +201,7 @@ internal class MXMegolmEncryption( continue } Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") - //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument - contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo))) + contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo))) haveTargets = true } } @@ -227,9 +218,8 @@ internal class MXMegolmEncryption( // attempted to share with) rather than the contentMap (those we did // share with), because we don't want to try to claim a one-time-key // for dead devices on every message. - for (userId in devicesByUser.keys) { - val devicesToShareWith = devicesByUser[userId] - for ((deviceId) in devicesToShareWith!!) { + for ((userId, devicesToShareWith) in devicesByUser) { + for ((deviceId) in devicesToShareWith) { session.sharedWithDevices.setObject(userId, deviceId, chainIndex) } } @@ -302,7 +292,7 @@ internal class MXMegolmEncryption( continue } - if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { + if (deviceInfo.identityKey() == olmDevice.deviceCurve25519Key) { // Don't bother sending to ourself continue } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index df3695db94..b6d1b98546 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm -import android.text.TextUtils import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.internal.crypto.DeviceListManager @@ -28,7 +27,6 @@ import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import java.util.* internal class MXOlmEncryption( private var roomId: String, @@ -49,7 +47,7 @@ internal class MXOlmEncryption( val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() for (device in devices) { val key = device.identityKey() - if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { + if (key == olmDevice.deviceCurve25519Key) { // Don't bother setting up session to ourself continue } @@ -61,10 +59,11 @@ internal class MXOlmEncryption( } } - val messageMap = HashMap() - messageMap["room_id"] = roomId - messageMap["type"] = eventType - messageMap["content"] = eventContent + val messageMap = mapOf( + "room_id" to roomId, + "type" to eventType, + "content" to eventContent + ) messageEncrypter.encryptMessage(messageMap, deviceInfos) return messageMap.toContent()!! diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index 51c3527042..e640e807c8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -59,6 +59,7 @@ import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import im.vector.matrix.android.internal.util.awaitCallback import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -69,6 +70,9 @@ import org.matrix.olm.OlmPkMessage import timber.log.Timber import java.security.InvalidParameterException import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine import kotlin.random.Random /** @@ -144,8 +148,8 @@ internal class KeysBackup @Inject constructor( progressListener: ProgressListener?, callback: MatrixCallback) { GlobalScope.launch(coroutineDispatchers.main) { - withContext(coroutineDispatchers.crypto) { - Try { + runCatching { + withContext(coroutineDispatchers.crypto) { val olmPkDecryption = OlmPkDecryption() val megolmBackupAuthData = MegolmBackupAuthData() @@ -394,7 +398,7 @@ internal class KeysBackup @Inject constructor( return keysBackupVersionTrust } - for (keyId in mySigs.keys) { + for ((keyId, mySignature) in mySigs) { // XXX: is this how we're supposed to get the device id? var deviceId: String? = null val components = keyId.split(":") @@ -412,7 +416,7 @@ internal class KeysBackup @Inject constructor( val fingerprint = device.fingerprint() if (fingerprint != null) { try { - olmDevice.verifySignature(fingerprint, authData.signalableJSONDictionary(), mySigs[keyId] as String) + olmDevice.verifySignature(fingerprint, authData.signalableJSONDictionary(), mySignature) isSignatureValid = true } catch (e: OlmException) { Timber.v(e, "getKeysBackupTrust: Bad signature from device ${device.deviceId}") @@ -615,8 +619,8 @@ internal class KeysBackup @Inject constructor( Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}") GlobalScope.launch(coroutineDispatchers.main) { - withContext(coroutineDispatchers.crypto) { - Try { + runCatching { + val decryption = withContext(coroutineDispatchers.crypto) { // Check if the recovery is valid before going any further if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysVersionResult)) { Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key for this keys version") @@ -624,85 +628,66 @@ internal class KeysBackup @Inject constructor( } // Get a PK decryption instance - val decryption = pkDecryptionFromRecoveryKey(recoveryKey) - if (decryption == null) { - // This should not happen anymore - Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key. Error") - throw InvalidParameterException("Invalid recovery key") - } - - decryption + pkDecryptionFromRecoveryKey(recoveryKey) + } + if (decryption == null) { + // This should not happen anymore + Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key. Error") + throw InvalidParameterException("Invalid recovery key") } - }.fold( - { - callback.onFailure(it) - }, - { decryption -> - stepProgressListener?.onStepProgress(StepProgressListener.Step.DownloadingKey) - // Get backed up keys from the homeserver - getKeys(sessionId, roomId, keysVersionResult.version!!, object : MatrixCallback { - override fun onSuccess(data: KeysBackupData) { - GlobalScope.launch(coroutineDispatchers.main) { - val importRoomKeysResult = withContext(coroutineDispatchers.crypto) { - val sessionsData = ArrayList() - // Restore that data - var sessionsFromHsCount = 0 - for (roomIdLoop in data.roomIdToRoomKeysBackupData.keys) { - for (sessionIdLoop in data.roomIdToRoomKeysBackupData[roomIdLoop]!!.sessionIdToKeyBackupData.keys) { - sessionsFromHsCount++ + stepProgressListener?.onStepProgress(StepProgressListener.Step.DownloadingKey) - val keyBackupData = data.roomIdToRoomKeysBackupData[roomIdLoop]!!.sessionIdToKeyBackupData[sessionIdLoop]!! + // Get backed up keys from the homeserver + val data = getKeys(sessionId, roomId, keysVersionResult.version!!) - val sessionData = decryptKeyBackupData(keyBackupData, sessionIdLoop, roomIdLoop, decryption) + withContext(coroutineDispatchers.crypto) { + val sessionsData = ArrayList() + // Restore that data + var sessionsFromHsCount = 0 + for ((roomIdLoop, backupData) in data.roomIdToRoomKeysBackupData) { + for ((sessionIdLoop, keyBackupData) in backupData.sessionIdToKeyBackupData) { + sessionsFromHsCount++ - sessionData?.let { - sessionsData.add(it) - } - } - } - Timber.v("restoreKeysWithRecoveryKey: Decrypted ${sessionsData.size} keys out" + - " of $sessionsFromHsCount from the backup store on the homeserver") + val sessionData = decryptKeyBackupData(keyBackupData, sessionIdLoop, roomIdLoop, decryption) - // Do not trigger a backup for them if they come from the backup version we are using - val backUp = keysVersionResult.version != keysBackupVersion?.version - if (backUp) { - Timber.v("restoreKeysWithRecoveryKey: Those keys will be backed up" + - " to backup version: ${keysBackupVersion?.version}") - } - - // Import them into the crypto store - val progressListener = if (stepProgressListener != null) { - object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - // Note: no need to post to UI thread, importMegolmSessionsData() will do it - stepProgressListener.onStepProgress(StepProgressListener.Step.ImportingKey(progress, total)) - } - } - } else { - null - } - - val result = megolmSessionDataImporter.handle(sessionsData, !backUp, uiHandler, progressListener) - - // Do not back up the key if it comes from a backup recovery - if (backUp) { - maybeBackupKeys() - } - - result - } - - callback.onSuccess(importRoomKeysResult) - } + sessionData?.let { + sessionsData.add(it) } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - }) + } } - ) + Timber.v("restoreKeysWithRecoveryKey: Decrypted ${sessionsData.size} keys out" + + " of $sessionsFromHsCount from the backup store on the homeserver") + + // Do not trigger a backup for them if they come from the backup version we are using + val backUp = keysVersionResult.version != keysBackupVersion?.version + if (backUp) { + Timber.v("restoreKeysWithRecoveryKey: Those keys will be backed up" + + " to backup version: ${keysBackupVersion?.version}") + } + + // Import them into the crypto store + val progressListener = if (stepProgressListener != null) { + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + // Note: no need to post to UI thread, importMegolmSessionsData() will do it + stepProgressListener.onStepProgress(StepProgressListener.Step.ImportingKey(progress, total)) + } + } + } else { + null + } + + val result = megolmSessionDataImporter.handle(sessionsData, !backUp, uiHandler, progressListener) + + // Do not back up the key if it comes from a backup recovery + if (backUp) { + maybeBackupKeys() + } + + result + } + }.foldToCallback(callback) } } @@ -715,7 +700,7 @@ internal class KeysBackup @Inject constructor( Timber.v("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}") GlobalScope.launch(coroutineDispatchers.main) { - withContext(coroutineDispatchers.crypto) { + runCatching { val progressListener = if (stepProgressListener != null) { object : ProgressListener { override fun onProgress(progress: Int, total: Int) { @@ -728,22 +713,18 @@ internal class KeysBackup @Inject constructor( null } - Try { + val recoveryKey = withContext(coroutineDispatchers.crypto) { recoveryKeyFromPassword(password, keysBackupVersion, progressListener) } - }.fold( - { - callback.onFailure(it) - }, - { recoveryKey -> - if (recoveryKey == null) { - Timber.v("backupKeys: Invalid configuration") - callback.onFailure(IllegalStateException("Invalid configuration")) - } else { - restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, roomId, sessionId, stepProgressListener, callback) - } + if (recoveryKey == null) { + Timber.v("backupKeys: Invalid configuration") + throw IllegalStateException("Invalid configuration") + } else { + awaitCallback { + restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, roomId, sessionId, stepProgressListener, it) } - ) + } + }.foldToCallback(callback) } } @@ -751,60 +732,26 @@ internal class KeysBackup @Inject constructor( * Same method as [RoomKeysRestClient.getRoomKey] except that it accepts nullable * parameters and always returns a KeysBackupData object through the Callback */ - private fun getKeys(sessionId: String?, + private suspend fun getKeys(sessionId: String?, roomId: String?, - version: String, - callback: MatrixCallback) { - if (roomId != null && sessionId != null) { + version: String): KeysBackupData { + return if (roomId != null && sessionId != null) { // Get key for the room and for the session - getRoomSessionDataTask - .configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) { - this.callback = object : MatrixCallback { - override fun onSuccess(data: KeyBackupData) { - // Convert to KeysBackupData - val keysBackupData = KeysBackupData() - keysBackupData.roomIdToRoomKeysBackupData = HashMap() - val roomKeysBackupData = RoomKeysBackupData() - roomKeysBackupData.sessionIdToKeyBackupData = HashMap() - roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data - keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData - - callback.onSuccess(keysBackupData) - } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - } - } - .executeBy(taskExecutor) + val data = getRoomSessionDataTask.execute(GetRoomSessionDataTask.Params(roomId, sessionId, version)) + // Convert to KeysBackupData + KeysBackupData(mutableMapOf( + roomId to RoomKeysBackupData(mutableMapOf( + sessionId to data + )) + )) } else if (roomId != null) { // Get all keys for the room - getRoomSessionsDataTask - .configureWith(GetRoomSessionsDataTask.Params(roomId, version)) { - this.callback = object : MatrixCallback { - override fun onSuccess(data: RoomKeysBackupData) { - // Convert to KeysBackupData - val keysBackupData = KeysBackupData() - keysBackupData.roomIdToRoomKeysBackupData = HashMap() - keysBackupData.roomIdToRoomKeysBackupData[roomId] = data - - callback.onSuccess(keysBackupData) - } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - } - } - .executeBy(taskExecutor) + val data = getRoomSessionsDataTask.execute(GetRoomSessionsDataTask.Params(roomId, version)) + // Convert to KeysBackupData + KeysBackupData(mutableMapOf(roomId to data)) } else { // Get all keys - getSessionsDataTask - .configureWith(GetSessionsDataTask.Params(version)) { - this.callback = callback - } - .executeBy(taskExecutor) + getSessionsDataTask.execute(GetSessionsDataTask.Params(version)) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt index 69fc314796..361b8bc205 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt @@ -17,13 +17,11 @@ package im.vector.matrix.android.internal.crypto.model -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import im.vector.matrix.android.internal.crypto.MegolmSessionData import org.matrix.olm.OlmInboundGroupSession import timber.log.Timber import java.io.Serializable -import java.util.* /** * This class adds more context to a OlmInboundGroupSession object. @@ -91,7 +89,7 @@ class OlmInboundGroupSessionWrapper : Serializable { try { olmInboundGroupSession = OlmInboundGroupSession.importSession(megolmSessionData.sessionKey!!) - if (!TextUtils.equals(olmInboundGroupSession!!.sessionIdentifier(), megolmSessionData.sessionId)) { + if (olmInboundGroupSession!!.sessionIdentifier() != megolmSessionData.sessionId) { throw Exception("Mismatched group session Id") } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 01826cee71..d88a84de9e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.store.db -import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.NewSessionListener @@ -101,8 +100,8 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati // Check credentials // The device id may not have been provided in credentials. // Check it only if provided, else trust the stored one. - if (!TextUtils.equals(currentMetadata.userId, credentials.userId) - || (credentials.deviceId != null && !TextUtils.equals(credentials.deviceId, currentMetadata.deviceId))) { + if (currentMetadata.userId != credentials.userId + || (credentials.deviceId != null && credentials.deviceId != currentMetadata.deviceId)) { Timber.w("## open() : Credentials do not match, close this store and delete data") deleteAll = true currentMetadata = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt index 23b0f958bf..80f326e083 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt @@ -44,18 +44,14 @@ internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private } val map = MXUsersDevicesMap() keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> - for (userId in oneTimeKeys.keys) { - val mapByUserId = oneTimeKeys[userId] + for ((userId, mapByUserId) in oneTimeKeys) { + for ((deviceId, deviceKey) in mapByUserId) { + val mxKey = MXKey.from(deviceKey) - if (mapByUserId != null) { - for (deviceId in mapByUserId.keys) { - val mxKey = MXKey.from(mapByUserId[deviceId]) - - if (mxKey != null) { - map.setObject(userId, deviceId, mxKey) - } else { - Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") - } + if (mxKey != null) { + map.setObject(userId, deviceId, mxKey) + } else { + Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt index 92908071a1..230af3874e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -16,13 +16,11 @@ package im.vector.matrix.android.internal.crypto.tasks -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import java.util.* import javax.inject.Inject internal interface DownloadKeysForUsersTask : Task { @@ -37,19 +35,13 @@ internal class DefaultDownloadKeysForUsers @Inject constructor(private val crypt : DownloadKeysForUsersTask { override suspend fun execute(params: DownloadKeysForUsersTask.Params): KeysQueryResponse { - val downloadQuery = HashMap>() - - if (null != params.userIds) { - for (userId in params.userIds) { - downloadQuery[userId] = HashMap() - } - } + val downloadQuery = params.userIds?.associateWith { emptyMap() }.orEmpty() val body = KeysQueryBody( deviceKeys = downloadQuery ) - if (!TextUtils.isEmpty(params.token)) { + if (!params.token.isNullOrEmpty()) { body.token = params.token } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt index 1abef5763e..47f3050b88 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.tasks -import android.text.TextUtils import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody import im.vector.matrix.android.internal.network.executeRequest @@ -37,7 +36,7 @@ internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoAp override suspend fun execute(params: SetDeviceNameTask.Params) { val body = UpdateDeviceInfoBody( - displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName + displayName = params.deviceName ) return executeRequest { apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt index 097ab50abc..89da02b275 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.network import android.content.Context -import android.text.TextUtils import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.internal.di.MatrixScope import timber.log.Timber @@ -60,10 +59,10 @@ internal class UserAgentHolder @Inject constructor(private val context: Context) Timber.e(e, "## initUserAgent() : failed") } - var systemUserAgent = System.getProperty("http.agent") + val systemUserAgent = System.getProperty("http.agent") // cannot retrieve the application version - if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(appVersion)) { + if (appName.isEmpty() || appVersion.isEmpty()) { if (null == systemUserAgent) { userAgent = "Java" + System.getProperty("java.version") } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt index 5fcad0e30f..68f48d20db 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt @@ -75,9 +75,7 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU private fun updateState(key: String, state: ContentUploadStateTracker.State) { states[key] = state mainHandler.post { - listeners[key]?.also { listeners -> - listeners.forEach { it.onUpdate(state) } - } + listeners[key]?.forEach { it.onUpdate(state) } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index 3479d6dcdc..40dc0a44c0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -65,13 +65,11 @@ internal class DefaultGetGroupDataTask @Inject constructor( groupSummaryEntity.displayName = if (name.isNullOrEmpty()) groupId else name groupSummaryEntity.shortDescription = groupSummary.profile?.shortDescription ?: "" - val roomIds = groupRooms.rooms.map { it.roomId } groupSummaryEntity.roomIds.clear() - groupSummaryEntity.roomIds.addAll(roomIds) + groupRooms.rooms.mapTo(groupSummaryEntity.roomIds) { it.roomId } - val userIds = groupUsers.users.map { it.userId } groupSummaryEntity.userIds.clear() - groupSummaryEntity.userIds.addAll(userIds) + groupUsers.users.mapTo(groupSummaryEntity.userIds) { it.userId } groupSummaryEntity.membership = when (groupSummary.user?.membership) { Membership.JOIN.value -> Membership.JOIN diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt index a48990d48f..6e47bdfeaa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt @@ -50,19 +50,15 @@ internal class DefaultProcessEventForPushTask @Inject constructor( defaultPushRuleService.dispatchRoomJoined(it) } val newJoinEvents = params.syncResponse.join - .map { entries -> - entries.value.timeline?.events?.map { it.copy(roomId = entries.key) } + .mapNotNull { (key, value) -> + value.timeline?.events?.map { it.copy(roomId = key) } } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) + .flatten() val inviteEvents = params.syncResponse.invite - .map { entries -> - entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) } + .mapNotNull { (key, value) -> + value.inviteState?.events?.map { it.copy(roomId = key) } } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) + .flatten() val allEvents = (newJoinEvents + inviteEvents).filter { event -> when (event.type) { EventType.MESSAGE, @@ -84,16 +80,12 @@ internal class DefaultProcessEventForPushTask @Inject constructor( } val allRedactedEvents = params.syncResponse.join - .map { entries -> - entries.value.timeline?.events?.filter { - it.type == EventType.REDACTION - } - .orEmpty() - .mapNotNull { it.redacts } - } - .fold(emptyList(), { acc, next -> - acc + next - }) + .asSequence() + .mapNotNull { (_, value) -> value.timeline?.events } + .flatten() + .filter { it.type == EventType.REDACTION } + .mapNotNull { it.redacts } + .toList() Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events") @@ -107,18 +99,11 @@ internal class DefaultProcessEventForPushTask @Inject constructor( private fun fulfilledBingRule(event: Event, rules: List): PushRule? { // TODO This should be injected val conditionResolver = DefaultConditionResolver(event, roomService, userId) - rules.filter { it.enabled }.forEach { rule -> - val isFullfilled = rule.conditions?.map { + return rules.firstOrNull { rule -> + // All conditions must hold true for an event in order to apply the action for the event. + rule.enabled && rule.conditions?.all { it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false - }?.fold(true/*A rule with no conditions always matches*/, { acc, next -> - // All conditions must hold true for an event in order to apply the action for the event. - acc && next - }) ?: false - - if (isFullfilled) { - return rule - } + } ?: false } - return null } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt index 74b8d3c3a7..b50424b343 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt @@ -132,8 +132,7 @@ internal class RoomMembers(private val realm: Realm, .findAll() .map { it.asDomain() } .associateBy { it.stateKey!! } - .mapValues { it.value.content.toModel()!! } - .filterValues { predicate(it) } + .filterValues { predicate(it.content.toModel()!!) } .keys .toList() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt index d1abf93c21..baa01e4042 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt @@ -49,23 +49,22 @@ internal class DefaultFindReactionEventForUndoTask @Inject constructor(private v } private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String): EventEntity? { - val summary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() - if (summary != null) { - summary.reactionsSummary.where() - .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction) - .findFirst()?.let { - // want to find the event orignated by me! - it.sourceEvents.forEach { - // find source event - EventEntity.where(realm, it).findFirst()?.let { eventEntity -> - // is it mine? - if (eventEntity.sender == userId) { - return eventEntity - } - } - } - } - } - return null + val summary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() ?: return null + + val rase = summary.reactionsSummary.where() + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction) + .findFirst() ?: return null + + // want to find the event orignated by me! + return rase.sourceEvents + .asSequence() + .mapNotNull { + // find source event + EventEntity.where(realm, it).findFirst() + } + .firstOrNull { eventEntity -> + // is it mine? + eventEntity.sender == userId + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventDecryptor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventDecryptor.kt index 9562f57d63..e3b57949f4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventDecryptor.kt @@ -39,11 +39,10 @@ internal class TimelineEventDecryptor( override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { synchronized(unknownSessionsFailure) { val toDecryptAgain = ArrayList() - unknownSessionsFailure[sessionId]?.let { eventIds -> - toDecryptAgain.addAll(eventIds) - } + val eventIds = unknownSessionsFailure[sessionId] + if (eventIds != null) toDecryptAgain.addAll(eventIds) if (toDecryptAgain.isNotEmpty()) { - unknownSessionsFailure[sessionId]?.clear() + eventIds?.clear() toDecryptAgain.forEach { requestDecryption(it) } @@ -72,17 +71,17 @@ internal class TimelineEventDecryptor( fun requestDecryption(eventId: String) { synchronized(existingRequests) { - if (existingRequests.contains(eventId)) { - return Unit.also { - Timber.d("Skip Decryption request for event $eventId, already requested") - } + if (eventId in existingRequests) { + Timber.d("Skip Decryption request for event $eventId, already requested") + return } existingRequests.add(eventId) } synchronized(unknownSessionsFailure) { - unknownSessionsFailure.values.forEach { - if (it.contains(eventId)) return@synchronized Unit.also { + for (it in unknownSessionsFailure.values) { + if (eventId in it) { Timber.d("Skip Decryption request for event $eventId, unknown session") + break } } } @@ -116,10 +115,7 @@ internal class TimelineEventDecryptor( event.content?.toModel()?.let { content -> content.sessionId?.let { sessionId -> synchronized(unknownSessionsFailure) { - val list = unknownSessionsFailure[sessionId] - ?: ArrayList().also { - unknownSessionsFailure[sessionId] = it - } + val list = unknownSessionsFailure.getOrPut(sessionId) { ArrayList() } list.add(eventId) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/securestorage/SecretStoringUtils.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/securestorage/SecretStoringUtils.kt index de1c7b0479..260f98d97f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/securestorage/SecretStoringUtils.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/securestorage/SecretStoringUtils.kt @@ -461,9 +461,9 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.certificate.publicKey) val outputStream = ByteArrayOutputStream() - val cipherOutputStream = CipherOutputStream(outputStream, inputCipher) - cipherOutputStream.write(secret) - cipherOutputStream.close() + CipherOutputStream(outputStream, inputCipher).use { + it.write(secret) + } return outputStream.toByteArray() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt index b7ad577203..91397fae7e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.sync -import android.text.TextUtils import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType @@ -41,9 +40,9 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: initialSyncProgressService?.reportProgress(((index / total.toFloat()) * 100).toInt()) // Decrypt event if necessary decryptEvent(event, null) - if (TextUtils.equals(event.getClearType(), EventType.MESSAGE) + if (event.getClearType() == EventType.MESSAGE && event.getClearContent()?.toModel()?.type == "m.bad.encrypted") { - Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content) + Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}") } else { sasVerificationService.onToDeviceEvent(event) cryptoService.onToDeviceEvent(event) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt index c630f95b29..4e57aa5be1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt @@ -31,7 +31,8 @@ import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber import java.net.SocketTimeoutException -import java.util.* +import java.util.Timer +import java.util.TimerTask /** * Can execute periodic sync task. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt index cfb8a48038..b5f404019a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt @@ -31,18 +31,10 @@ internal class DirectChatsHelper @Inject constructor(@SessionDatabase */ fun getLocalUserAccount(filterRoomId: String? = null): MutableMap> { return Realm.getInstance(realmConfiguration).use { realm -> - val currentDirectRooms = RoomSummaryEntity.getDirectRooms(realm) - val directChatsMap = mutableMapOf>() - for (directRoom in currentDirectRooms) { - if (directRoom.roomId == filterRoomId) continue - val directUserId = directRoom.directUserId ?: continue - directChatsMap - .getOrPut(directUserId, { arrayListOf() }) - .apply { - add(directRoom.roomId) - } - } - directChatsMap + RoomSummaryEntity.getDirectRooms(realm) + .asSequence() + .filter { it.roomId != filterRoomId && it.directUserId != null } + .groupByTo(mutableMapOf(), { it.directUserId!! }, { it.roomId }) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt index 366f4e3d30..1bf939b91b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.task import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable -import java.util.* +import java.util.UUID internal fun Task.configureWith(params: PARAMS, init: (ConfigurableTask.Builder.() -> Unit) = {} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt index 2e71d6b2fc..058a862bc8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt @@ -59,19 +59,11 @@ object CompatUtil { private const val SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED = "android_version_when_key_has_been_generated" private var sSecretKeyAndVersion: SecretKeyAndVersion? = null - private var sPrng: SecureRandom? = null /** * Returns the unique SecureRandom instance shared for all local storage encryption operations. */ - private val prng: SecureRandom - get() { - if (sPrng == null) { - sPrng = SecureRandom() - } - - return sPrng!! - } + private val prng: SecureRandom by lazy(LazyThreadSafetyMode.NONE) { SecureRandom() } /** * Create a GZIPOutputStream instance diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt index e57c289388..e748ad2778 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt @@ -24,12 +24,9 @@ import java.security.MessageDigest fun String.md5() = try { val digest = MessageDigest.getInstance("md5") digest.update(toByteArray()) - val bytes = digest.digest() - val sb = StringBuilder() - for (i in bytes.indices) { - sb.append(String.format("%02X", bytes[i])) - } - sb.toString().toLowerCase() + digest.digest() + .joinToString("") { String.format("%02X", it) } + .toLowerCase() } catch (exc: Exception) { // Should not happen, but just in case hashCode().toString() diff --git a/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt index ac5b5c882d..271d747fef 100755 --- a/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -21,7 +21,6 @@ package im.vector.riotx.gplay.push.fcm import android.os.Handler import android.os.Looper -import android.text.TextUtils import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner import com.google.firebase.messaging.FirebaseMessagingService @@ -214,10 +213,10 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { } } else { if (notifiableEvent is NotifiableMessageEvent) { - if (TextUtils.isEmpty(notifiableEvent.senderName)) { + if (notifiableEvent.senderName.isEmpty()) { notifiableEvent.senderName = data["sender_display_name"] ?: data["sender"] ?: "" } - if (TextUtils.isEmpty(notifiableEvent.roomName)) { + if (notifiableEvent.roomName.isEmpty()) { notifiableEvent.roomName = findRoomNameBestEffort(data, session) ?: "" } } diff --git a/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt b/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt index dc88dec406..fb320afded 100644 --- a/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt +++ b/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt @@ -18,7 +18,6 @@ package im.vector.riotx.core.dialogs import android.app.Activity import android.text.Editable -import android.text.TextUtils import android.widget.Button import android.widget.ImageView import androidx.appcompat.app.AlertDialog @@ -45,15 +44,15 @@ class ExportKeysDialog { val textWatcher = object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { when { - TextUtils.isEmpty(passPhrase1EditText.text) -> { + passPhrase1EditText.text.isNullOrEmpty() -> { exportButton.isEnabled = false passPhrase2Til.error = null } - TextUtils.equals(passPhrase1EditText.text, passPhrase2EditText.text) -> { + passPhrase1EditText.text == passPhrase2EditText.text -> { exportButton.isEnabled = true passPhrase2Til.error = null } - else -> { + else -> { exportButton.isEnabled = false passPhrase2Til.error = activity.getString(R.string.passphrase_passphrase_does_not_match) } diff --git a/vector/src/main/java/im/vector/riotx/core/intent/ExternalIntentAnalyser.kt b/vector/src/main/java/im/vector/riotx/core/intent/ExternalIntentAnalyser.kt index d04290b7c1..11710d3e13 100644 --- a/vector/src/main/java/im/vector/riotx/core/intent/ExternalIntentAnalyser.kt +++ b/vector/src/main/java/im/vector/riotx/core/intent/ExternalIntentAnalyser.kt @@ -21,9 +21,7 @@ import android.content.ClipDescription import android.content.Intent import android.net.Uri import android.os.Build -import android.text.TextUtils import androidx.core.util.PatternsCompat.WEB_URL -import java.util.* /** * Inspired from Riot code: RoomMediaMessage.java @@ -69,34 +67,28 @@ fun analyseIntent(intent: Intent): List { // chrome adds many items when sharing an web page link // so, test first the type - if (TextUtils.equals(intent.type, ClipDescription.MIMETYPE_TEXT_PLAIN)) { + if (intent.type == ClipDescription.MIMETYPE_TEXT_PLAIN) { var message: String? = intent.getStringExtra(Intent.EXTRA_TEXT) - - if (null == message) { - val sequence = intent.getCharSequenceExtra(Intent.EXTRA_TEXT) - if (null != sequence) { - message = sequence.toString() - } - } + ?: intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString() val subject = intent.getStringExtra(Intent.EXTRA_SUBJECT) - if (!TextUtils.isEmpty(subject)) { - if (TextUtils.isEmpty(message)) { + if (!subject.isNullOrEmpty()) { + if (message.isNullOrEmpty()) { message = subject - } else if (WEB_URL.matcher(message!!).matches()) { + } else if (WEB_URL.matcher(message).matches()) { message = subject + "\n" + message } } - if (!TextUtils.isEmpty(message)) { - externalIntentDataList.add(ExternalIntentData.IntentDataText(message!!, null, intent.type)) + if (!message.isNullOrEmpty()) { + externalIntentDataList.add(ExternalIntentData.IntentDataText(message, null, intent.type)) return externalIntentDataList } } var clipData: ClipData? = null - var mimetypes: MutableList? = null + var mimeTypes: List? = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { clipData = intent.clipData @@ -106,41 +98,26 @@ fun analyseIntent(intent: Intent): List { if (null != clipData) { if (null != clipData.description) { if (0 != clipData.description.mimeTypeCount) { - mimetypes = ArrayList() - - for (i in 0 until clipData.description.mimeTypeCount) { - mimetypes.add(clipData.description.getMimeType(i)) + mimeTypes = with(clipData.description) { + List(mimeTypeCount) { getMimeType(it) } } // if the filter is "accept anything" the mimetype does not make sense - if (1 == mimetypes.size) { - if (mimetypes[0].endsWith("/*")) { - mimetypes = null + if (1 == mimeTypes.size) { + if (mimeTypes[0].endsWith("/*")) { + mimeTypes = null } } } } - val count = clipData.itemCount - - for (i in 0 until count) { + for (i in 0 until clipData.itemCount) { val item = clipData.getItemAt(i) - var mimetype: String? = null + val mimeType = mimeTypes?.getOrElse(i) { mimeTypes[0] } + // uris list is not a valid mimetype + .takeUnless { it == ClipDescription.MIMETYPE_TEXT_URILIST } - if (null != mimetypes) { - if (i < mimetypes.size) { - mimetype = mimetypes[i] - } else { - mimetype = mimetypes[0] - } - - // uris list is not a valid mimetype - if (TextUtils.equals(mimetype, ClipDescription.MIMETYPE_TEXT_URILIST)) { - mimetype = null - } - } - - externalIntentDataList.add(ExternalIntentData.IntentDataClipData(item, mimetype)) + externalIntentDataList.add(ExternalIntentData.IntentDataClipData(item, mimeType)) } } else if (null != intent.data) { externalIntentDataList.add(ExternalIntentData.IntentDataUri(intent.data!!)) diff --git a/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt b/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt index 5c0bc88765..76df61dd33 100755 --- a/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt +++ b/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt @@ -17,7 +17,6 @@ package im.vector.riotx.core.preference import android.content.Context -import android.text.TextUtils import android.util.AttributeSet import android.view.View import android.widget.RadioGroup @@ -84,7 +83,7 @@ class BingRulePreference : VectorPreference { val ruleStatusIndex: Int get() { if (null != rule) { - if (TextUtils.equals(rule!!.ruleId, BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS)) { + if (rule!!.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { if (rule!!.shouldNotNotify()) { return if (rule!!.isEnabled) { NOTIFICATION_OFF_INDEX @@ -143,7 +142,7 @@ class BingRulePreference : VectorPreference { if (null != this.rule && index != ruleStatusIndex) { rule = BingRule(this.rule!!) - if (TextUtils.equals(rule.ruleId, BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS)) { + if (rule.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { when (index) { NOTIFICATION_OFF_INDEX -> { rule.isEnabled = true @@ -164,8 +163,8 @@ class BingRulePreference : VectorPreference { } if (NOTIFICATION_OFF_INDEX == index) { - if (TextUtils.equals(this.rule!!.kind, BingRule.KIND_UNDERRIDE) - || TextUtils.equals(rule.ruleId, BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS)) { + if (this.rule!!.kind == BingRule.KIND_UNDERRIDE + || rule.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { rule.setNotify(false) } else { rule.isEnabled = false @@ -173,11 +172,11 @@ class BingRulePreference : VectorPreference { } else { rule.isEnabled = true rule.setNotify(true) - rule.setHighlight(!TextUtils.equals(this.rule!!.kind, BingRule.KIND_UNDERRIDE) - && !TextUtils.equals(rule.ruleId, BingRule.RULE_ID_INVITE_ME) + rule.setHighlight(this.rule!!.kind != BingRule.KIND_UNDERRIDE + && rule.ruleId != BingRule.RULE_ID_INVITE_ME && NOTIFICATION_NOISY_INDEX == index) if (NOTIFICATION_NOISY_INDEX == index) { - rule.notificationSound = if (TextUtils.equals(rule.ruleId, BingRule.RULE_ID_CALL)) { + rule.notificationSound = if (rule.ruleId == BingRule.RULE_ID_CALL) { BingRule.ACTION_VALUE_RING } else { BingRule.ACTION_VALUE_DEFAULT diff --git a/vector/src/main/java/im/vector/riotx/core/resources/ResourceUtils.kt b/vector/src/main/java/im/vector/riotx/core/resources/ResourceUtils.kt index dad95c97be..236caec081 100644 --- a/vector/src/main/java/im/vector/riotx/core/resources/ResourceUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/resources/ResourceUtils.kt @@ -18,7 +18,6 @@ package im.vector.riotx.core.resources import android.content.Context import android.net.Uri -import android.text.TextUtils import android.webkit.MimeTypeMap import im.vector.riotx.core.utils.getFileExtension import timber.log.Timber @@ -73,7 +72,7 @@ fun openResource(context: Context, uri: Uri, providedMimetype: String?): Resourc var mimetype = providedMimetype try { // if the mime type is not provided, try to find it out - if (TextUtils.isEmpty(mimetype)) { + if (mimetype.isNullOrEmpty()) { mimetype = context.contentResolver.getType(uri) // try to find the mimetype from the filename diff --git a/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt index b159b7b03b..145a26aed2 100644 --- a/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt @@ -20,7 +20,6 @@ import android.content.Context import android.graphics.Color import android.text.SpannableString import android.text.TextPaint -import android.text.TextUtils import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.util.AttributeSet @@ -168,7 +167,7 @@ class NotificationAreaView @JvmOverloads constructor( } else { imageView.setImageResource(R.drawable.scrolldown) messageView.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_room_notification_text_color)) - if (!TextUtils.isEmpty(state.message)) { + if (!state.message.isNullOrEmpty()) { messageView.text = SpannableString(state.message) } } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt index 4fd574f904..0009fd46f9 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt @@ -17,7 +17,6 @@ package im.vector.riotx.core.utils import android.content.Context -import android.text.TextUtils import timber.log.Timber import java.io.File @@ -60,7 +59,7 @@ private fun logAction(file: File): Boolean { if (file.isDirectory) { Timber.v(file.toString()) } else { - Timber.v(file.toString() + " " + file.length() + " bytes") + Timber.v("$file ${file.length()} bytes") } return true } @@ -96,26 +95,19 @@ private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean { fun getFileExtension(fileUri: String): String? { var reducedStr = fileUri - if (!TextUtils.isEmpty(reducedStr)) { + if (reducedStr.isNotEmpty()) { // Remove fragment - val fragment = fileUri.lastIndexOf('#') - if (fragment > 0) { - reducedStr = fileUri.substring(0, fragment) - } + reducedStr = reducedStr.substringBeforeLast('#') // Remove query - val query = reducedStr.lastIndexOf('?') - if (query > 0) { - reducedStr = reducedStr.substring(0, query) - } + reducedStr = reducedStr.substringBeforeLast('?') // Remove path - val filenamePos = reducedStr.lastIndexOf('/') - val filename = if (0 <= filenamePos) reducedStr.substring(filenamePos + 1) else reducedStr + val filename = reducedStr.substringAfterLast('/') // Contrary to method MimeTypeMap.getFileExtensionFromUrl, we do not check the pattern // See https://stackoverflow.com/questions/14320527/android-should-i-use-mimetypemap-getfileextensionfromurl-bugs - if (!filename.isEmpty()) { + if (filename.isNotEmpty()) { val dotPos = filename.lastIndexOf('.') if (0 <= dotPos) { val ext = filename.substring(dotPos + 1) @@ -135,14 +127,10 @@ fun getFileExtension(fileUri: String): String? { * ========================================================================================== */ fun getSizeOfFiles(context: Context, root: File): Int { - Timber.v("Get size of " + root.absolutePath) - return if (root.isDirectory) { - root.list() - .map { - getSizeOfFiles(context, File(root, it)) - } - .fold(0, { acc, other -> acc + other }) - } else { - root.length().toInt() - } + return root.walkTopDown() + .onEnter { + Timber.v("Get size of ${it.absolutePath}") + true + } + .sumBy { it.length().toInt() } } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt index b97614eaa5..346a4b07e9 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt @@ -21,7 +21,6 @@ import android.app.Activity import android.content.Context import android.content.pm.PackageManager import android.os.Build -import android.text.TextUtils import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat @@ -29,7 +28,6 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import im.vector.riotx.R import timber.log.Timber -import java.util.* private const val LOG_TAG = "PermissionUtils" @@ -72,7 +70,7 @@ const val PERMISSION_REQUEST_CODE_DOWNLOAD_FILE = 575 */ fun logPermissionStatuses(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val permissions = Arrays.asList( + val permissions = listOf( Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE, @@ -221,25 +219,25 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, permissionListAlreadyDenied.forEach { when (it) { Manifest.permission.CAMERA -> { - if (!TextUtils.isEmpty(explanationMessage)) { + if (explanationMessage.isNotEmpty()) { explanationMessage += "\n\n" } explanationMessage += activity.getString(R.string.permissions_rationale_msg_camera) } Manifest.permission.RECORD_AUDIO -> { - if (!TextUtils.isEmpty(explanationMessage)) { + if (explanationMessage.isNotEmpty()) { explanationMessage += "\n\n" } explanationMessage += activity.getString(R.string.permissions_rationale_msg_record_audio) } Manifest.permission.WRITE_EXTERNAL_STORAGE -> { - if (!TextUtils.isEmpty(explanationMessage)) { + if (explanationMessage.isNotEmpty()) { explanationMessage += "\n\n" } explanationMessage += activity.getString(R.string.permissions_rationale_msg_storage) } Manifest.permission.READ_CONTACTS -> { - if (!TextUtils.isEmpty(explanationMessage)) { + if (!explanationMessage.isEmpty()) { explanationMessage += "\n\n" } explanationMessage += activity.getString(R.string.permissions_rationale_msg_contacts) @@ -255,7 +253,7 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, .setMessage(explanationMessage) .setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() } .setPositiveButton(R.string.ok) { _, _ -> - if (!permissionsListToBeGranted.isEmpty()) { + if (permissionsListToBeGranted.isNotEmpty()) { fragment?.requestPermissions(permissionsListToBeGranted.toTypedArray(), requestCode) ?: run { ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode) diff --git a/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt index f299589119..0b5df0d2e0 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt @@ -24,9 +24,9 @@ import java.util.* object TextUtils { private val suffixes = TreeMap().also { - it.put(1000, "k") - it.put(1000000, "M") - it.put(1000000000, "G") + it[1000] = "k" + it[1000000] = "M" + it[1000000000] = "G" } fun formatCountToShortDecimal(value: Int): String { diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt index b87972b28c..f4717fa7b3 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.crypto.keysbackup.setup import android.os.AsyncTask import android.os.Bundle -import android.text.TextUtils import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.EditText @@ -122,7 +121,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() { }) viewModel.passphrase.observe(this, Observer { newValue -> - if (TextUtils.isEmpty(newValue)) { + if (newValue.isEmpty()) { viewModel.passwordStrength.value = null } else { AsyncTask.execute { @@ -172,7 +171,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() { @OnClick(R.id.keys_backup_setup_step2_button) fun doNext() { when { - TextUtils.isEmpty(viewModel.passphrase.value) -> { + viewModel.passphrase.value.isNullOrEmpty() -> { viewModel.passphraseError.value = context?.getString(R.string.passphrase_empty_error_message) } viewModel.passphrase.value != viewModel.confirmPassphrase.value -> { @@ -192,7 +191,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() { @OnClick(R.id.keys_backup_setup_step2_skip_button) fun skipPassphrase() { when { - TextUtils.isEmpty(viewModel.passphrase.value) -> { + viewModel.passphrase.value.isNullOrEmpty() -> { // Generate a recovery key for the user viewModel.megolmBackupCreationInfo = null diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt index 8e9ae8c08f..f5db16c8ee 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt @@ -20,7 +20,6 @@ package im.vector.riotx.features.crypto.keysrequest import android.content.Context -import android.text.TextUtils import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener @@ -39,7 +38,8 @@ import im.vector.riotx.features.popup.PopupAlertManager import timber.log.Timber import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Locale +import java.util.Date import javax.inject.Inject import javax.inject.Singleton import kotlin.collections.ArrayList @@ -100,7 +100,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) alertsToRequests[mappingKey] = ArrayList().apply { this.add(request) } // Add a notification for every incoming request - session?.downloadKeys(Arrays.asList(userId), false, object : MatrixCallback> { + session?.downloadKeys(listOf(userId), false, object : MatrixCallback> { override fun onSuccess(data: MXUsersDevicesMap) { val deviceInfo = data.getObject(userId, deviceId) @@ -147,7 +147,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) wasNewDevice: Boolean, deviceInfo: MXDeviceInfo?, moreInfo: DeviceInfo? = null) { - val deviceName = if (TextUtils.isEmpty(deviceInfo!!.displayName())) deviceInfo.deviceId else deviceInfo.displayName() + val deviceName = if (deviceInfo!!.displayName().isNullOrEmpty()) deviceInfo.deviceId else deviceInfo.displayName() val dialogText: String? if (moreInfo != null) { @@ -244,12 +244,12 @@ class KeyRequestHandler @Inject constructor(private val context: Context) val deviceId = request.deviceId val requestId = request.requestId - if (TextUtils.isEmpty(userId) || TextUtils.isEmpty(deviceId) || TextUtils.isEmpty(requestId)) { + if (userId.isNullOrEmpty() || deviceId.isNullOrEmpty() || requestId.isNullOrEmpty()) { Timber.e("## handleKeyRequestCancellation() : invalid parameters") return } - val alertMgrUniqueKey = alertManagerId(deviceId!!, userId!!) + val alertMgrUniqueKey = alertManagerId(deviceId, userId) alertsToRequests[alertMgrUniqueKey]?.removeAll { it.deviceId == request.deviceId && it.userId == request.userId diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 0774e48d27..4d93c8a16c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.home.room.detail import android.net.Uri -import android.text.TextUtils import androidx.annotation.IdRes import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -379,7 +378,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro val document = parser.parse(finalText) val renderer = HtmlRenderer.builder().build() val htmlText = renderer.render(document) - if (TextUtils.equals(finalText, htmlText)) { + if (finalText == htmlText) { room.sendTextMessage(finalText) } else { room.sendFormattedTextMessage(finalText, htmlText) @@ -404,19 +403,22 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() - val quotedTextMsg = StringBuilder() - if (messageParagraphs != null) { - for (i in messageParagraphs.indices) { - if (messageParagraphs[i].trim() != "") { - quotedTextMsg.append("> ").append(messageParagraphs[i]) - } + return buildString { + if (messageParagraphs != null) { + for (i in messageParagraphs.indices) { + if (messageParagraphs[i].isNotBlank()) { + append("> ") + append(messageParagraphs[i]) + } - if (i + 1 != messageParagraphs.size) { - quotedTextMsg.append("\n\n") + if (i != messageParagraphs.lastIndex) { + append("\n\n") + } } } + append("\n\n") + append(myText) } - return "$quotedTextMsg\n\n$myText" } private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 695ab53812..cb74b1144d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -131,8 +131,8 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room setState { copy( - joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(roomId) }, - rejectingErrorRoomsIds = rejectingErrorRoomsIds.toMutableSet().apply { remove(roomId) } + joiningRoomsIds = joiningRoomsIds + roomId, + rejectingErrorRoomsIds = rejectingErrorRoomsIds - roomId ) } @@ -148,8 +148,8 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room setState { copy( - joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(roomId) }, - joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(roomId) } + joiningRoomsIds = joiningRoomsIds - roomId, + joiningErrorRoomsIds = joiningErrorRoomsIds + roomId ) } } @@ -167,8 +167,8 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room setState { copy( - rejectingRoomsIds = rejectingRoomsIds.toMutableSet().apply { add(roomId) }, - joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { remove(roomId) } + rejectingRoomsIds = rejectingRoomsIds + roomId, + joiningErrorRoomsIds = joiningErrorRoomsIds - roomId ) } @@ -186,8 +186,8 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room setState { copy( - rejectingRoomsIds = rejectingRoomsIds.toMutableSet().apply { remove(roomId) }, - rejectingErrorRoomsIds = rejectingErrorRoomsIds.toMutableSet().apply { add(roomId) } + rejectingRoomsIds = rejectingRoomsIds - roomId, + rejectingErrorRoomsIds = rejectingErrorRoomsIds + roomId ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/homeserver/ServerUrlsRepository.kt b/vector/src/main/java/im/vector/riotx/features/homeserver/ServerUrlsRepository.kt index 23d6c09ec2..9535499d70 100644 --- a/vector/src/main/java/im/vector/riotx/features/homeserver/ServerUrlsRepository.kt +++ b/vector/src/main/java/im/vector/riotx/features/homeserver/ServerUrlsRepository.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.homeserver import android.content.Context -import android.text.TextUtils import androidx.core.content.edit import androidx.preference.PreferenceManager import im.vector.riotx.R @@ -41,11 +40,11 @@ object ServerUrlsRepository { fun setDefaultUrlsFromReferrer(context: Context, homeServerUrl: String, identityServerUrl: String) { PreferenceManager.getDefaultSharedPreferences(context) .edit { - if (!TextUtils.isEmpty(homeServerUrl)) { + if (homeServerUrl.isNotEmpty()) { putString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF, homeServerUrl) } - if (!TextUtils.isEmpty(identityServerUrl)) { + if (identityServerUrl.isNotEmpty()) { putString(DEFAULT_REFERRER_IDENTITY_SERVER_URL_PREF, identityServerUrl) } } diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt index 3aaaa82b49..187566f660 100755 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt @@ -16,7 +16,6 @@ package im.vector.riotx.features.rageshake -import android.text.TextUtils import android.view.Menu import android.view.MenuItem import android.widget.Toast @@ -122,7 +121,7 @@ class BugReportActivity : VectorBaseActivity() { object : BugReporter.IMXBugReportListener { override fun onUploadFailed(reason: String?) { try { - if (!TextUtils.isEmpty(reason)) { + if (!reason.isNullOrEmpty()) { if (forSuggestion) { Toast.makeText(this@BugReportActivity, getString(R.string.send_suggestion_failed, reason), Toast.LENGTH_LONG).show() diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt index e358823316..9a7707d063 100755 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt @@ -25,7 +25,6 @@ import android.content.Intent import android.graphics.Bitmap import android.os.AsyncTask import android.os.Build -import android.text.TextUtils import android.view.View import im.vector.matrix.android.api.Matrix import im.vector.riotx.BuildConfig @@ -166,14 +165,11 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes if (withDevicesLogs) { val files = vectorFileLogger.getLogFiles() - - for (f in files) { + files.mapNotNullTo(gzippedFiles) { f -> if (!mIsCancelled) { - val gzippedFile = compressFile(f) - - if (null != gzippedFile) { - gzippedFiles.add(gzippedFile) - } + compressFile(f) + } else { + null } } } @@ -244,7 +240,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) val buildNumber = context.getString(R.string.build_number) - if (!TextUtils.isEmpty(buildNumber) && buildNumber != "0") { + if (buildNumber.isNotEmpty() && buildNumber != "0") { builder.addFormDataPart("build_number", buildNumber) } @@ -266,10 +262,9 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes } try { - val fos = FileOutputStream(logCatScreenshotFile) - bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos) - fos.flush() - fos.close() + logCatScreenshotFile.outputStream().use { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + } builder.addFormDataPart("file", logCatScreenshotFile.name, logCatScreenshotFile.asRequestBody("application/octet-stream".toMediaTypeOrNull())) @@ -303,16 +298,14 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes // add a progress listener requestBody.setWriteListener { totalWritten, contentLength -> - val percentage: Int - - if (-1L != contentLength) { + val percentage = if (-1L != contentLength) { if (totalWritten > contentLength) { - percentage = 100 + 100 } else { - percentage = (totalWritten * 100 / contentLength).toInt() + (totalWritten * 100 / contentLength).toInt() } } else { - percentage = 0 + 0 } if (mIsCancelled && null != mBugReportCall) { @@ -350,19 +343,18 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes } else if (null == response || null == response.body) { serverError = "Failed with error $responseCode" } else { - var inputStream: InputStream? = null - try { - inputStream = response.body!!.byteStream() + val inputStream = response.body!!.byteStream() - var ch = inputStream.read() - val b = StringBuilder() - while (ch != -1) { - b.append(ch.toChar()) - ch = inputStream.read() + serverError = inputStream.use { + buildString { + var ch = it.read() + while (ch != -1) { + append(ch.toChar()) + ch = it.read() + } + } } - serverError = b.toString() - inputStream.close() // check if the error message try { @@ -378,12 +370,6 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes } } catch (e: Exception) { Timber.e(e, "## sendBugReport() : failed to parse error") - } finally { - try { - inputStream?.close() - } catch (e: Exception) { - Timber.e(e, "## sendBugReport() : failed to close the error stream") - } } } } @@ -481,15 +467,9 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes crashFile.delete() } - if (!TextUtils.isEmpty(crashDescription)) { + if (crashDescription.isNotEmpty()) { try { - val fos = FileOutputStream(crashFile) - val osw = OutputStreamWriter(fos) - osw.write(crashDescription) - osw.close() - - fos.flush() - fos.close() + crashFile.writeText(crashDescription) } catch (e: Exception) { Timber.e(e, "## saveCrashReport() : fail to write $e") } @@ -503,25 +483,17 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes * @return the crash description */ private fun getCrashDescription(context: Context): String? { - var crashDescription: String? = null val crashFile = getCrashFile(context) if (crashFile.exists()) { try { - val fis = FileInputStream(crashFile) - val isr = InputStreamReader(fis) - - val buffer = CharArray(fis.available()) - val len = isr.read(buffer, 0, fis.available()) - crashDescription = String(buffer, 0, len) - isr.close() - fis.close() + return crashFile.readText() } catch (e: Exception) { Timber.e(e, "## getCrashDescription() : fail to read $e") } } - return crashDescription + return null } // ============================================================================================================== @@ -589,13 +561,9 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes } try { - val fos = FileOutputStream(logCatErrFile) - val osw = OutputStreamWriter(fos) - getLogCatError(osw, isErrorLogcat) - osw.close() - - fos.flush() - fos.close() + logCatErrFile.writer().use { + getLogCatError(it, isErrorLogcat) + } return compressFile(logCatErrFile) } catch (error: OutOfMemoryError) { @@ -622,26 +590,17 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes return } - var reader: BufferedReader? = null try { val separator = System.getProperty("line.separator") - reader = BufferedReader(InputStreamReader(logcatProc.inputStream), BUFFER_SIZE) - var line = reader.readLine() - while (line != null) { - streamWriter.append(line) - streamWriter.append(separator) - line = reader.readLine() - } + logcatProc.inputStream + .reader() + .buffered(BUFFER_SIZE) + .forEachLine { line -> + streamWriter.append(line) + streamWriter.append(separator) + } } catch (e: IOException) { Timber.e(e, "getLog fails") - } finally { - if (reader != null) { - try { - reader.close() - } catch (e: IOException) { - Timber.e(e, "getLog fails with") - } - } } } @@ -658,45 +617,25 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes private fun compressFile(fin: File): File? { Timber.v("## compressFile() : compress ${fin.name}") - val dstFile = File(fin.parent, fin.name + ".gz") + val dstFile = fin.resolveSibling(fin.name + ".gz") if (dstFile.exists()) { dstFile.delete() } - var fos: FileOutputStream? = null - var gos: GZIPOutputStream? = null - var inputStream: InputStream? = null try { - fos = FileOutputStream(dstFile) - gos = GZIPOutputStream(fos) - - inputStream = FileInputStream(fin) - - val buffer = ByteArray(2048) - var n = inputStream.read(buffer) - while (n != -1) { - gos.write(buffer, 0, n) - n = inputStream.read(buffer) + GZIPOutputStream(dstFile.outputStream()).use { gos -> + fin.inputStream().use { + it.copyTo(gos, 2048) + } } - gos.close() - inputStream.close() - Timber.v("## compressFile() : ${fin.length()} compressed to ${dstFile.length()} bytes") return dstFile } catch (e: Exception) { Timber.e(e, "## compressFile() failed") } catch (oom: OutOfMemoryError) { Timber.e(oom, "## compressFile() failed") - } finally { - try { - fos?.close() - gos?.close() - inputStream?.close() - } catch (e: Exception) { - Timber.e(e, "## compressFile() failed to close inputStream") - } } return null diff --git a/vector/src/main/java/im/vector/riotx/features/settings/FontScale.kt b/vector/src/main/java/im/vector/riotx/features/settings/FontScale.kt index c68edac6ce..a9e797ba7a 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/FontScale.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/FontScale.kt @@ -18,7 +18,6 @@ package im.vector.riotx.features.settings import android.content.Context import android.content.res.Configuration -import android.text.TextUtils import androidx.core.content.edit import androidx.preference.PreferenceManager import im.vector.riotx.R @@ -68,7 +67,7 @@ object FontScale { val preferences = PreferenceManager.getDefaultSharedPreferences(context) var scalePreferenceValue: String - if (!preferences.contains(APPLICATION_FONT_SCALE_KEY)) { + if (APPLICATION_FONT_SCALE_KEY !in preferences) { val fontScale = context.resources.configuration.fontScale scalePreferenceValue = FONT_SCALE_NORMAL @@ -96,9 +95,9 @@ object FontScale { val fontScale = getFontScalePrefValue(context) if (fontScaleToPrefValue.containsValue(fontScale)) { - for (entry in fontScaleToPrefValue) { - if (TextUtils.equals(entry.value, fontScale)) { - return entry.key + for ((key, value) in fontScaleToPrefValue) { + if (value == fontScale) { + return key } } } @@ -125,9 +124,9 @@ object FontScale { * @param fontScaleDescription the font scale description */ fun updateFontScale(context: Context, fontScaleDescription: String) { - for (entry in prefValueToNameResId) { - if (TextUtils.equals(context.getString(entry.value), fontScaleDescription)) { - saveFontScale(context, entry.key) + for ((key, value) in prefValueToNameResId) { + if (context.getString(value) == fontScaleDescription) { + saveFontScale(context, key) } } @@ -143,7 +142,7 @@ object FontScale { * @param scaleValue the text scale */ fun saveFontScale(context: Context, scaleValue: String) { - if (!TextUtils.isEmpty(scaleValue)) { + if (scaleValue.isNotEmpty()) { PreferenceManager.getDefaultSharedPreferences(context) .edit { putString(APPLICATION_FONT_SCALE_KEY, scaleValue) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt index eae21cf5d4..7fef12cddf 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt @@ -20,15 +20,13 @@ import android.content.Context import android.content.res.Configuration import android.os.Build import android.preference.PreferenceManager -import android.text.TextUtils -import android.util.Pair import androidx.core.content.edit import im.vector.riotx.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber -import java.util.* +import java.util.Locale /** * Object to manage the Locale choice of the user @@ -68,7 +66,7 @@ object VectorLocale { // detect if the default language is used val defaultStringValue = getString(context, defaultLocale, R.string.resources_country_code) - if (TextUtils.equals(defaultStringValue, getString(context, applicationLocale, R.string.resources_country_code))) { + if (defaultStringValue == getString(context, applicationLocale, R.string.resources_country_code)) { applicationLocale = defaultLocale } @@ -89,21 +87,21 @@ object VectorLocale { PreferenceManager.getDefaultSharedPreferences(context).edit { val language = locale.language - if (TextUtils.isEmpty(language)) { + if (language.isEmpty()) { remove(APPLICATION_LOCALE_LANGUAGE_KEY) } else { putString(APPLICATION_LOCALE_LANGUAGE_KEY, language) } val country = locale.country - if (TextUtils.isEmpty(country)) { + if (country.isEmpty()) { remove(APPLICATION_LOCALE_COUNTRY_KEY) } else { putString(APPLICATION_LOCALE_COUNTRY_KEY, country) } val variant = locale.variant - if (TextUtils.isEmpty(variant)) { + if (variant.isEmpty()) { remove(APPLICATION_LOCALE_VARIANT_KEY) } else { putString(APPLICATION_LOCALE_VARIANT_KEY, variant) @@ -120,17 +118,17 @@ object VectorLocale { * @return the localized string */ private fun getString(context: Context, locale: Locale, resourceId: Int): String { - var result: String + val result: String if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { val config = Configuration(context.resources.configuration) config.setLocale(locale) - try { - result = context.createConfigurationContext(config).getText(resourceId).toString() + result = try { + context.createConfigurationContext(config).getText(resourceId).toString() } catch (e: Exception) { Timber.e(e, "## getString() failed") // use the default one - result = context.getString(resourceId) + context.getString(resourceId) } } else { val resources = context.resources @@ -177,8 +175,8 @@ object VectorLocale { supportedLocales.clear() - for (knownLocale in knownLocalesSet) { - supportedLocales.add(Locale(knownLocale.first, knownLocale.second)) + knownLocalesSet.mapTo(supportedLocales) { (language, country) -> + Locale(language, country) } // sort by human display names @@ -194,7 +192,7 @@ object VectorLocale { fun localeToLocalisedString(locale: Locale): String { var res = locale.getDisplayLanguage(locale) - if (!TextUtils.isEmpty(locale.getDisplayCountry(locale))) { + if (locale.getDisplayCountry(locale).isNotEmpty()) { res += " (" + locale.getDisplayCountry(locale) + ")" } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt index 4ad3f54e30..f9601265d3 100755 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt @@ -18,11 +18,9 @@ package im.vector.riotx.features.settings import android.content.Context -import android.database.Cursor import android.media.RingtoneManager import android.net.Uri import android.provider.MediaStore -import android.text.TextUtils import androidx.core.content.edit import androidx.preference.PreferenceManager import im.vector.riotx.R @@ -30,7 +28,6 @@ import im.vector.riotx.features.homeserver.ServerUrlsRepository import im.vector.riotx.features.themes.ThemeUtils import timber.log.Timber import java.io.File -import java.util.* import javax.inject.Inject class VectorPreferences @Inject constructor(private val context: Context) { @@ -173,7 +170,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { private const val MEDIA_SAVING_FOREVER = 3 // some preferences keys must be kept after a logout - private val mKeysToKeepAfterLogout = Arrays.asList( + private val mKeysToKeepAfterLogout = listOf( SETTINGS_DEFAULT_MEDIA_COMPRESSION_KEY, SETTINGS_DEFAULT_MEDIA_SOURCE_KEY, SETTINGS_PLAY_SHUTTER_SOUND_KEY, @@ -394,7 +391,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { val url = defaultPrefs.getString(SETTINGS_NOTIFICATION_RINGTONE_PREFERENCE_KEY, null) // the user selects "None" - if (TextUtils.equals(url, "")) { + if (url == "") { return null } @@ -425,29 +422,18 @@ class VectorPreferences @Inject constructor(private val context: Context) { fun getNotificationRingToneName(): String? { val toneUri = getNotificationRingTone() ?: return null - var name: String? = null - - var cursor: Cursor? = null - try { val proj = arrayOf(MediaStore.Audio.Media.DATA) - cursor = context.contentResolver.query(toneUri, proj, null, null, null) - val column_index = cursor!!.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) - cursor.moveToFirst() - - val file = File(cursor.getString(column_index)) - name = file.name - - if (name!!.contains(".")) { - name = name.substring(0, name.lastIndexOf(".")) + return context.contentResolver.query(toneUri, proj, null, null, null)?.use { + val columnIndex = it.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) + it.moveToFirst() + File(it.getString(columnIndex)).nameWithoutExtension } } catch (e: Exception) { Timber.e(e, "## getNotificationRingToneName() failed") - } finally { - cursor?.close() } - return name + return null } /** @@ -553,16 +539,13 @@ class VectorPreferences @Inject constructor(private val context: Context) { * @return the min last access time (in seconds) */ fun getMinMediasLastAccessTime(): Long { - val selection = getSelectedMediasSavingPeriod() - - when (selection) { - MEDIA_SAVING_3_DAYS -> return System.currentTimeMillis() / 1000 - 3 * 24 * 60 * 60 - MEDIA_SAVING_1_WEEK -> return System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60 - MEDIA_SAVING_1_MONTH -> return System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60 - MEDIA_SAVING_FOREVER -> return 0 + return when (getSelectedMediasSavingPeriod()) { + MEDIA_SAVING_3_DAYS -> System.currentTimeMillis() / 1000 - 3 * 24 * 60 * 60 + MEDIA_SAVING_1_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60 + MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60 + MEDIA_SAVING_FOREVER -> 0 + else -> 0 } - - return 0 } /** @@ -571,15 +554,13 @@ class VectorPreferences @Inject constructor(private val context: Context) { * @return the selected period */ fun getSelectedMediasSavingPeriodString(): String { - val selection = getSelectedMediasSavingPeriod() - - when (selection) { - MEDIA_SAVING_3_DAYS -> return context.getString(R.string.media_saving_period_3_days) - MEDIA_SAVING_1_WEEK -> return context.getString(R.string.media_saving_period_1_week) - MEDIA_SAVING_1_MONTH -> return context.getString(R.string.media_saving_period_1_month) - MEDIA_SAVING_FOREVER -> return context.getString(R.string.media_saving_period_forever) + return when (getSelectedMediasSavingPeriod()) { + MEDIA_SAVING_3_DAYS -> context.getString(R.string.media_saving_period_3_days) + MEDIA_SAVING_1_WEEK -> context.getString(R.string.media_saving_period_1_week) + MEDIA_SAVING_1_MONTH -> context.getString(R.string.media_saving_period_1_month) + MEDIA_SAVING_FOREVER -> context.getString(R.string.media_saving_period_forever) + else -> "?" } - return "?" } /** diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsPreferencesFragment.kt index 8c09faf73a..ab81e6937e 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsPreferencesFragment.kt @@ -19,7 +19,6 @@ package im.vector.riotx.features.settings import android.app.Activity import android.content.Context import android.content.Intent -import android.text.TextUtils import android.widget.CheckedTextView import android.widget.LinearLayout import androidx.appcompat.app.AlertDialog @@ -194,7 +193,7 @@ class VectorSettingsPreferencesFragment : VectorSettingsBaseFragment() { val v = linearLayout.getChildAt(i) if (v is CheckedTextView) { - v.isChecked = TextUtils.equals(v.text, scaleText) + v.isChecked = v.text == scaleText v.setOnClickListener { dialog.dismiss() diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt index dfc7004554..c89b8435f9 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -21,7 +21,6 @@ import android.app.Activity import android.content.DialogInterface import android.content.Intent import android.graphics.Typeface -import android.text.TextUtils import android.view.KeyEvent import android.widget.Button import android.widget.EditText @@ -69,7 +68,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { private var mAccountPassword: String = "" // devices: device IDs and device names - private var mDevicesNameList: List = ArrayList() + private val mDevicesNameList: MutableList = mutableListOf() private var mMyDeviceInfo: DeviceInfo? = null @@ -308,7 +307,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { passPhraseEditText.addTextChangedListener(object : SimpleTextWatcher() { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - importButton.isEnabled = !TextUtils.isEmpty(passPhraseEditText.text) + importButton.isEnabled = !passPhraseEditText.text.isNullOrEmpty() } }) @@ -393,20 +392,20 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { } // crypto section: device ID - if (!TextUtils.isEmpty(deviceId)) { + if (!deviceId.isNullOrEmpty()) { cryptoInfoDeviceIdPreference.summary = deviceId cryptoInfoDeviceIdPreference.setOnPreferenceClickListener { - activity?.let { copyToClipboard(it, deviceId!!) } + activity?.let { copyToClipboard(it, deviceId) } true } } // crypto section: device key (fingerprint) - if (!TextUtils.isEmpty(deviceId) && !TextUtils.isEmpty(userId)) { + if (!deviceId.isNullOrEmpty() && userId.isNotEmpty()) { val deviceInfo = session.getDeviceInfo(userId, deviceId) - if (null != deviceInfo && !TextUtils.isEmpty(deviceInfo.fingerprint())) { + if (null != deviceInfo && !deviceInfo.fingerprint().isNullOrEmpty()) { cryptoInfoTextPreference.summary = deviceInfo.getFingerprintHumanReadable() cryptoInfoTextPreference.setOnPreferenceClickListener { @@ -446,7 +445,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { * It can be any mobile device, as any browser. */ private fun refreshDevicesList() { - if (session.isCryptoEnabled() && !TextUtils.isEmpty(session.sessionParams.credentials.deviceId)) { + if (session.isCryptoEnabled() && !session.sessionParams.credentials.deviceId.isNullOrEmpty()) { // display a spinner while loading the devices list if (0 == mDevicesListSettingsCategory.preferenceCount) { activity?.let { @@ -502,7 +501,8 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { if (isNewList) { var prefIndex = 0 - mDevicesNameList = aDeviceInfoList + mDevicesNameList.clear() + mDevicesNameList.addAll(aDeviceInfoList) // sort before display: most recent first mDevicesNameList.sortByLastSeen() @@ -570,7 +570,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { // device name textView = layout.findViewById(R.id.device_name) - val displayName = if (TextUtils.isEmpty(aDeviceInfo.displayName)) LABEL_UNAVAILABLE_DATA else aDeviceInfo.displayName + val displayName = if (aDeviceInfo.displayName.isNullOrEmpty()) LABEL_UNAVAILABLE_DATA else aDeviceInfo.displayName textView.text = displayName // last seen info @@ -598,7 +598,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { .setPositiveButton(R.string.rename) { _, _ -> displayDeviceRenameDialog(aDeviceInfo) } // disable the deletion for our own device - if (!TextUtils.equals(session.getMyDevice().deviceId, aDeviceInfo.deviceId)) { + if (session.getMyDevice().deviceId != aDeviceInfo.deviceId) { builder.setNegativeButton(R.string.delete) { _, _ -> deleteDevice(aDeviceInfo) } } @@ -645,13 +645,13 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { for (i in 0 until count) { val pref = mDevicesListSettingsCategory.getPreference(i) - if (TextUtils.equals(aDeviceInfoToRename.deviceId, pref.title)) { + if (aDeviceInfoToRename.deviceId == pref.title) { pref.summary = newName } } // detect if the updated device is the current account one - if (TextUtils.equals(cryptoInfoDeviceIdPreference.summary, aDeviceInfoToRename.deviceId)) { + if (cryptoInfoDeviceIdPreference.summary == aDeviceInfoToRename.deviceId) { cryptoInfoDeviceNamePreference.summary = newName } @@ -716,7 +716,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { * Show a dialog to ask for user password, or use a previously entered password. */ private fun maybeShowDeleteDeviceWithPasswordDialog(deviceId: String, authSession: String?) { - if (!TextUtils.isEmpty(mAccountPassword)) { + if (mAccountPassword.isNotEmpty()) { deleteDeviceWithPassword(deviceId, authSession, mAccountPassword) } else { activity?.let { @@ -729,7 +729,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() { .setTitle(R.string.devices_delete_dialog_title) .setView(layout) .setPositiveButton(R.string.devices_delete_submit_button_label, DialogInterface.OnClickListener { _, _ -> - if (TextUtils.isEmpty(passwordEditText.toString())) { + if (passwordEditText.toString().isEmpty()) { it.toast(R.string.error_empty_field_your_password) return@OnClickListener }