diff --git a/CHANGES.md b/CHANGES.md index c2c287eb20..936e6b0ffe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,11 +7,13 @@ Features ✨: Improvements 🙌: - Open an existing DM instead of creating a new one (#2319) - Ask for explicit user consent to send their contact details to the identity server (#2375) + - Handle events of type "m.room.server_acl" (#890) Bugfix 🐛: - Fix issue when restoring draft after sharing (#2287) - Fix issue when updating the avatar of a room (new avatar vanishing) - Discard change dialog displayed by mistake when avatar has been updated + - Registration: annoying error message scares every new user when they add an email (#2391) Translations 🗣: - diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 1a9165ade4..cbb22daf0f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -68,8 +68,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { if (encryptedRoom) { val room = aliceSession.getRoom(roomId)!! - mTestHelper.doSync { - room.enableEncryption(callback = it) + mTestHelper.runBlockingTest { + room.enableEncryption() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt index a17e65b8e0..e264843ea4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/extensions/Strings.kt @@ -22,3 +22,8 @@ fun CharSequence.ensurePrefix(prefix: CharSequence): CharSequence { else -> "$prefix$this" } } + +/** + * Append a new line and then the provided string + */ +fun StringBuilder.appendNl(str: String) = append("\n").append(str) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 82dea81a5b..0a7f3ff09f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -56,6 +56,7 @@ object EventType { const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups" const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events" const val STATE_ROOM_ENCRYPTION = "m.room.encryption" + const val STATE_ROOM_SERVER_ACL = "m.room.server_acl" // Call Events const val CALL_INVITE = "m.call.invite" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt index e7e6bacc22..1251fd9857 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.session.room.crypto -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM interface RoomCryptoService { @@ -30,6 +29,5 @@ interface RoomCryptoService { /** * Enable encryption of the room */ - fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM, - callback: MatrixCallback) + suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt new file mode 100644 index 0000000000..92078054b7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomServerAclContent.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing the EventType.STATE_ROOM_SERVER_ACL state event content + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl + */ +@JsonClass(generateAdapter = true) +data class RoomServerAclContent( + /** + * True to allow server names that are IP address literals. False to deny. + * Defaults to true if missing or otherwise not a boolean. + * This is strongly recommended to be set to false as servers running with IP literal names are strongly + * discouraged in order to require legitimate homeservers to be backed by a valid registered domain name. + */ + @Json(name = "allow_ip_literals") + val allowIpLiterals: Boolean = true, + + /** + * The server names to allow in the room, excluding any port information. Wildcards may be used to cover + * a wider range of hosts, where * matches zero or more characters and ? matches exactly one character. + * + * This defaults to an empty list when not provided, effectively disallowing every server. + */ + @Json(name = "allow") + val allowList: List = emptyList(), + + /** + * The server names to disallow in the room, excluding any port information. Wildcards may be used to cover + * a wider range of hosts, where * matches zero or more characters and ? matches exactly one character. + * + * This defaults to an empty list when not provided. + */ + @Json(name = "deny") + val denyList: List = emptyList() + +) { + companion object { + const val ALL = "*" + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt index 32d6033578..eb822c68ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/notification/RoomPushRuleService.kt @@ -17,12 +17,10 @@ package org.matrix.android.sdk.api.session.room.notification import androidx.lifecycle.LiveData -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable interface RoomPushRuleService { fun getLiveRoomNotificationState(): LiveData - fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback): Cancelable + suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt index 0ccdfd1d3c..a444e2346e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.room.reporting -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to report content of an event. */ @@ -28,5 +25,5 @@ interface ReportingService { * Report content * Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid */ - fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback): Cancelable + suspend fun reportContent(eventId: String, score: Int, reason: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt index 3278c640de..69fde61f90 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.room.tags -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to handle tags of a room. It's implemented at the room level. */ @@ -26,10 +23,10 @@ interface TagsService { /** * Add a tag to a room */ - fun addTag(tag: String, order: Double?, callback: MatrixCallback): Cancelable + suspend fun addTag(tag: String, order: Double?) /** * Remove tag from a room */ - fun deleteTag(tag: String, callback: MatrixCallback): Cancelable + suspend fun deleteTag(tag: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 1338df6878..c7bb640f7c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -101,13 +101,13 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, return cryptoService.shouldEncryptForInvitedMembers(roomId) } - override fun enableEncryption(algorithm: String, callback: MatrixCallback) { + override suspend fun enableEncryption(algorithm: String) { when { isEncrypted() -> { - callback.onFailure(IllegalStateException("Encryption is already enabled for this room")) + throw IllegalStateException("Encryption is already enabled for this room") } algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> { - callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")) + throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported") } else -> { val params = SendStateTask.Params( @@ -118,11 +118,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, "algorithm" to algorithm )) - sendStateTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + sendStateTask.execute(params) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt index 8797b0c764..67ae55c066 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt @@ -21,21 +21,16 @@ import androidx.lifecycle.Transformations import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.model.PushRuleEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String, private val setRoomNotificationStateTask: SetRoomNotificationStateTask, - @SessionDatabase private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor) + @SessionDatabase private val monarchy: Monarchy) : RoomPushRuleService { @AssistedInject.Factory @@ -49,12 +44,8 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted } } - override fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback): Cancelable { - return setRoomNotificationStateTask - .configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) { - this.callback = matrixCallback - } - .executeBy(taskExecutor) + override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) { + setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) } private fun getPushRuleForRoom(): LiveData { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt index 384c544ee0..cac87a9d30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt @@ -18,14 +18,9 @@ package org.matrix.android.sdk.internal.session.room.reporting import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.reporting.ReportingService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String, - private val taskExecutor: TaskExecutor, private val reportContentTask: ReportContentTask ) : ReportingService { @@ -34,13 +29,8 @@ internal class DefaultReportingService @AssistedInject constructor(@Assisted pri fun create(roomId: String): ReportingService } - override fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback): Cancelable { + override suspend fun reportContent(eventId: String, score: Int, reason: String) { val params = ReportContentTask.Params(roomId, eventId, score, reason) - - return reportContentTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + reportContentTask.execute(params) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt index 932cb5d67e..d6c02f0a49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt @@ -18,15 +18,10 @@ package org.matrix.android.sdk.internal.session.room.tags import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.tags.TagsService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultTagsService @AssistedInject constructor( @Assisted private val roomId: String, - private val taskExecutor: TaskExecutor, private val addTagToRoomTask: AddTagToRoomTask, private val deleteTagFromRoomTask: DeleteTagFromRoomTask ) : TagsService { @@ -36,21 +31,13 @@ internal class DefaultTagsService @AssistedInject constructor( fun create(roomId: String): TagsService } - override fun addTag(tag: String, order: Double?, callback: MatrixCallback): Cancelable { + override suspend fun addTag(tag: String, order: Double?) { val params = AddTagToRoomTask.Params(roomId, tag, order) - return addTagToRoomTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + addTagToRoomTask.execute(params) } - override fun deleteTag(tag: String, callback: MatrixCallback): Cancelable { + override suspend fun deleteTag(tag: String) { val params = DeleteTagFromRoomTask.Params(roomId, tag) - return deleteTagFromRoomTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + deleteTagFromRoomTask.execute(params) } } diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index 27f083269f..de30a64c32 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -72,6 +72,23 @@ You upgraded this room. %s upgraded here. You upgraded here. + %s set the server ACLs for this room. + You set the server ACLs for this room. + • Server matching %s are banned. + • Server matching %s are allowed. + • Server matching IP literals are allowed. + • Server matching IP literals are banned. + + %s changed the server ACLs for this room. + You changed the server ACLs for this room. + • Server matching %s are now banned. + • Server matching %s were removed from the ban list. + • Server matching %s are now allowed. + • Server matching %s were removed from the allowed list. + • Server matching IP literals are now allowed. + • Server matching IP literals are now banned. + No change. + 🎉 All servers are banned from participating! This room can no longer be used. %1$s requested a VoIP conference You requested a VoIP conference diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 102a0673d4..1f22406883 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -99,6 +99,7 @@ import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap import timber.log.Timber import java.io.File +import java.lang.Exception import java.util.UUID import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -1112,15 +1113,15 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleReportContent(action: RoomDetailAction.ReportContent) { - room.reportContent(action.eventId, -100, action.reason, object : MatrixCallback { - override fun onSuccess(data: Unit) { - _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action)) + viewModelScope.launch { + val event = try { + room.reportContent(action.eventId, -100, action.reason) + RoomDetailViewEvents.ActionSuccess(action) + } catch (failure: Exception) { + RoomDetailViewEvents.ActionFailure(action, failure) } - - override fun onFailure(failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure)) - } - }) + _viewEvents.post(event) + } } private fun handleIgnoreUser(action: RoomDetailAction.IgnoreUser) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 0d1e2261cd..8b0b905805 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -186,6 +186,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted EventType.STATE_ROOM_ALIASES, EventType.STATE_ROOM_CANONICAL_ALIAS, EventType.STATE_ROOM_HISTORY_VISIBILITY, + EventType.STATE_ROOM_SERVER_ACL, EventType.CALL_INVITE, EventType.CALL_CANDIDATES, EventType.CALL_HANGUP, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 1a4db3bdfc..575f28b610 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -57,6 +57,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.STATE_ROOM_CANONICAL_ALIAS, EventType.STATE_ROOM_JOIN_RULES, EventType.STATE_ROOM_HISTORY_VISIBILITY, + EventType.STATE_ROOM_SERVER_ACL, EventType.STATE_ROOM_GUEST_ACCESS, EventType.STATE_ROOM_WIDGET_LEGACY, EventType.STATE_ROOM_WIDGET, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 8055ef9a99..c4cc2e87b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -19,6 +19,8 @@ package im.vector.app.features.home.room.detail.timeline.format import im.vector.app.ActiveSessionDataSource import im.vector.app.R import im.vector.app.core.resources.StringProvider +import im.vector.app.features.settings.VectorPreferences +import org.matrix.android.sdk.api.extensions.appendNl import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -35,6 +37,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomNameContent +import org.matrix.android.sdk.api.session.room.model.RoomServerAclContent import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.RoomTopicContent @@ -48,9 +51,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent import timber.log.Timber import javax.inject.Inject -class NoticeEventFormatter @Inject constructor(private val activeSessionDataSource: ActiveSessionDataSource, - private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter, - private val sp: StringProvider) { +class NoticeEventFormatter @Inject constructor( + private val activeSessionDataSource: ActiveSessionDataSource, + private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter, + private val vectorPreferences: VectorPreferences, + private val sp: StringProvider +) { private val currentUserId: String? get() = activeSessionDataSource.currentValue?.orNull()?.myUserId @@ -72,6 +78,7 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour EventType.STATE_ROOM_CANONICAL_ALIAS -> formatRoomCanonicalAliasEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) + EventType.STATE_ROOM_SERVER_ACL -> formatRoomServerAclEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_GUEST_ACCESS -> formatRoomGuestAccessEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) EventType.STATE_ROOM_ENCRYPTION -> formatRoomEncryptionEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_WIDGET, @@ -383,6 +390,79 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } + private fun formatRoomServerAclEvent(event: Event, senderName: String?): String? { + val eventContent = event.getClearContent().toModel() ?: return null + val prevEventContent = event.resolvedPrevContent()?.toModel() + + return buildString { + // Title + append(if (prevEventContent == null) { + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_server_acl_set_title_by_you) + } else { + sp.getString(R.string.notice_room_server_acl_set_title, senderName) + } + } else { + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_server_acl_updated_title_by_you) + } else { + sp.getString(R.string.notice_room_server_acl_updated_title, senderName) + } + }) + if (eventContent.allowList.isEmpty()) { + // Special case for stuck room + appendNl(sp.getString(R.string.notice_room_server_acl_allow_is_empty)) + } else if (vectorPreferences.developerMode()) { + // Details, only in developer mode + appendAclDetails(eventContent, prevEventContent) + } + } + } + + private fun StringBuilder.appendAclDetails(eventContent: RoomServerAclContent, prevEventContent: RoomServerAclContent?) { + if (prevEventContent == null) { + eventContent.allowList.forEach { appendNl(sp.getString(R.string.notice_room_server_acl_set_allowed, it)) } + eventContent.denyList.forEach { appendNl(sp.getString(R.string.notice_room_server_acl_set_banned, it)) } + if (eventContent.allowIpLiterals) { + appendNl(sp.getString(R.string.notice_room_server_acl_set_ip_literals_allowed)) + } else { + appendNl(sp.getString(R.string.notice_room_server_acl_set_ip_literals_not_allowed)) + } + } else { + // Display only diff + var hasChanged = false + // New allowed servers + (eventContent.allowList - prevEventContent.allowList) + .also { hasChanged = hasChanged || it.isNotEmpty() } + .forEach { appendNl(sp.getString(R.string.notice_room_server_acl_updated_allowed, it)) } + // Removed allowed servers + (prevEventContent.allowList - eventContent.allowList) + .also { hasChanged = hasChanged || it.isNotEmpty() } + .forEach { appendNl(sp.getString(R.string.notice_room_server_acl_updated_was_allowed, it)) } + // New denied servers + (eventContent.denyList - prevEventContent.denyList) + .also { hasChanged = hasChanged || it.isNotEmpty() } + .forEach { appendNl(sp.getString(R.string.notice_room_server_acl_updated_banned, it)) } + // Removed denied servers + (prevEventContent.denyList - eventContent.denyList) + .also { hasChanged = hasChanged || it.isNotEmpty() } + .forEach { appendNl(sp.getString(R.string.notice_room_server_acl_updated_was_banned, it)) } + + if (prevEventContent.allowIpLiterals != eventContent.allowIpLiterals) { + hasChanged = true + if (eventContent.allowIpLiterals) { + appendNl(sp.getString(R.string.notice_room_server_acl_updated_ip_literals_allowed)) + } else { + appendNl(sp.getString(R.string.notice_room_server_acl_updated_ip_literals_not_allowed)) + } + } + + if (!hasChanged) { + appendNl(sp.getString(R.string.notice_room_server_acl_updated_no_change)) + } + } + } + private fun formatRoomCanonicalAliasEvent(event: Event, senderName: String?): String? { val eventContent: RoomCanonicalAliasContent? = event.getClearContent().toModel() val canonicalAlias = eventContent?.canonicalAlias diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt index 14b8c12fee..4fcac6c7f7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt @@ -33,6 +33,7 @@ object TimelineDisplayableEvents { EventType.STATE_ROOM_ALIASES, EventType.STATE_ROOM_CANONICAL_ALIAS, EventType.STATE_ROOM_HISTORY_VISIBILITY, + EventType.STATE_ROOM_SERVER_ACL, EventType.STATE_ROOM_POWER_LEVELS, EventType.CALL_INVITE, EventType.CALL_HANGUP, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index c32629d6ae..84652506cd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -33,9 +33,9 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.tag.RoomTag -import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import timber.log.Timber +import java.lang.Exception import javax.inject.Inject class RoomListViewModel @Inject constructor(initialState: RoomListViewState, @@ -169,11 +169,16 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, } private fun handleChangeNotificationMode(action: RoomListAction.ChangeRoomNotificationState) { - session.getRoom(action.roomId)?.setRoomNotificationState(action.notificationState, object : MatrixCallback { - override fun onFailure(failure: Throwable) { - _viewEvents.post(RoomListViewEvents.Failure(failure)) + val room = session.getRoom(action.roomId) + if (room != null) { + viewModelScope.launch { + try { + room.setRoomNotificationState(action.notificationState) + } catch (failure: Exception) { + _viewEvents.post(RoomListViewEvents.Failure(failure)) + } } - }) + } } private fun handleToggleTag(action: RoomListAction.ToggleTag) { @@ -185,17 +190,13 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, action.tag.otherTag() ?.takeIf { room.roomSummary()?.hasTag(it).orFalse() } ?.let { tagToRemove -> - awaitCallback { room.deleteTag(tagToRemove, it) } + room.deleteTag(tagToRemove) } // Set the tag. We do not handle the order for the moment - awaitCallback { - room.addTag(action.tag, 0.5, it) - } + room.addTag(action.tag, 0.5) } else { - awaitCallback { - room.deleteTag(action.tag, it) - } + room.deleteTag(action.tag) } } catch (failure: Throwable) { _viewEvents.post(RoomListViewEvents.Failure(failure)) diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt index c6658925af..e3c1aa7b12 100644 --- a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt @@ -69,6 +69,11 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed { } override fun showFailure(throwable: Throwable) { + // Only the resumed Fragment can eventually show the error, to avoid multiple dialog display + if (!isResumed) { + return + } + when (throwable) { is Failure.Cancelled -> /* Ignore this error, user has cancelled the action */ diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt index 81d6a78123..1f47916538 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt @@ -207,7 +207,6 @@ class LoginViewModel @AssistedInject constructor( private fun handleCheckIfEmailHasBeenValidated(action: LoginAction.CheckIfEmailHasBeenValidated) { // We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state currentTask?.cancel() - currentTask = null currentTask = registrationWizard?.checkIfEmailHasBeenValidated(action.delayMillis, registrationCallback) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index e927ec9876..a78bf472d6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -102,11 +102,13 @@ class RoomProfileViewModel @AssistedInject constructor( } private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { - room.setRoomNotificationState(action.notificationState, object : MatrixCallback { - override fun onFailure(failure: Throwable) { + viewModelScope.launch { + try { + room.setRoomNotificationState(action.notificationState) + } catch (failure: Throwable) { _viewEvents.post(RoomProfileViewEvents.Failure(failure)) } - }) + } } private fun handleLeaveRoom() { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 4e540f867e..086ce93bb0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roomprofile.settings import androidx.core.net.toFile +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,7 +28,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import io.reactivex.Completable import io.reactivex.Observable -import org.matrix.android.sdk.api.MatrixCallback +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -228,16 +229,13 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: private fun handleEnableEncryption() { postLoading(true) - room.enableEncryption(callback = object : MatrixCallback { - override fun onFailure(failure: Throwable) { - postLoading(false) + viewModelScope.launch { + val result = runCatching { room.enableEncryption() } + postLoading(false) + result.onFailure { failure -> _viewEvents.post(RoomSettingsViewEvents.Failure(failure)) } - - override fun onSuccess(data: Unit) { - postLoading(false) - } - }) + } } private fun postLoading(isLoading: Boolean) {