diff --git a/CHANGES.md b/CHANGES.md
index 85ed3f05e3..f2d6e2d3fc 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,13 +5,19 @@ Features ✨:
  -
 
 Improvements 🙌:
+ - New room creation tile with quick action (#2346)
  - 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)
+ - Move "Enable Encryption" from room setting screen to room profile screen (#2394)
 
 Bugfix 🐛:
  - Exclude yourself when decorating rooms which are direct or don't have more than 2 users (#2370)
+ - F-Droid version: ensure timeout of sync request can be more than 60 seconds (#2169)
  - 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/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 99d667ccdc..cdc95ef6eb 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionSha256Sum=0080de8491f0918e4f529a6db6820fa0b9e818ee2386117f4394f95feb1d5583
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
+distributionSha256Sum=22449f5231796abd892c98b2a07c9ceebe4688d192cd2d6763f8e3bf8acbedeb
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
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<Unit> {
-                room.enableEncryption(callback = it)
+            mTestHelper.runBlockingTest {
+                room.enableEncryption()
             }
         }
 
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
index 7db159cd0b..ae300c936d 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
@@ -71,38 +71,27 @@ class SearchMessagesTest : InstrumentedTest {
             commonTestHelper.await(lock)
 
             lock = CountDownLatch(1)
-            aliceSession
-                    .searchService()
-                    .search(
-                            searchTerm = "lore",
-                            limit = 10,
-                            includeProfile = true,
-                            afterLimit = 0,
-                            beforeLimit = 10,
-                            orderByRecent = true,
-                            nextBatch = null,
-                            roomId = aliceRoomId,
-                            callback = object : MatrixCallback<SearchResult> {
-                                override fun onSuccess(data: SearchResult) {
-                                    super.onSuccess(data)
-                                    assertTrue(data.results?.size == 2)
-                                    assertTrue(
-                                            data.results
-                                                    ?.all {
-                                                        (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
-                                                    }.orFalse()
-                                    )
-                                    lock.countDown()
-                                }
-
-                                override fun onFailure(failure: Throwable) {
-                                    super.onFailure(failure)
-                                    fail(failure.localizedMessage)
-                                    lock.countDown()
-                                }
-                            }
-                    )
-            lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)
+            val data = commonTestHelper.runBlockingTest {
+                aliceSession
+                        .searchService()
+                        .search(
+                                searchTerm = "lore",
+                                limit = 10,
+                                includeProfile = true,
+                                afterLimit = 0,
+                                beforeLimit = 10,
+                                orderByRecent = true,
+                                nextBatch = null,
+                                roomId = aliceRoomId
+                        )
+            }
+            assertTrue(data.results?.size == 2)
+            assertTrue(
+                    data.results
+                            ?.all {
+                                (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
+                            }.orFalse()
+            )
 
             aliceTimeline.removeAllListeners()
             cryptoTestData.cleanUp(commonTestHelper)
diff --git a/matrix-sdk-android/src/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/pushrules/PushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt
index 880a7be9ac..4da1662681 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/PushRuleService.kt
@@ -15,11 +15,9 @@
  */
 package org.matrix.android.sdk.api.pushrules
 
-import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
 import org.matrix.android.sdk.api.pushrules.rest.RuleSet
 import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.util.Cancelable
 
 interface PushRuleService {
     /**
@@ -29,13 +27,13 @@ interface PushRuleService {
 
     fun getPushRules(scope: String = RuleScope.GLOBAL): RuleSet
 
-    fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean)
 
-    fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun addPushRule(kind: RuleKind, pushRule: PushRule)
 
-    fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule)
 
-    fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun removePushRule(kind: RuleKind, pushRule: PushRule)
 
     fun addPushRuleListener(listener: PushRuleListener)
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt
index 4e24a17047..19549a338e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt
@@ -16,9 +16,6 @@
 
 package org.matrix.android.sdk.api.raw
 
-import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.util.Cancelable
-
 /**
  * Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
  */
@@ -26,17 +23,15 @@ interface RawService {
     /**
      * Get a URL, either from cache or from the remote server, depending on the cache strategy
      */
-    fun getUrl(url: String,
-               rawCacheStrategy: RawCacheStrategy,
-               matrixCallback: MatrixCallback<String>): Cancelable
+    suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String
 
     /**
      * Specific case for the well-known file. Cache validity is 8 hours
      */
-    fun getWellknown(userId: String, matrixCallback: MatrixCallback<String>): Cancelable
+    suspend fun getWellknown(userId: String): String
 
     /**
      * Clear all the cache data
      */
-    fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable
+    suspend fun clearCache()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/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/group/Group.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt
index a4186b5a32..25c69e5025 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/group/Group.kt
@@ -16,9 +16,6 @@
 
 package org.matrix.android.sdk.api.session.group
 
-import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.util.Cancelable
-
 /**
  * This interface defines methods to interact within a group.
  */
@@ -28,8 +25,7 @@ interface Group {
     /**
      * This methods allows you to refresh data about this group. It will be reflected on the GroupSummary.
      * The SDK also takes care of refreshing group data every hour.
-     * @param callback : the matrix callback to be notified of success or failure
      * @return a Cancelable to be able to cancel requests.
      */
-    fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable
+    suspend fun fetchGroupData()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
index 537104a084..aedb813735 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
@@ -92,9 +92,29 @@ interface IdentityService {
 
     /**
      * Search MatrixId of users providing email and phone numbers
+     * Note the the user consent has to be set to true, or it will throw a UserConsentNotProvided failure
+     * Application has to explicitly ask for the user consent, and the answer can be stored using [setUserConsent]
+     * Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details.
      */
     fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable
 
+    /**
+     * Return the current user consent for the current identity server, which has been stored using [setUserConsent].
+     * If [setUserConsent] has not been called, the returned value will be false.
+     * Note that if the identity server is changed, the user consent is reset to false.
+     * @return the value stored using [setUserConsent] or false if [setUserConsent] has never been called, or if the identity server
+     *         has been changed
+     */
+    fun getUserConsent(): Boolean
+
+    /**
+     * Set the user consent to the provided value. Application MUST explicitly ask for the user consent to send their private data
+     * (email and phone numbers) to the identity server.
+     * Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details.
+     * @param newValue true if the user explicitly give their consent, false if the user wants to revoke their consent.
+     */
+    fun setUserConsent(newValue: Boolean)
+
     /**
      * Get the status of the current user's threePid
      * A lookup will be performed, but also pending binding state will be restored
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt
index 72bb72cc2c..42fdb97643 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityServiceError.kt
@@ -24,6 +24,7 @@ sealed class IdentityServiceError : Failure.FeatureFailure() {
     object NoIdentityServerConfigured : IdentityServiceError()
     object TermsNotSignedException : IdentityServiceError()
     object BulkLookupSha256NotSupported : IdentityServiceError()
+    object UserConsentNotProvided : IdentityServiceError()
     object BindingError : IdentityServiceError()
     object NoCurrentBindingError : IdentityServiceError()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
index 7f264c6228..5e9f3e1eb9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
@@ -17,6 +17,7 @@
 package org.matrix.android.sdk.api.session.permalinks
 
 import android.text.Spannable
+import org.matrix.android.sdk.api.MatrixPatterns
 
 /**
  *  MatrixLinkify take a piece of text and turns all of the
@@ -35,7 +36,7 @@ object MatrixLinkify {
          * I disable it because it mess up with pills, and even with pills, it does not work correctly:
          * The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to
          */
-        /*
+
         // sanity checks
         if (spannable.isEmpty()) {
             return false
@@ -48,14 +49,21 @@ object MatrixLinkify {
                 val startPos = match.range.first
                 if (startPos == 0 || text[startPos - 1] != '/') {
                     val endPos = match.range.last + 1
-                    val url = text.substring(match.range)
+                    var url = text.substring(match.range)
+                    if (MatrixPatterns.isUserId(url)
+                            || MatrixPatterns.isRoomAlias(url)
+                            || MatrixPatterns.isRoomId(url)
+                            || MatrixPatterns.isGroupId(url)
+                            || MatrixPatterns.isEventId(url)) {
+                        url = PermalinkService.MATRIX_TO_URL_BASE  + url
+                    }
                     val span = MatrixPermalinkSpan(url, callback)
                     spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
                 }
             }
         }
         return hasMatch
-         */
-        return false
+
+//        return false
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/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<Unit>)
+    suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/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<String> = emptyList(),
+
+        /**
+         * The server names to disallow in the room, excluding any port information. Wildcards may be used to cover
+         * a wider range of hosts, where * matches zero or more characters and ? matches exactly one character.
+         *
+         * This defaults to an empty list when not provided.
+         */
+        @Json(name = "deny")
+        val denyList: List<String> = emptyList()
+
+) {
+    companion object {
+        const val ALL = "*"
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/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<RoomNotificationState>
 
-    fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable
+    suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt
index 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<Unit>): Cancelable
+    suspend fun reportContent(eventId: String, score: Int, reason: String)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt
index 116a60e323..a9481d71a2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/DraftService.kt
@@ -17,8 +17,6 @@
 package org.matrix.android.sdk.api.session.room.send
 
 import androidx.lifecycle.LiveData
-import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.util.Cancelable
 import org.matrix.android.sdk.api.util.Optional
 
 interface DraftService {
@@ -26,12 +24,12 @@ interface DraftService {
     /**
      * Save or update a draft to the room
      */
-    fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun saveDraft(draft: UserDraft)
 
     /**
      * Delete the last draft, basically just after sending the message
      */
-    fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
+    suspend fun deleteDraft()
 
     /**
      * Return the current draft or null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/tags/TagsService.kt
index 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<Unit>): Cancelable
+    suspend fun addTag(tag: String, order: Double?)
 
     /**
      * Remove tag from a room
      */
-    fun deleteTag(tag: String, callback: MatrixCallback<Unit>): Cancelable
+    suspend fun deleteTag(tag: String)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt
index ef2eec433f..bc1c9e5769 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/search/SearchService.kt
@@ -16,9 +16,6 @@
 
 package org.matrix.android.sdk.api.session.search
 
-import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.util.Cancelable
-
 /**
  * This interface defines methods to search messages in rooms.
  */
@@ -35,15 +32,13 @@ interface SearchService {
      * @param beforeLimit how many events before the result are returned.
      * @param afterLimit how many events after the result are returned.
      * @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
-     * @param callback Callback to get the search result
      */
-    fun search(searchTerm: String,
-               roomId: String,
-               nextBatch: String?,
-               orderByRecent: Boolean,
-               limit: Int,
-               beforeLimit: Int,
-               afterLimit: Int,
-               includeProfile: Boolean,
-               callback: MatrixCallback<SearchResult>): Cancelable
+    suspend fun search(searchTerm: String,
+                       roomId: String,
+                       nextBatch: String?,
+                       orderByRecent: Boolean,
+                       limit: Int,
+                       beforeLimit: Int,
+                       afterLimit: Int,
+                       includeProfile: Boolean): SearchResult
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt
index 6c604f232f..724ec0dc7f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/TimeOutInterceptor.kt
@@ -52,5 +52,8 @@ internal class TimeOutInterceptor @Inject constructor() : Interceptor {
         const val CONNECT_TIMEOUT = "CONNECT_TIMEOUT"
         const val READ_TIMEOUT = "READ_TIMEOUT"
         const val WRITE_TIMEOUT = "WRITE_TIMEOUT"
+
+        // 1 minute
+        const val DEFAULT_LONG_TIMEOUT: Long = 60_000
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
index be01366efa..3b0d7546e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
@@ -16,45 +16,28 @@
 
 package org.matrix.android.sdk.internal.raw
 
-import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.raw.RawCacheStrategy
 import org.matrix.android.sdk.api.raw.RawService
-import org.matrix.android.sdk.api.util.Cancelable
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
 internal class DefaultRawService @Inject constructor(
-        private val taskExecutor: TaskExecutor,
         private val getUrlTask: GetUrlTask,
         private val cleanRawCacheTask: CleanRawCacheTask
 ) : RawService {
-    override fun getUrl(url: String,
-                        rawCacheStrategy: RawCacheStrategy,
-                        matrixCallback: MatrixCallback<String>): Cancelable {
-        return getUrlTask
-                .configureWith(GetUrlTask.Params(url, rawCacheStrategy)) {
-                    callback = matrixCallback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String {
+        return getUrlTask.execute(GetUrlTask.Params(url, rawCacheStrategy))
     }
 
-    override fun getWellknown(userId: String,
-                              matrixCallback: MatrixCallback<String>): Cancelable {
+    override suspend fun getWellknown(userId: String): String {
         val homeServerDomain = userId.substringAfter(":")
         return getUrl(
                 "https://$homeServerDomain/.well-known/matrix/client",
-                RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false),
-                matrixCallback
+                RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false)
         )
     }
 
-    override fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable {
-        return cleanRawCacheTask
-                .configureWith(Unit) {
-                    callback = matrixCallback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun clearCache() {
+        cleanRawCacheTask.execute(Unit)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt
index 01b57767b3..4f610fd81b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroup.kt
@@ -16,20 +16,13 @@
 
 package org.matrix.android.sdk.internal.session.group
 
-import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.session.group.Group
-import org.matrix.android.sdk.api.util.Cancelable
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
 
 internal class DefaultGroup(override val groupId: String,
-                            private val taskExecutor: TaskExecutor,
                             private val getGroupDataTask: GetGroupDataTask) : Group {
 
-    override fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable {
+    override suspend fun fetchGroupData() {
         val params = GetGroupDataTask.Params.FetchWithIds(listOf(groupId))
-        return getGroupDataTask.configureWith(params) {
-            this.callback = callback
-        }.executeBy(taskExecutor)
+        getGroupDataTask.execute(params)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt
index 31450763d8..653d2a6933 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GroupFactory.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.group
 
 import org.matrix.android.sdk.api.session.group.Group
 import org.matrix.android.sdk.internal.session.SessionScope
-import org.matrix.android.sdk.internal.task.TaskExecutor
 import javax.inject.Inject
 
 internal interface GroupFactory {
@@ -26,14 +25,12 @@ internal interface GroupFactory {
 }
 
 @SessionScope
-internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask,
-                                                       private val taskExecutor: TaskExecutor) :
+internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask) :
         GroupFactory {
 
     override fun create(groupId: String): Group {
         return DefaultGroup(
                 groupId = groupId,
-                taskExecutor = taskExecutor,
                 getGroupDataTask = getGroupDataTask
         )
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt
index 20f8b7f868..c6fb34151c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt
@@ -55,6 +55,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
 import org.matrix.android.sdk.internal.util.ensureProtocol
 import kotlinx.coroutines.withContext
 import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 import javax.inject.Inject
 import javax.net.ssl.HttpsURLConnection
@@ -243,7 +244,20 @@ internal class DefaultIdentityService @Inject constructor(
         ))
     }
 
+    override fun getUserConsent(): Boolean {
+        return identityStore.getIdentityData()?.userConsent.orFalse()
+    }
+
+    override fun setUserConsent(newValue: Boolean) {
+        identityStore.setUserConsent(newValue)
+    }
+
     override fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable {
+        if (!getUserConsent()) {
+            callback.onFailure(IdentityServiceError.UserConsentNotProvided)
+            return NoOpCancellable
+        }
+
         if (threePids.isEmpty()) {
             callback.onSuccess(emptyList())
             return NoOpCancellable
@@ -255,6 +269,9 @@ internal class DefaultIdentityService @Inject constructor(
     }
 
     override fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable {
+        // Note: we do not require user consent here, because it is used for emails and phone numbers that the user has already sent
+        // to the home server, and not emails and phone numbers from the contact book of the user
+
         if (threePids.isEmpty()) {
             callback.onSuccess(emptyMap())
             return NoOpCancellable
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt
index e140cc19f3..7a39a333a5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt
@@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.session.identity.db.IdentityRealmModule
 import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStore
 import io.realm.RealmConfiguration
 import okhttp3.OkHttpClient
+import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStoreMigration
 import java.io.File
 
 @Module
@@ -59,6 +60,7 @@ internal abstract class IdentityModule {
         @SessionScope
         fun providesIdentityRealmConfiguration(realmKeysUtils: RealmKeysUtils,
                                                @SessionFilesDirectory directory: File,
+                                               migration: RealmIdentityStoreMigration,
                                                @UserMd5 userMd5: String): RealmConfiguration {
             return RealmConfiguration.Builder()
                     .directory(directory)
@@ -66,6 +68,8 @@ internal abstract class IdentityModule {
                     .apply {
                         realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
                     }
+                    .schemaVersion(RealmIdentityStoreMigration.IDENTITY_STORE_SCHEMA_VERSION)
+                    .migration(migration)
                     .allowWritesOnUiThread(true)
                     .modules(IdentityRealmModule())
                     .build()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt
index 0f04f2fe1a..54d35b34fa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityData.kt
@@ -20,5 +20,6 @@ internal data class IdentityData(
         val identityServerUrl: String?,
         val token: String?,
         val hashLookupPepper: String?,
-        val hashLookupAlgorithm: List<String>
+        val hashLookupAlgorithm: List<String>,
+        val userConsent: Boolean
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt
index 3a905833d5..0e05224be5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/data/IdentityStore.kt
@@ -27,6 +27,8 @@ internal interface IdentityStore {
 
     fun setToken(token: String?)
 
+    fun setUserConsent(consent: Boolean)
+
     fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse)
 
     /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt
index cc03465cc8..019289a884 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntity.kt
@@ -23,7 +23,8 @@ internal open class IdentityDataEntity(
         var identityServerUrl: String? = null,
         var token: String? = null,
         var hashLookupPepper: String? = null,
-        var hashLookupAlgorithm: RealmList<String> = RealmList()
+        var hashLookupAlgorithm: RealmList<String> = RealmList(),
+        var userConsent: Boolean = false
 ) : RealmObject() {
 
     companion object
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt
index 062c28ea55..5152e33743 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityDataEntityQuery.kt
@@ -52,6 +52,13 @@ internal fun IdentityDataEntity.Companion.setToken(realm: Realm,
     }
 }
 
+internal fun IdentityDataEntity.Companion.setUserConsent(realm: Realm,
+                                                         newConsent: Boolean) {
+    get(realm)?.apply {
+        userConsent = newConsent
+    }
+}
+
 internal fun IdentityDataEntity.Companion.setHashDetails(realm: Realm,
                                                          pepper: String,
                                                          algorithms: List<String>) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt
index 98207f1b38..bf23c05811 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityMapper.kt
@@ -26,7 +26,8 @@ internal object IdentityMapper {
                 identityServerUrl = entity.identityServerUrl,
                 token = entity.token,
                 hashLookupPepper = entity.hashLookupPepper,
-                hashLookupAlgorithm = entity.hashLookupAlgorithm.toList()
+                hashLookupAlgorithm = entity.hashLookupAlgorithm.toList(),
+                userConsent = entity.userConsent
         )
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt
index 0352e9b936..2fa3fc0cfb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt
@@ -55,6 +55,14 @@ internal class RealmIdentityStore @Inject constructor(
         }
     }
 
+    override fun setUserConsent(consent: Boolean) {
+        Realm.getInstance(realmConfiguration).use {
+            it.executeTransaction { realm ->
+                IdentityDataEntity.setUserConsent(realm, consent)
+            }
+        }
+    }
+
     override fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse) {
         Realm.getInstance(realmConfiguration).use {
             it.executeTransaction { realm ->
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt
new file mode 100644
index 0000000000..6081dbab12
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStoreMigration.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.session.identity.db
+
+import io.realm.DynamicRealm
+import io.realm.RealmMigration
+import timber.log.Timber
+import javax.inject.Inject
+
+internal class RealmIdentityStoreMigration @Inject constructor() : RealmMigration {
+
+    companion object {
+        const val IDENTITY_STORE_SCHEMA_VERSION = 1L
+    }
+
+    override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
+        Timber.v("Migrating Realm Identity from $oldVersion to $newVersion")
+
+        if (oldVersion <= 0) migrateTo1(realm)
+    }
+
+    private fun migrateTo1(realm: DynamicRealm) {
+        Timber.d("Step 0 -> 1")
+        Timber.d("Add field userConsent (Boolean) and set the value to false")
+
+        realm.schema.get("IdentityDataEntity")
+                ?.addField(IdentityDataEntityFields.USER_CONSENT, Boolean::class.java)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
index 217da269f9..e00d2ff26c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
@@ -16,7 +16,6 @@
 package org.matrix.android.sdk.internal.session.notification
 
 import com.zhuinden.monarchy.Monarchy
-import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.pushrules.PushRuleService
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import org.matrix.android.sdk.api.pushrules.RuleSetKey
@@ -24,7 +23,6 @@ import org.matrix.android.sdk.api.pushrules.getActions
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
 import org.matrix.android.sdk.api.pushrules.rest.RuleSet
 import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.util.Cancelable
 import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
 import org.matrix.android.sdk.internal.database.model.PushRulesEntity
 import org.matrix.android.sdk.internal.database.query.where
@@ -103,37 +101,21 @@ internal class DefaultPushRuleService @Inject constructor(
         )
     }
 
-    override fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable {
+    override suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean) {
         // The rules will be updated, and will come back from the next sync response
-        return updatePushRuleEnableStatusTask
-                .configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+        updatePushRuleEnableStatusTask.execute(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled))
     }
 
-    override fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
-        return addPushRuleTask
-                .configureWith(AddPushRuleTask.Params(kind, pushRule)) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun addPushRule(kind: RuleKind, pushRule: PushRule) {
+        addPushRuleTask.execute(AddPushRuleTask.Params(kind, pushRule))
     }
 
-    override fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
-        return updatePushRuleActionsTask
-                .configureWith(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule)) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule) {
+        updatePushRuleActionsTask.execute(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule))
     }
 
-    override fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
-        return removePushRuleTask
-                .configureWith(RemovePushRuleTask.Params(kind, pushRule)) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun removePushRule(kind: RuleKind, pushRule: PushRule) {
+        removePushRuleTask.execute(RemovePushRuleTask.Params(kind, pushRule))
     }
 
     override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/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<Unit>) {
+    override suspend fun enableEncryption(algorithm: String) {
         when {
             isEncrypted()                          -> {
-                callback.onFailure(IllegalStateException("Encryption is already enabled for this room"))
+                throw IllegalStateException("Encryption is already enabled for this room")
             }
             algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> {
-                callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported"))
+                throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")
             }
             else                                   -> {
                 val params = SendStateTask.Params(
@@ -118,11 +118,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
                                 "algorithm" to algorithm
                         ))
 
-                sendStateTask
-                        .configureWith(params) {
-                            this.callback = callback
-                        }
-                        .executeBy(taskExecutor)
+                sendStateTask.execute(params)
             }
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
index 90ee99a919..99f9d3644d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
@@ -26,6 +26,8 @@ import org.matrix.android.sdk.internal.database.query.getOrNull
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import io.realm.Realm
+import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
+import org.matrix.android.sdk.internal.database.query.where
 import javax.inject.Inject
 
 internal class RoomAvatarResolver @Inject constructor(@UserId private val userId: String) {
@@ -46,11 +48,14 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId
         val roomMembers = RoomMemberHelper(realm, roomId)
         val members = roomMembers.queryActiveRoomMembersEvent().findAll()
         // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat)
-        if (members.size == 1) {
-            res = members.firstOrNull()?.avatarUrl
-        } else if (members.size == 2) {
-            val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst()
-            res = firstOtherMember?.avatarUrl
+        val isDirectRoom =  RoomSummaryEntity.where(realm, roomId).findFirst()?.isDirect ?: false
+        if (isDirectRoom) {
+            if (members.size == 1) {
+                res = members.firstOrNull()?.avatarUrl
+            } else if (members.size == 2) {
+                val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst()
+                res = firstOtherMember?.avatarUrl
+            }
         }
         return res
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
index 8b011980d0..58a119cc77 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
@@ -17,6 +17,9 @@
 package org.matrix.android.sdk.internal.session.room.alias
 
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.query.findByAlias
@@ -24,8 +27,6 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Optional<String>> {
@@ -50,9 +51,11 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor(
         } else if (!params.searchOnServer) {
             Optional.from<String>(null)
         } else {
-            roomId = executeRequest<RoomAliasDescription>(eventBus) {
-                apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
-            }.roomId
+            roomId = tryOrNull("## Failed to get roomId from alias") {
+                executeRequest<RoomAliasDescription>(eventBus) {
+                    apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
+                }
+            }?.roomId
             Optional.from(roomId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
index 92e16a3501..93fbfb4df0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt
@@ -19,18 +19,14 @@ package org.matrix.android.sdk.internal.session.room.draft
 import androidx.lifecycle.LiveData
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
-import org.matrix.android.sdk.api.MatrixCallback
+import kotlinx.coroutines.withContext
 import org.matrix.android.sdk.api.session.room.send.DraftService
 import org.matrix.android.sdk.api.session.room.send.UserDraft
-import org.matrix.android.sdk.api.util.Cancelable
 import org.matrix.android.sdk.api.util.Optional
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.launchToCallback
 import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
 
 internal class DefaultDraftService @AssistedInject constructor(@Assisted private val roomId: String,
                                                                private val draftRepository: DraftRepository,
-                                                               private val taskExecutor: TaskExecutor,
                                                                private val coroutineDispatchers: MatrixCoroutineDispatchers
 ) : DraftService {
 
@@ -43,14 +39,14 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
      * The draft stack can contain several drafts. Depending of the draft to save, it will update the top draft, or create a new draft,
      * or even move an existing draft to the top of the list
      */
-    override fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable {
-        return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
+    override suspend fun saveDraft(draft: UserDraft) {
+        withContext(coroutineDispatchers.main) {
             draftRepository.saveDraft(roomId, draft)
         }
     }
 
-    override fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable {
-        return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
+    override suspend fun deleteDraft() {
+        withContext(coroutineDispatchers.main) {
             draftRepository.deleteDraft(roomId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
index 7f3796c1ce..a7dfcfc96f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
@@ -93,6 +93,8 @@ internal class RoomDisplayNameResolver @Inject constructor(
             }
         } else if (roomEntity?.membership == Membership.JOIN) {
             val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
+            val invitedCount = roomSummary?.invitedMembersCount ?: 0
+            val joinedCount = roomSummary?.joinedMembersCount ?: 0
             val otherMembersSubset: List<RoomMemberSummaryEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
                 roomSummary.heroes.mapNotNull { userId ->
                     roomMembers.getLastRoomMember(userId)?.takeIf {
@@ -102,22 +104,49 @@ internal class RoomDisplayNameResolver @Inject constructor(
             } else {
                 activeMembers.where()
                         .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId)
-                        .limit(3)
+                        .limit(5)
                         .findAll()
                         .createSnapshot()
             }
             val otherMembersCount = otherMembersSubset.count()
             name = when (otherMembersCount) {
-                0    -> stringProvider.getString(R.string.room_displayname_empty_room)
+                0    -> {
+                    stringProvider.getString(R.string.room_displayname_empty_room)
+                    // TODO (was xx and yyy) ...
+                }
                 1    -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
-                2    -> stringProvider.getString(R.string.room_displayname_two_members,
-                        resolveRoomMemberName(otherMembersSubset[0], roomMembers),
-                        resolveRoomMemberName(otherMembersSubset[1], roomMembers)
-                )
-                else -> stringProvider.getQuantityString(R.plurals.room_displayname_three_and_more_members,
-                        roomMembers.getNumberOfJoinedMembers() - 1,
-                        resolveRoomMemberName(otherMembersSubset[0], roomMembers),
-                        roomMembers.getNumberOfJoinedMembers() - 1)
+                2    -> {
+                    stringProvider.getString(R.string.room_displayname_two_members,
+                            resolveRoomMemberName(otherMembersSubset[0], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[1], roomMembers)
+                    )
+                }
+                3    -> {
+                    stringProvider.getString(R.string.room_displayname_3_members,
+                            resolveRoomMemberName(otherMembersSubset[0], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[1], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[2], roomMembers)
+                    )
+                }
+                4    -> {
+                    stringProvider.getString(R.string.room_displayname_4_members,
+                            resolveRoomMemberName(otherMembersSubset[0], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[1], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[2], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[3], roomMembers)
+                    )
+                }
+                else -> {
+                    val remainingCount = invitedCount + joinedCount - otherMembersCount + 1
+                    stringProvider.getQuantityString(
+                            R.plurals.room_displayname_four_and_more_members,
+                            remainingCount,
+                            resolveRoomMemberName(otherMembersSubset[0], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[1], roomMembers),
+                            resolveRoomMemberName(otherMembersSubset[2], roomMembers),
+                            remainingCount
+                    )
+                }
             }
         }
         return name ?: roomId
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt
index 7105a2cc22..2a7c46bd42 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt
@@ -16,6 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.room.membership
 
+import io.realm.Realm
+import io.realm.RealmQuery
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
@@ -25,8 +27,6 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.query.getOrNull
 import org.matrix.android.sdk.internal.database.query.where
-import io.realm.Realm
-import io.realm.RealmQuery
 
 /**
  * This class is an helper around STATE_ROOM_MEMBER events.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt
index 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<Unit>): Cancelable {
-        return setRoomNotificationStateTask
-                .configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) {
-                    this.callback = matrixCallback
-                }
-                .executeBy(taskExecutor)
+    override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) {
+        setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState))
     }
 
     private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt
index 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<Unit>): Cancelable {
+    override suspend fun reportContent(eventId: String, score: Int, reason: String) {
         val params = ReportContentTask.Params(roomId, eventId, score, reason)
-
-        return reportContentTask
-                .configureWith(params) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+        reportContentTask.execute(params)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/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<Unit>): Cancelable {
+    override suspend fun addTag(tag: String, order: Double?) {
         val params = AddTagToRoomTask.Params(roomId, tag, order)
-        return addTagToRoomTask
-                .configureWith(params) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+        addTagToRoomTask.execute(params)
     }
 
-    override fun deleteTag(tag: String, callback: MatrixCallback<Unit>): Cancelable {
+    override suspend fun deleteTag(tag: String) {
         val params = DeleteTagFromRoomTask.Params(roomId, tag)
-        return deleteTagFromRoomTask
-                .configureWith(params) {
-                    this.callback = callback
-                }
-                .executeBy(taskExecutor)
+        deleteTagFromRoomTask.execute(params)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt
index 2ba1eebe61..8033b0654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/DefaultSearchService.kt
@@ -16,40 +16,31 @@
 
 package org.matrix.android.sdk.internal.session.search
 
-import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.session.search.SearchResult
 import org.matrix.android.sdk.api.session.search.SearchService
-import org.matrix.android.sdk.api.util.Cancelable
 import javax.inject.Inject
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
 
 internal class DefaultSearchService @Inject constructor(
-        private val taskExecutor: TaskExecutor,
         private val searchTask: SearchTask
 ) : SearchService {
 
-    override fun search(searchTerm: String,
-                        roomId: String,
-                        nextBatch: String?,
-                        orderByRecent: Boolean,
-                        limit: Int,
-                        beforeLimit: Int,
-                        afterLimit: Int,
-                        includeProfile: Boolean,
-                        callback: MatrixCallback<SearchResult>): Cancelable {
-        return searchTask
-                .configureWith(SearchTask.Params(
-                        searchTerm = searchTerm,
-                        roomId = roomId,
-                        nextBatch = nextBatch,
-                        orderByRecent = orderByRecent,
-                        limit = limit,
-                        beforeLimit = beforeLimit,
-                        afterLimit = afterLimit,
-                        includeProfile = includeProfile
-                )) {
-                    this.callback = callback
-                }.executeBy(taskExecutor)
+    override suspend fun search(searchTerm: String,
+                                roomId: String,
+                                nextBatch: String?,
+                                orderByRecent: Boolean,
+                                limit: Int,
+                                beforeLimit: Int,
+                                afterLimit: Int,
+                                includeProfile: Boolean): SearchResult {
+        return searchTask.execute(SearchTask.Params(
+                searchTerm = searchTerm,
+                roomId = roomId,
+                nextBatch = nextBatch,
+                orderByRecent = orderByRecent,
+                limit = limit,
+                beforeLimit = beforeLimit,
+                afterLimit = afterLimit,
+                includeProfile = includeProfile
+        ))
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt
index 427a8896c9..77289f04b4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt
@@ -17,18 +17,21 @@
 package org.matrix.android.sdk.internal.session.sync
 
 import org.matrix.android.sdk.internal.network.NetworkConstants
+import org.matrix.android.sdk.internal.network.TimeOutInterceptor
 import org.matrix.android.sdk.internal.session.sync.model.SyncResponse
 import retrofit2.Call
 import retrofit2.http.GET
-import retrofit2.http.Headers
+import retrofit2.http.Header
 import retrofit2.http.QueryMap
 
 internal interface SyncAPI {
-
     /**
-     * Set all the timeouts to 1 minute
+     * Set all the timeouts to 1 minute by default
      */
-    @Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
     @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync")
-    fun sync(@QueryMap params: Map<String, String>): Call<SyncResponse>
+    fun sync(@QueryMap params: Map<String, String>,
+             @Header(TimeOutInterceptor.CONNECT_TIMEOUT) connectTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT,
+             @Header(TimeOutInterceptor.READ_TIMEOUT) readTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT,
+             @Header(TimeOutInterceptor.WRITE_TIMEOUT) writeTimeOut: Long = TimeOutInterceptor.DEFAULT_LONG_TIMEOUT
+    ): Call<SyncResponse>
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
index 303bb45419..b4fd6e7386 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
@@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync
 import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.R
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.TimeOutInterceptor
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService
 import org.matrix.android.sdk.internal.session.filter.FilterRepository
@@ -78,8 +79,13 @@ internal class DefaultSyncTask @Inject constructor(
         // Maybe refresh the home server capabilities data we know
         getHomeServerCapabilitiesTask.execute(Unit)
 
+        val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
+
         val syncResponse = executeRequest<SyncResponse>(eventBus) {
-            apiCall = syncAPI.sync(requestParams)
+            apiCall = syncAPI.sync(
+                    params = requestParams,
+                    readTimeOut = readTimeOut
+            )
         }
         syncResponseHandler.handleResponse(syncResponse, token)
         if (isInitialSync) {
@@ -87,4 +93,8 @@ internal class DefaultSyncTask @Inject constructor(
         }
         Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}")
     }
+
+    companion object {
+        private const val TIMEOUT_MARGIN: Long = 10_000
+    }
 }
diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml
index 27f083269f..f77cd3203d 100644
--- a/matrix-sdk-android/src/main/res/values/strings.xml
+++ b/matrix-sdk-android/src/main/res/values/strings.xml
@@ -72,6 +72,23 @@
     <string name="notice_room_update_by_you">You upgraded this room.</string>
     <string name="notice_direct_room_update">%s upgraded here.</string>
     <string name="notice_direct_room_update_by_you">You upgraded here.</string>
+    <string name="notice_room_server_acl_set_title">%s set the server ACLs for this room.</string>
+    <string name="notice_room_server_acl_set_title_by_you">You set the server ACLs for this room.</string>
+    <string name="notice_room_server_acl_set_banned">• Server matching %s are banned.</string>
+    <string name="notice_room_server_acl_set_allowed">• Server matching %s are allowed.</string>
+    <string name="notice_room_server_acl_set_ip_literals_allowed">• Server matching IP literals are allowed.</string>
+    <string name="notice_room_server_acl_set_ip_literals_not_allowed">• Server matching IP literals are banned.</string>
+
+    <string name="notice_room_server_acl_updated_title">%s changed the server ACLs for this room.</string>
+    <string name="notice_room_server_acl_updated_title_by_you">You changed the server ACLs for this room.</string>
+    <string name="notice_room_server_acl_updated_banned">• Server matching %s are now banned.</string>
+    <string name="notice_room_server_acl_updated_was_banned">• Server matching %s were removed from the ban list.</string>
+    <string name="notice_room_server_acl_updated_allowed">• Server matching %s are now allowed.</string>
+    <string name="notice_room_server_acl_updated_was_allowed">• Server matching %s were removed from the allowed list.</string>
+    <string name="notice_room_server_acl_updated_ip_literals_allowed">• Server matching IP literals are now allowed.</string>
+    <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Server matching IP literals are now banned.</string>
+    <string name="notice_room_server_acl_updated_no_change">No change.</string>
+    <string name="notice_room_server_acl_allow_is_empty">🎉 All servers are banned from participating! This room can no longer be used.</string>
 
     <string name="notice_requested_voip_conference">%1$s requested a VoIP conference</string>
     <string name="notice_requested_voip_conference_by_you">You requested a VoIP conference</string>
@@ -158,13 +175,22 @@
 
     <!-- The 2 parameters will be members' name -->
     <string name="room_displayname_two_members">%1$s and %2$s</string>
-
+    <!-- The 3 parameters will be members' name -->
+    <string name="room_displayname_3_members">%1$s, %2$s and %3$s</string>
+    <!-- The 4 parameters will be members' name -->
+    <string name="room_displayname_4_members">%1$s, %2$s, %3$s and %4$s</string>
+    <!-- The 3 first parameters will be members' name -->
+    <plurals name="room_displayname_four_and_more_members">
+        <item quantity="one">%1$s, %2$s, %3$s and %4$d other</item>
+        <item quantity="other">%1$s, %2$s, %3$s and %4$d others</item>
+    </plurals>
     <plurals name="room_displayname_three_and_more_members">
         <item quantity="one">%1$s and 1 other</item>
         <item quantity="other">%1$s and %2$d others</item>
     </plurals>
 
     <string name="room_displayname_empty_room">Empty room</string>
+    <string name="room_displayname_empty_room_was">Empty room (was %s)</string>
 
     <string name="initial_sync_start_importing_account">Initial Sync:\nImporting account…</string>
     <string name="initial_sync_start_importing_account_crypto">Initial Sync:\nImporting crypto</string>
diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
index 6065c74541..b9bc935890 100644
--- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
+++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
@@ -136,6 +136,7 @@ class DefaultErrorFormatter @Inject constructor(
             IdentityServiceError.BulkLookupSha256NotSupported -> R.string.identity_server_error_bulk_sha256_not_supported
             IdentityServiceError.BindingError                 -> R.string.identity_server_error_binding_error
             IdentityServiceError.NoCurrentBindingError        -> R.string.identity_server_error_no_current_binding_error
+            IdentityServiceError.UserConsentNotProvided       -> R.string.identity_server_user_consent_not_provided
         })
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt
index 8eb5bc733b..e380998fd2 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookAction.kt
@@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewModelAction
 sealed class ContactsBookAction : VectorViewModelAction {
     data class FilterWith(val filter: String) : ContactsBookAction()
     data class OnlyBoundContacts(val onlyBoundContacts: Boolean) : ContactsBookAction()
+    object UserConsentGranted : ContactsBookAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookController.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookController.kt
index 9eca2afa60..59c23f4ac7 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookController.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookController.kt
@@ -52,11 +52,10 @@ class ContactsBookController @Inject constructor(
 
     override fun buildModels() {
         val currentState = state ?: return
-        val hasSearch = currentState.searchTerm.isNotEmpty()
         when (val asyncMappedContacts = currentState.mappedContacts) {
             is Uninitialized -> renderEmptyState(false)
             is Loading       -> renderLoading()
-            is Success       -> renderSuccess(currentState.filteredMappedContacts, hasSearch, currentState.onlyBoundContacts)
+            is Success       -> renderSuccess(currentState)
             is Fail          -> renderFailure(asyncMappedContacts.error)
         }
     }
@@ -75,13 +74,13 @@ class ContactsBookController @Inject constructor(
         }
     }
 
-    private fun renderSuccess(mappedContacts: List<MappedContact>,
-                              hasSearch: Boolean,
-                              onlyBoundContacts: Boolean) {
+    private fun renderSuccess(state: ContactsBookViewState) {
+        val mappedContacts = state.filteredMappedContacts
+
         if (mappedContacts.isEmpty()) {
-            renderEmptyState(hasSearch)
+            renderEmptyState(state.searchTerm.isNotEmpty())
         } else {
-            renderContacts(mappedContacts, onlyBoundContacts)
+            renderContacts(mappedContacts, state.onlyBoundContacts)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt
index c4cf9eab39..23d21f5240 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.contactsbook
 
 import android.os.Bundle
 import android.view.View
+import androidx.appcompat.app.AlertDialog
 import androidx.core.view.isVisible
 import com.airbnb.mvrx.activityViewModel
 import com.airbnb.mvrx.withState
@@ -57,10 +58,26 @@ class ContactsBookFragment @Inject constructor(
         sharedActionViewModel = activityViewModelProvider.get(UserDirectorySharedActionViewModel::class.java)
         setupRecyclerView()
         setupFilterView()
+        setupConsentView()
         setupOnlyBoundContactsView()
         setupCloseView()
     }
 
+    private fun setupConsentView() {
+        phoneBookSearchForMatrixContacts.setOnClickListener {
+            withState(contactsBookViewModel) { state ->
+                AlertDialog.Builder(requireActivity())
+                        .setTitle(R.string.identity_server_consent_dialog_title)
+                        .setMessage(getString(R.string.identity_server_consent_dialog_content, state.identityServerUrl ?: ""))
+                        .setPositiveButton(R.string.yes) { _, _ ->
+                            contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted)
+                        }
+                        .setNegativeButton(R.string.no, null)
+                        .show()
+            }
+        }
+    }
+
     private fun setupOnlyBoundContactsView() {
         phoneBookOnlyBoundContacts.checkedChanges()
                 .subscribe {
@@ -98,6 +115,7 @@ class ContactsBookFragment @Inject constructor(
     }
 
     override fun invalidate() = withState(contactsBookViewModel) { state ->
+        phoneBookSearchForMatrixContacts.isVisible = state.filteredMappedContacts.isNotEmpty() && state.identityServerUrl != null && !state.userConsent
         phoneBookOnlyBoundContacts.isVisible = state.isBoundRetrieved
         contactsBookController.setData(state)
     }
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
index 167660d11e..2c4c5d0596 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt
@@ -38,11 +38,10 @@ import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.identity.FoundThreePid
+import org.matrix.android.sdk.api.session.identity.IdentityServiceError
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import timber.log.Timber
 
-private typealias PhoneBookSearch = String
-
 class ContactsBookViewModel @AssistedInject constructor(@Assisted
                                                         initialState: ContactsBookViewState,
                                                         private val contactsDataSource: ContactsDataSource,
@@ -85,7 +84,9 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
     private fun loadContacts() {
         setState {
             copy(
-                    mappedContacts = Loading()
+                    mappedContacts = Loading(),
+                    identityServerUrl = session.identityService().getCurrentIdentityServerUrl(),
+                    userConsent = session.identityService().getUserConsent()
             )
         }
 
@@ -109,6 +110,9 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
     }
 
     private fun performLookup(data: List<MappedContact>) {
+        if (!session.identityService().getUserConsent()) {
+            return
+        }
         viewModelScope.launch {
             val threePids = data.flatMap { contact ->
                 contact.emails.map { ThreePid.Email(it.email) } +
@@ -116,8 +120,14 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
             }
             session.identityService().lookUp(threePids, object : MatrixCallback<List<FoundThreePid>> {
                 override fun onFailure(failure: Throwable) {
-                    // Ignore
                     Timber.w(failure, "Unable to perform the lookup")
+
+                    // Should not happen, but just to be sure
+                    if (failure is IdentityServiceError.UserConsentNotProvided) {
+                        setState {
+                            copy(userConsent = false)
+                        }
+                    }
                 }
 
                 override fun onSuccess(data: List<FoundThreePid>) {
@@ -171,9 +181,21 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted
         when (action) {
             is ContactsBookAction.FilterWith        -> handleFilterWith(action)
             is ContactsBookAction.OnlyBoundContacts -> handleOnlyBoundContacts(action)
+            ContactsBookAction.UserConsentGranted   -> handleUserConsentGranted()
         }.exhaustive
     }
 
+    private fun handleUserConsentGranted() {
+        session.identityService().setUserConsent(true)
+
+        setState {
+            copy(userConsent = true)
+        }
+
+        // Perform the lookup
+        performLookup(allContacts)
+    }
+
     private fun handleOnlyBoundContacts(action: ContactsBookAction.OnlyBoundContacts) {
         setState {
             copy(
diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt
index 3e4f4ddcb6..d2ee684c4d 100644
--- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewState.kt
@@ -26,10 +26,14 @@ data class ContactsBookViewState(
         val mappedContacts: Async<List<MappedContact>> = Loading(),
         // Use to filter contacts by display name
         val searchTerm: String = "",
-        // Tru to display only bound contacts with their bound 2pid
+        // True to display only bound contacts with their bound 2pid
         val onlyBoundContacts: Boolean = false,
         // All contacts, filtered by searchTerm and onlyBoundContacts
         val filteredMappedContacts: List<MappedContact> = emptyList(),
         // True when the identity service has return some data
-        val isBoundRetrieved: Boolean = false
+        val isBoundRetrieved: Boolean = false,
+        // The current identity server url if any
+        val identityServerUrl: String? = null,
+        // User consent to perform lookup (send emails to the identity server)
+        val userConsent: Boolean = false
 ) : MvRxState
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt
index c66ae69e6a..426f1321e7 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsAction.kt
@@ -25,6 +25,7 @@ sealed class DiscoverySettingsAction : VectorViewModelAction {
 
     object DisconnectIdentityServer : DiscoverySettingsAction()
     data class ChangeIdentityServer(val url: String) : DiscoverySettingsAction()
+    data class UpdateUserConsent(val newConsent: Boolean) : DiscoverySettingsAction()
     data class RevokeThreePid(val threePid: ThreePid) : DiscoverySettingsAction()
     data class ShareThreePid(val threePid: ThreePid) : DiscoverySettingsAction()
     data class FinalizeBind3pid(val threePid: ThreePid) : DiscoverySettingsAction()
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt
index 306d9bffd1..55c11f3a50 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt
@@ -65,6 +65,7 @@ class DiscoverySettingsController @Inject constructor(
                 buildIdentityServerSection(data)
                 val hasIdentityServer = data.identityServer().isNullOrBlank().not()
                 if (hasIdentityServer && !data.termsNotSigned) {
+                    buildConsentSection(data)
                     buildEmailsSection(data.emailList)
                     buildMsisdnSection(data.phoneNumbersList)
                 }
@@ -72,6 +73,38 @@ class DiscoverySettingsController @Inject constructor(
         }
     }
 
+    private fun buildConsentSection(data: DiscoverySettingsState) {
+        settingsSectionTitleItem {
+            id("idConsentTitle")
+            titleResId(R.string.settings_discovery_consent_title)
+        }
+
+        if (data.userConsent) {
+            settingsInfoItem {
+                id("idConsentInfo")
+                helperTextResId(R.string.settings_discovery_consent_notice_on)
+            }
+            settingsButtonItem {
+                id("idConsentButton")
+                colorProvider(colorProvider)
+                buttonTitleId(R.string.settings_discovery_consent_action_revoke)
+                buttonStyle(ButtonStyle.DESTRUCTIVE)
+                buttonClickListener { listener?.onTapUpdateUserConsent(false) }
+            }
+        } else {
+            settingsInfoItem {
+                id("idConsentInfo")
+                helperTextResId(R.string.settings_discovery_consent_notice_off)
+            }
+            settingsButtonItem {
+                id("idConsentButton")
+                colorProvider(colorProvider)
+                buttonTitleId(R.string.settings_discovery_consent_action_give_consent)
+                buttonClickListener { listener?.onTapUpdateUserConsent(true) }
+            }
+        }
+    }
+
     private fun buildIdentityServerSection(data: DiscoverySettingsState) {
         val identityServer = data.identityServer() ?: stringProvider.getString(R.string.none)
 
@@ -359,6 +392,7 @@ class DiscoverySettingsController @Inject constructor(
         fun sendMsisdnVerificationCode(threePid: ThreePid.Msisdn, code: String)
         fun onTapChangeIdentityServer()
         fun onTapDisconnectIdentityServer()
+        fun onTapUpdateUserConsent(newValue: Boolean)
         fun onTapRetryToRetrieveBindings()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt
index bfbc00b15a..97d824054d 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt
@@ -170,6 +170,23 @@ class DiscoverySettingsFragment @Inject constructor(
         }
     }
 
+    override fun onTapUpdateUserConsent(newValue: Boolean) {
+        if (newValue) {
+            withState(viewModel) { state ->
+                AlertDialog.Builder(requireActivity())
+                        .setTitle(R.string.identity_server_consent_dialog_title)
+                        .setMessage(getString(R.string.identity_server_consent_dialog_content, state.identityServer.invoke()))
+                        .setPositiveButton(R.string.yes) { _, _ ->
+                            viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true))
+                        }
+                        .setNegativeButton(R.string.no, null)
+                        .show()
+            }
+        } else {
+            viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(false))
+        }
+    }
+
     override fun onTapRetryToRetrieveBindings() {
         viewModel.handle(DiscoverySettingsAction.RetrieveBinding)
     }
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt
index 6b28c07e89..21fbcf1ca7 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt
@@ -25,5 +25,6 @@ data class DiscoverySettingsState(
         val emailList: Async<List<PidInfo>> = Uninitialized,
         val phoneNumbersList: Async<List<PidInfo>> = Uninitialized,
         // Can be true if terms are updated
-        val termsNotSigned: Boolean = false
+        val termsNotSigned: Boolean = false,
+        val userConsent: Boolean = false
 ) : MvRxState
diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
index 0bfcdd9984..0f294e080a 100644
--- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt
@@ -63,7 +63,10 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
             val identityServerUrl = identityService.getCurrentIdentityServerUrl()
             val currentIS = state.identityServer()
             setState {
-                copy(identityServer = Success(identityServerUrl))
+                copy(
+                        identityServer = Success(identityServerUrl),
+                        userConsent = false
+                )
             }
             if (currentIS != identityServerUrl) retrieveBinding()
         }
@@ -71,7 +74,10 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
 
     init {
         setState {
-            copy(identityServer = Success(identityService.getCurrentIdentityServerUrl()))
+            copy(
+                    identityServer = Success(identityService.getCurrentIdentityServerUrl()),
+                    userConsent = identityService.getUserConsent()
+            )
         }
         startListenToIdentityManager()
         observeThreePids()
@@ -97,6 +103,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
             DiscoverySettingsAction.RetrieveBinding          -> retrieveBinding()
             DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer()
             is DiscoverySettingsAction.ChangeIdentityServer  -> changeIdentityServer(action)
+            is DiscoverySettingsAction.UpdateUserConsent     -> handleUpdateUserConsent(action)
             is DiscoverySettingsAction.RevokeThreePid        -> revokeThreePid(action)
             is DiscoverySettingsAction.ShareThreePid         -> shareThreePid(action)
             is DiscoverySettingsAction.FinalizeBind3pid      -> finalizeBind3pid(action, true)
@@ -105,13 +112,23 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
         }.exhaustive
     }
 
+    private fun handleUpdateUserConsent(action: DiscoverySettingsAction.UpdateUserConsent) {
+        identityService.setUserConsent(action.newConsent)
+        setState { copy(userConsent = action.newConsent) }
+    }
+
     private fun disconnectIdentityServer() {
         setState { copy(identityServer = Loading()) }
 
         viewModelScope.launch {
             try {
                 awaitCallback<Unit> { session.identityService().disconnect(it) }
-                setState { copy(identityServer = Success(null)) }
+                setState {
+                    copy(
+                            identityServer = Success(null),
+                            userConsent = false
+                    )
+                }
             } catch (failure: Throwable) {
                 setState { copy(identityServer = Fail(failure)) }
             }
@@ -126,7 +143,12 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
                 val data = awaitCallback<String?> {
                     session.identityService().setNewIdentityServer(action.url, it)
                 }
-                setState { copy(identityServer = Success(data)) }
+                setState {
+                    copy(
+                            identityServer = Success(data),
+                            userConsent = false
+                    )
+                }
                 retrieveBinding()
             } catch (failure: Throwable) {
                 setState { copy(identityServer = Fail(failure)) }
diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
index 588d939635..a17aa4dbf2 100644
--- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt
@@ -17,6 +17,7 @@
 
 package im.vector.app.features.grouplist
 
+import androidx.lifecycle.viewModelScope
 import arrow.core.Option
 import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
@@ -28,7 +29,7 @@ import im.vector.app.core.platform.VectorViewModel
 import im.vector.app.core.resources.StringProvider
 import io.reactivex.Observable
 import io.reactivex.functions.BiFunction
-import org.matrix.android.sdk.api.NoOpMatrixCallback
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams
@@ -95,7 +96,9 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
     private fun handleSelectGroup(action: GroupListAction.SelectGroup) = withState { state ->
         if (state.selectedGroup?.groupId != action.groupSummary.groupId) {
             // We take care of refreshing group data when selecting to be sure we get all the rooms and users
-            session.getGroup(action.groupSummary.groupId)?.fetchGroupData(NoOpMatrixCallback())
+            viewModelScope.launch {
+                session.getGroup(action.groupSummary.groupId)?.fetchGroupData()
+            }
             setState { copy(selectedGroup = action.groupSummary) }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
index 99adc0bf83..8891218a11 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
@@ -16,6 +16,8 @@
 
 package im.vector.app.features.home.room.detail
 
+import android.net.Uri
+import android.view.View
 import im.vector.app.core.platform.VectorViewModelAction
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -24,6 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.widgets.model.Widget
+import org.matrix.android.sdk.api.util.MatrixItem
 
 sealed class RoomDetailAction : VectorViewModelAction {
     data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction()
@@ -90,4 +93,9 @@ sealed class RoomDetailAction : VectorViewModelAction {
 
     data class OpenOrCreateDm(val userId: String) : RoomDetailAction()
     data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
+    object QuickActionInvitePeople : RoomDetailAction()
+    object QuickActionSetAvatar : RoomDetailAction()
+    data class SetAvatarAction(val newAvatarUri: Uri, val newAvatarFileName: String) : RoomDetailAction()
+    object QuickActionSetTopic : RoomDetailAction()
+    data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val transitionView: View?) : RoomDetailAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 9c6c473a7f..bec84f6479 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -71,6 +71,7 @@ import com.google.android.material.textfield.TextInputEditText
 import com.jakewharton.rxbinding3.widget.textChanges
 import im.vector.app.R
 import im.vector.app.core.dialogs.ConfirmationDialogBuilder
+import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
 import im.vector.app.core.dialogs.withColoredButton
 import im.vector.app.core.epoxy.LayoutManagerStateRestorer
 import im.vector.app.core.extensions.cleanup
@@ -82,6 +83,7 @@ import im.vector.app.core.extensions.showKeyboard
 import im.vector.app.core.extensions.trackItemsVisibilityChange
 import im.vector.app.core.glide.GlideApp
 import im.vector.app.core.glide.GlideRequests
+import im.vector.app.core.intent.getFilenameFromUri
 import im.vector.app.core.intent.getMimeTypeFromUri
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.core.resources.ColorProvider
@@ -149,6 +151,7 @@ import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.permalink.NavigationInterceptor
 import im.vector.app.features.permalink.PermalinkHandler
 import im.vector.app.features.reactions.EmojiReactionPickerActivity
+import im.vector.app.features.roomprofile.RoomProfileActivity
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity
 import im.vector.app.features.share.SharedData
@@ -196,6 +199,7 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
 import timber.log.Timber
 import java.io.File
 import java.net.URL
+import java.util.UUID
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
@@ -229,7 +233,7 @@ class RoomDetailFragment @Inject constructor(
         JumpToReadMarkerView.Callback,
         AttachmentTypeSelectorView.Callback,
         AttachmentsHelper.Callback,
-//        RoomWidgetsBannerView.Callback,
+        GalleryOrCameraDialogHelper.Listener,
         ActiveCallView.Callback {
 
     companion object {
@@ -250,6 +254,8 @@ class RoomDetailFragment @Inject constructor(
         private const val ircPattern = " (IRC)"
     }
 
+    private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
+
     private val roomDetailArgs: RoomDetailArgs by args()
     private val glideRequests by lazy {
         GlideApp.with(this)
@@ -364,6 +370,12 @@ class RoomDetailFragment @Inject constructor(
                 RoomDetailViewEvents.HideWaitingView                     -> vectorBaseActivity.hideWaitingView()
                 is RoomDetailViewEvents.RequestNativeWidgetPermission    -> requestNativeWidgetPermission(it)
                 is RoomDetailViewEvents.OpenRoom                         -> handleOpenRoom(it)
+                RoomDetailViewEvents.OpenInvitePeople                    -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
+                RoomDetailViewEvents.OpenSetRoomAvatarDialog             -> galleryOrCameraDialogHelper.show()
+                RoomDetailViewEvents.OpenRoomSettings                    -> handleOpenRoomSettings()
+                is RoomDetailViewEvents.ShowRoomAvatarFullScreen         -> it.matrixItem?.let { item ->
+                    navigator.openBigImageViewer(requireActivity(), it.view, item)
+                }
             }.exhaustive
         }
 
@@ -372,6 +384,24 @@ class RoomDetailFragment @Inject constructor(
         }
     }
 
+    override fun onImageReady(uri: Uri?) {
+        uri ?: return
+        roomDetailViewModel.handle(
+                RoomDetailAction.SetAvatarAction(
+                        newAvatarUri = uri,
+                        newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()
+                )
+        )
+    }
+
+    private fun handleOpenRoomSettings() {
+        navigator.openRoomProfile(
+                requireContext(),
+                roomDetailArgs.roomId,
+                RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS
+        )
+    }
+
     private fun handleOpenRoom(openRoom: RoomDetailViewEvents.OpenRoom) {
         navigator.openRoom(requireContext(), openRoom.roomId, null)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
index b9e3e6b31d..d5d94a0ca5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
@@ -17,10 +17,12 @@
 package im.vector.app.features.home.room.detail
 
 import android.net.Uri
+import android.view.View
 import androidx.annotation.StringRes
 import im.vector.app.core.platform.VectorViewEvents
 import im.vector.app.features.command.Command
 import org.matrix.android.sdk.api.session.widgets.model.Widget
+import org.matrix.android.sdk.api.util.MatrixItem
 import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
 import java.io.File
 
@@ -43,6 +45,11 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
     data class NavigateToEvent(val eventId: String) : RoomDetailViewEvents()
     data class JoinJitsiConference(val widget: Widget, val withVideo: Boolean) : RoomDetailViewEvents()
 
+    object OpenInvitePeople : RoomDetailViewEvents()
+    object OpenSetRoomAvatarDialog : RoomDetailViewEvents()
+    object OpenRoomSettings : RoomDetailViewEvents()
+    data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents()
+
     object ShowWaitingView : RoomDetailViewEvents()
     object HideWaitingView : RoomDetailViewEvents()
 
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..beaecbb898 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
@@ -50,6 +50,7 @@ import io.reactivex.functions.BiFunction
 import io.reactivex.rxkotlin.subscribeBy
 import io.reactivex.schedulers.Schedulers
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.NonCancellable
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.commonmark.parser.Parser
@@ -99,6 +100,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
@@ -275,9 +277,39 @@ class RoomDetailViewModel @AssistedInject constructor(
             is RoomDetailAction.CancelSend                       -> handleCancel(action)
             is RoomDetailAction.OpenOrCreateDm                   -> handleOpenOrCreateDm(action)
             is RoomDetailAction.JumpToReadReceipt                -> handleJumpToReadReceipt(action)
+            RoomDetailAction.QuickActionInvitePeople             -> handleInvitePeople()
+            RoomDetailAction.QuickActionSetAvatar                -> handleQuickSetAvatar()
+            is RoomDetailAction.SetAvatarAction                  -> handleSetNewAvatar(action)
+            RoomDetailAction.QuickActionSetTopic                 -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings)
+            is RoomDetailAction.ShowRoomAvatarFullScreen         -> {
+                _viewEvents.post(
+                        RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView)
+                )
+            }
         }.exhaustive
     }
 
+    private fun handleSetNewAvatar(action: RoomDetailAction.SetAvatarAction) {
+        viewModelScope.launch(Dispatchers.IO) {
+            try {
+                awaitCallback<Unit> {
+                    room.updateAvatar(action.newAvatarUri, action.newAvatarFileName, it)
+                }
+                _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
+            } catch (failure: Throwable) {
+                _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
+            }
+        }
+    }
+
+    private fun handleInvitePeople() {
+        _viewEvents.post(RoomDetailViewEvents.OpenInvitePeople)
+    }
+
+    private fun handleQuickSetAvatar() {
+        _viewEvents.post(RoomDetailViewEvents.OpenSetRoomAvatarDialog)
+    }
+
     private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) {
         val existingDmRoomId = session.getExistingDirectRoomWithUser(action.userId)
         if (existingDmRoomId == null) {
@@ -475,22 +507,24 @@ class RoomDetailViewModel @AssistedInject constructor(
      * Convert a send mode to a draft and save the draft
      */
     private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) = withState {
-        when {
-            it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> {
-                setState { copy(sendMode = it.sendMode.copy(action.draft)) }
-                room.saveDraft(UserDraft.REGULAR(action.draft), NoOpMatrixCallback())
-            }
-            it.sendMode is SendMode.REPLY                               -> {
-                setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
-                room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
-            }
-            it.sendMode is SendMode.QUOTE                               -> {
-                setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
-                room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
-            }
-            it.sendMode is SendMode.EDIT                                -> {
-                setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
-                room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
+        viewModelScope.launch(NonCancellable) {
+            when {
+                it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> {
+                    setState { copy(sendMode = it.sendMode.copy(action.draft)) }
+                    room.saveDraft(UserDraft.REGULAR(action.draft))
+                }
+                it.sendMode is SendMode.REPLY                               -> {
+                    setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
+                    room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft))
+                }
+                it.sendMode is SendMode.QUOTE                               -> {
+                    setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
+                    room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft))
+                }
+                it.sendMode is SendMode.EDIT                                -> {
+                    setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
+                    room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft))
+                }
             }
         }
     }
@@ -777,7 +811,9 @@ class RoomDetailViewModel @AssistedInject constructor(
         } else {
             // Otherwise we clear the composer and remove the draft from db
             setState { copy(sendMode = SendMode.REGULAR("", false)) }
-            room.deleteDraft(NoOpMatrixCallback())
+            viewModelScope.launch {
+                room.deleteDraft()
+            }
         }
     }
 
@@ -1112,15 +1148,15 @@ class RoomDetailViewModel @AssistedInject constructor(
     }
 
     private fun handleReportContent(action: RoomDetailAction.ReportContent) {
-        room.reportContent(action.eventId, -100, action.reason, object : MatrixCallback<Unit> {
-            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/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt
index e7a911ceb1..23bd041e95 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt
@@ -31,10 +31,14 @@ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEve
 import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_
 import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem
 import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_
+import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod
 import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
 import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
 import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
@@ -187,6 +191,11 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
                 collapsedEventIds.removeAll(mergedEventIds)
             }
             val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
+            val powerLevelsHelper = roomSummaryHolder.roomSummary?.roomId
+                    ?.let { activeSessionHolder.getSafeActiveSession()?.getRoom(it) }
+                    ?.let { it.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)?.content?.toModel<PowerLevelsContent>() }
+                    ?.let { PowerLevelsHelper(it) }
+            val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: ""
             val attributes = MergedRoomCreationItem.Attributes(
                     isCollapsed = isCollapsed,
                     mergeData = mergedData,
@@ -198,13 +207,19 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
                     hasEncryptionEvent = hasEncryption,
                     isEncryptionAlgorithmSecure = encryptionAlgorithm == MXCRYPTO_ALGORITHM_MEGOLM,
                     readReceiptsCallback = callback,
-                    currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: ""
+                    callback = callback,
+                    currentUserId = currentUserId,
+                    roomSummary = roomSummaryHolder.roomSummary,
+                    canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false,
+                    canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false,
+                    canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false
             )
             MergedRoomCreationItem_()
                     .id(mergeId)
                     .leftGuideline(avatarSizeProvider.leftGuideline)
                     .highlighted(isCollapsed && highlighted)
                     .attributes(attributes)
+                    .movementMethod(createLinkMovementMethod(callback))
                     .also {
                         it.setOnVisibilityStateChanged(MergedTimelineEventVisibilityStateChangedListener(callback, mergedEvents))
                     }
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<RoomServerAclContent>() ?: return null
+        val prevEventContent = event.resolvedPrevContent()?.toModel<RoomServerAclContent>()
+
+        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/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt
index 1896a812fc..34b9ae1b9d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt
@@ -16,11 +16,14 @@
 
 package im.vector.app.features.home.room.detail.timeline.item
 
+import android.text.SpannableString
+import android.text.method.MovementMethod
+import android.text.style.ClickableSpan
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
-import android.widget.RelativeLayout
 import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.ContextCompat
 import androidx.core.view.isGone
 import androidx.core.view.isVisible
@@ -28,8 +31,16 @@ import androidx.core.view.updateLayoutParams
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
+import im.vector.app.core.extensions.setTextOrHide
+import im.vector.app.core.utils.DebouncedClickListener
+import im.vector.app.core.utils.tappableMatchingText
 import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.RoomDetailAction
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.tools.linkify
+import me.gujun.android.span.span
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.util.toMatrixItem
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
 abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.Holder>() {
@@ -37,11 +48,16 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
     @EpoxyAttribute
     override lateinit var attributes: Attributes
 
+    @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+    var movementMethod: MovementMethod? = null
+
     override fun getViewType() = STUB_ID
 
     override fun bind(holder: Holder) {
         super.bind(holder)
 
+        bindCreationSummaryTile(holder)
+
         if (attributes.isCollapsed) {
             // Take the oldest data
             val data = distinctMergeData.lastOrNull()
@@ -70,34 +86,7 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
                 holder.avatarView.visibility = View.GONE
             }
 
-            if (attributes.hasEncryptionEvent) {
-                holder.encryptionTile.isVisible = true
-                holder.encryptionTile.updateLayoutParams<RelativeLayout.LayoutParams> {
-                    this.marginEnd = leftGuideline
-                }
-                if (attributes.isEncryptionAlgorithmSecure) {
-                    holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled)
-                    holder.e2eTitleDescriptionView.text = if (data?.isDirectRoom == true) {
-                        holder.expandView.resources.getString(R.string.direct_room_encryption_enabled_tile_description)
-                    } else {
-                        holder.expandView.resources.getString(R.string.encryption_enabled_tile_description)
-                    }
-                    holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER
-                    holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
-                            ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black),
-                            null, null, null
-                    )
-                } else {
-                    holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled)
-                    holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description)
-                    holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
-                            ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning),
-                            null, null, null
-                    )
-                }
-            } else {
-                holder.encryptionTile.isVisible = false
-            }
+            bindEncryptionTile(holder, data)
         } else {
             holder.avatarView.visibility = View.INVISIBLE
             holder.summaryView.visibility = View.GONE
@@ -107,6 +96,109 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
         holder.readReceiptsView.isVisible = false
     }
 
+    private fun bindEncryptionTile(holder: Holder, data: Data?) {
+        if (attributes.hasEncryptionEvent) {
+            holder.encryptionTile.isVisible = true
+            holder.encryptionTile.updateLayoutParams<ConstraintLayout.LayoutParams> {
+                this.marginEnd = leftGuideline
+            }
+            if (attributes.isEncryptionAlgorithmSecure) {
+                holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled)
+                holder.e2eTitleDescriptionView.text = if (data?.isDirectRoom == true) {
+                    holder.expandView.resources.getString(R.string.direct_room_encryption_enabled_tile_description)
+                } else {
+                    holder.expandView.resources.getString(R.string.encryption_enabled_tile_description)
+                }
+                holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER
+                holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
+                        ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black),
+                        null, null, null
+                )
+            } else {
+                holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled)
+                holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description)
+                holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
+                        ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning),
+                        null, null, null
+                )
+            }
+        } else {
+            holder.encryptionTile.isVisible = false
+        }
+    }
+
+    private fun bindCreationSummaryTile(holder: Holder) {
+        val roomSummary = attributes.roomSummary
+        val roomDisplayName = roomSummary?.displayName
+        holder.roomNameText.setTextOrHide(roomDisplayName)
+        val isDirect = roomSummary?.isDirect == true
+        val membersCount = roomSummary?.otherMemberIds?.size ?: 0
+
+        if (isDirect) {
+            holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_dm, roomSummary?.displayName ?: "")
+        } else if (roomDisplayName.isNullOrBlank() || roomSummary.name.isBlank()) {
+            holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_room_no_name)
+        } else {
+            holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_room, roomDisplayName)
+        }
+
+        val topic = roomSummary?.topic
+        if (topic.isNullOrBlank()) {
+            // do not show hint for DMs or group DMs
+            if (!isDirect) {
+                val addTopicLink = holder.view.resources.getString(R.string.add_a_topic_link_text)
+                val styledText = SpannableString(holder.view.resources.getString(R.string.room_created_summary_no_topic_creation_text, addTopicLink))
+                holder.roomTopicText.setTextOrHide(styledText.tappableMatchingText(addTopicLink, object : ClickableSpan() {
+                    override fun onClick(widget: View) {
+                        attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetTopic)
+                    }
+                }))
+            }
+        } else {
+            holder.roomTopicText.setTextOrHide(
+                    span {
+                        span(holder.view.resources.getString(R.string.topic_prefix)) {
+                            textStyle = "bold"
+                        }
+                        +topic.linkify(attributes.callback)
+                    }
+            )
+        }
+        holder.roomTopicText.movementMethod = movementMethod
+
+        val roomItem = roomSummary?.toMatrixItem()
+        val shouldSetAvatar = attributes.canChangeAvatar
+                && (roomSummary?.isDirect == false || (isDirect && membersCount >= 2))
+                && roomItem?.avatarUrl.isNullOrBlank()
+
+        holder.roomAvatarImageView.isVisible = roomItem != null
+        if (roomItem != null) {
+            attributes.avatarRenderer.render(roomItem, holder.roomAvatarImageView)
+            holder.roomAvatarImageView.setOnClickListener(DebouncedClickListener({ view ->
+                if (shouldSetAvatar) {
+                    attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar)
+                } else {
+                    // Note: this is no op if there is no avatar on the room
+                    attributes.callback?.onTimelineItemAction(RoomDetailAction.ShowRoomAvatarFullScreen(roomItem, view))
+                }
+            }))
+        }
+
+        holder.setAvatarButton.isVisible = shouldSetAvatar
+        if (shouldSetAvatar) {
+            holder.setAvatarButton.setOnClickListener(DebouncedClickListener({ _ ->
+                attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar)
+            }))
+        }
+
+        holder.addPeopleButton.isVisible = !isDirect
+        if (!isDirect) {
+            holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ ->
+                attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople)
+            }))
+        }
+    }
+
     class Holder : BasedMergedItem.Holder(STUB_ID) {
         val summaryView by bind<TextView>(R.id.itemNoticeTextView)
         val avatarView by bind<ImageView>(R.id.itemNoticeAvatarView)
@@ -114,6 +206,13 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
 
         val e2eTitleTextView by bind<TextView>(R.id.itemVerificationDoneTitleTextView)
         val e2eTitleDescriptionView by bind<TextView>(R.id.itemVerificationDoneDetailTextView)
+
+        val roomNameText by bind<TextView>(R.id.roomNameTileText)
+        val roomDescriptionText by bind<TextView>(R.id.roomNameDescriptionText)
+        val roomTopicText by bind<TextView>(R.id.roomNameTopicText)
+        val roomAvatarImageView by bind<ImageView>(R.id.creationTileRoomAvatarImageView)
+        val addPeopleButton by bind<View>(R.id.creationTileAddPeopleButton)
+        val setAvatarButton by bind<View>(R.id.creationTileSetAvatarButton)
     }
 
     companion object {
@@ -126,8 +225,13 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
             override val avatarRenderer: AvatarRenderer,
             override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
             override val onCollapsedStateChanged: (Boolean) -> Unit,
+            val callback: TimelineEventController.Callback? = null,
             val currentUserId: String,
             val hasEncryptionEvent: Boolean,
-            val isEncryptionAlgorithmSecure: Boolean
+            val isEncryptionAlgorithmSecure: Boolean,
+            val roomSummary: RoomSummary?,
+            val canChangeAvatar: Boolean = false,
+            val canChangeName: Boolean = false,
+            val canChangeTopic: Boolean = false
     ) : BasedMergedItem.Attributes
 }
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<Unit> {
-            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<Unit> { room.deleteTag(tagToRemove, it) }
+                                    room.deleteTag(tagToRemove)
                                 }
 
                         // Set the tag. We do not handle the order for the moment
-                        awaitCallback<Unit> {
-                            room.addTag(action.tag, 0.5, it)
-                        }
+                        room.addTag(action.tag, 0.5)
                     } else {
-                        awaitCallback<Unit> {
-                            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/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index 106d804cd3..2d0ca86d52 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -248,8 +248,8 @@ class DefaultNavigator @Inject constructor(
         context.startActivity(KeysBackupManageActivity.intent(context))
     }
 
-    override fun openRoomProfile(context: Context, roomId: String) {
-        context.startActivity(RoomProfileActivity.newIntent(context, roomId))
+    override fun openRoomProfile(context: Context, roomId: String, directAccess: Int?) {
+        context.startActivity(RoomProfileActivity.newIntent(context, roomId, directAccess))
     }
 
     override fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) {
diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
index 1d01a5e4f0..504fccb63a 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
@@ -78,7 +78,7 @@ interface Navigator {
 
     fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean = false)
 
-    fun openRoomProfile(context: Context, roomId: String)
+    fun openRoomProfile(context: Context, roomId: String, directAccess: Int? = null)
 
     fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem)
 
diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
index 119be66f94..c1118e40cb 100644
--- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
+++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
@@ -18,10 +18,9 @@ package im.vector.app.features.raw.wellknown
 
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.raw.RawService
-import org.matrix.android.sdk.internal.util.awaitCallback
 
 suspend fun RawService.getElementWellknown(userId: String): ElementWellKnown? {
-    return tryOrNull { awaitCallback<String> { getWellknown(userId, it) } }
+    return tryOrNull { getWellknown(userId) }
             ?.let { ElementWellKnownMapper.from(it) }
 }
 
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt
index 85bc8773a5..073d30ff8e 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt
@@ -21,6 +21,7 @@ import im.vector.app.core.platform.VectorViewModelAction
 import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
 
 sealed class RoomProfileAction : VectorViewModelAction {
+    object EnableEncryption : RoomProfileAction()
     object LeaveRoom : RoomProfileAction()
     data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction()
     object ShareRoomProfile : RoomProfileAction()
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
index 734620e378..609042ffa4 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
@@ -46,10 +46,16 @@ class RoomProfileActivity :
 
     companion object {
 
-        fun newIntent(context: Context, roomId: String): Intent {
+        private const val EXTRA_DIRECT_ACCESS = "EXTRA_DIRECT_ACCESS"
+
+        const val EXTRA_DIRECT_ACCESS_ROOM_ROOT = 0
+        const val EXTRA_DIRECT_ACCESS_ROOM_SETTINGS = 1
+
+        fun newIntent(context: Context, roomId: String, directAccess: Int?): Intent {
             val roomProfileArgs = RoomProfileArgs(roomId)
             return Intent(context, RoomProfileActivity::class.java).apply {
                 putExtra(MvRx.KEY_ARG, roomProfileArgs)
+                putExtra(EXTRA_DIRECT_ACCESS, directAccess)
             }
         }
     }
@@ -80,7 +86,13 @@ class RoomProfileActivity :
         sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
         roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return
         if (isFirstCreation()) {
-            addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs)
+            when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) {
+                EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> {
+                    addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs)
+                    addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs)
+                }
+                else -> addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs)
+            }
         }
         sharedActionViewModel
                 .observe()
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
index 7dc744da31..891d15d04f 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
@@ -28,6 +28,7 @@ import im.vector.app.core.ui.list.genericFooterItem
 import im.vector.app.features.home.ShortcutCreator
 import im.vector.app.features.settings.VectorPreferences
 import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import javax.inject.Inject
 
 class RoomProfileController @Inject constructor(
@@ -43,6 +44,7 @@ class RoomProfileController @Inject constructor(
 
     interface Callback {
         fun onLearnMoreClicked()
+        fun onEnableEncryptionClicked()
         fun onMemberListClicked()
         fun onBannedMemberListClicked()
         fun onNotificationsClicked()
@@ -84,6 +86,7 @@ class RoomProfileController @Inject constructor(
             centered(false)
             text(stringProvider.getString(learnMoreSubtitle))
         }
+        buildEncryptionAction(data.actionPermissions, roomSummary)
 
         // More
         buildProfileSection(stringProvider.getString(R.string.room_profile_section_more))
@@ -171,4 +174,29 @@ class RoomProfileController @Inject constructor(
             )
         }
     }
+
+    private fun buildEncryptionAction(actionPermissions: RoomProfileViewState.ActionPermissions, roomSummary: RoomSummary) {
+        if (!roomSummary.isEncrypted) {
+            if (actionPermissions.canEnableEncryption) {
+                buildProfileAction(
+                        id = "enableEncryption",
+                        title = stringProvider.getString(R.string.room_settings_enable_encryption),
+                        dividerColor = dividerColor,
+                        icon = R.drawable.ic_shield_black,
+                        divider = false,
+                        editable = false,
+                        action = { callback?.onEnableEncryptionClicked() }
+                )
+            } else {
+                buildProfileAction(
+                        id = "enableEncryption",
+                        title = stringProvider.getString(R.string.room_settings_enable_encryption_no_permission),
+                        dividerColor = dividerColor,
+                        icon = R.drawable.ic_shield_black,
+                        divider = false,
+                        editable = false
+                )
+            }
+        }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 5bd121d49b..bab64aebe9 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -49,6 +49,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
 import im.vector.app.features.media.BigImageViewerActivity
 import kotlinx.android.parcel.Parcelize
 import kotlinx.android.synthetic.main.fragment_matrix_profile.*
+import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
 import kotlinx.android.synthetic.main.view_stub_room_profile_header.*
 import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
 import org.matrix.android.sdk.api.util.MatrixItem
@@ -87,6 +88,7 @@ class RoomProfileFragment @Inject constructor(
             it.layoutResource = R.layout.view_stub_room_profile_header
             it.inflate()
         }
+        setupWaitingView()
         setupToolbar(matrixProfileToolbar)
         setupRecyclerView()
         appBarStateChangeListener = MatrixItemAppBarStateChangeListener(
@@ -111,6 +113,11 @@ class RoomProfileFragment @Inject constructor(
         setupLongClicks()
     }
 
+    private fun setupWaitingView() {
+        waiting_view_status_text.setText(R.string.please_wait)
+        waiting_view_status_text.isVisible = true
+    }
+
     private fun setupLongClicks() {
         roomProfileNameView.copyOnLongClick()
         roomProfileAliasView.copyOnLongClick()
@@ -155,6 +162,8 @@ class RoomProfileFragment @Inject constructor(
     }
 
     override fun invalidate() = withState(roomProfileViewModel) { state ->
+        waiting_view.isVisible = state.isLoading
+
         state.roomSummary()?.also {
             if (it.membership.isLeft()) {
                 Timber.w("The room has been left")
@@ -187,6 +196,17 @@ class RoomProfileFragment @Inject constructor(
         vectorBaseActivity.notImplemented()
     }
 
+    override fun onEnableEncryptionClicked() {
+        AlertDialog.Builder(requireActivity())
+                .setTitle(R.string.room_settings_enable_encryption_dialog_title)
+                .setMessage(R.string.room_settings_enable_encryption_dialog_content)
+                .setNegativeButton(R.string.cancel, null)
+                .setPositiveButton(R.string.room_settings_enable_encryption_dialog_submit) { _, _ ->
+                    roomProfileViewModel.handle(RoomProfileAction.EnableEncryption)
+                }
+                .show()
+    }
+
     override fun onMemberListClicked() {
         roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomMembers)
     }
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..ec772ffcaa 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
@@ -28,12 +28,15 @@ import im.vector.app.core.extensions.exhaustive
 import im.vector.app.core.platform.VectorViewModel
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.features.home.ShortcutCreator
+import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
 import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
 import org.matrix.android.sdk.rx.RxRoom
 import org.matrix.android.sdk.rx.rx
 import org.matrix.android.sdk.rx.unwrap
@@ -65,6 +68,7 @@ class RoomProfileViewModel @AssistedInject constructor(
         val rxRoom = room.rx()
         observeRoomSummary(rxRoom)
         observeBannedRoomMembers(rxRoom)
+        observePermissions()
     }
 
     private fun observeRoomSummary(rxRoom: RxRoom) {
@@ -82,8 +86,22 @@ class RoomProfileViewModel @AssistedInject constructor(
                 }
     }
 
+    private fun observePermissions() {
+        PowerLevelsObservableFactory(room)
+                .createObservable()
+                .subscribe {
+                    val powerLevelsHelper = PowerLevelsHelper(it)
+                    val permissions = RoomProfileViewState.ActionPermissions(
+                            canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
+                    )
+                    setState { copy(actionPermissions = permissions) }
+                }
+                .disposeOnClear()
+    }
+
     override fun handle(action: RoomProfileAction) {
         when (action) {
+            is RoomProfileAction.EnableEncryption            -> handleEnableEncryption()
             RoomProfileAction.LeaveRoom                      -> handleLeaveRoom()
             is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
             is RoomProfileAction.ShareRoomProfile            -> handleShareRoomProfile()
@@ -91,6 +109,24 @@ class RoomProfileViewModel @AssistedInject constructor(
         }.exhaustive
     }
 
+    private fun handleEnableEncryption() {
+        postLoading(true)
+
+        viewModelScope.launch {
+            val result = runCatching { room.enableEncryption() }
+            postLoading(false)
+            result.onFailure { failure ->
+                _viewEvents.post(RoomProfileViewEvents.Failure(failure))
+            }
+        }
+    }
+
+    private fun postLoading(isLoading: Boolean) {
+        setState {
+            copy(isLoading = isLoading)
+        }
+    }
+
     private fun handleCreateShortcut() {
         viewModelScope.launch(Dispatchers.IO) {
             withState { state ->
@@ -102,11 +138,13 @@ class RoomProfileViewModel @AssistedInject constructor(
     }
 
     private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) {
-        room.setRoomNotificationState(action.notificationState, object : MatrixCallback<Unit> {
-            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/RoomProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt
index 50723655bc..398982ede1 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt
@@ -26,8 +26,14 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
 data class RoomProfileViewState(
         val roomId: String,
         val roomSummary: Async<RoomSummary> = Uninitialized,
-        val bannedMembership: Async<List<RoomMemberSummary>> = Uninitialized
+        val bannedMembership: Async<List<RoomMemberSummary>> = Uninitialized,
+        val actionPermissions: ActionPermissions = ActionPermissions(),
+        val isLoading: Boolean = false
 ) : MvRxState {
 
     constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
+
+    data class ActionPermissions(
+            val canEnableEncryption: Boolean = false
+    )
 }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt
index 80bb8813cf..f0a7b38478 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt
@@ -25,7 +25,6 @@ sealed class RoomSettingsAction : VectorViewModelAction {
     data class SetRoomTopic(val newTopic: String) : RoomSettingsAction()
     data class SetRoomHistoryVisibility(val visibility: RoomHistoryVisibility) : RoomSettingsAction()
     data class SetRoomCanonicalAlias(val newCanonicalAlias: String) : RoomSettingsAction()
-    object EnableEncryption : RoomSettingsAction()
     object Save : RoomSettingsAction()
     object Cancel : RoomSettingsAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
index 5231cc6b06..3c73e6ed46 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
@@ -29,7 +29,6 @@ import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibi
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
-import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.util.toMatrixItem
 import javax.inject.Inject
 
@@ -44,7 +43,6 @@ class RoomSettingsController @Inject constructor(
         // Delete the avatar, or cancel an avatar change
         fun onAvatarDelete()
         fun onAvatarChange()
-        fun onEnableEncryptionClicked()
         fun onNameChanged(name: String)
         fun onTopicChanged(topic: String)
         fun onHistoryVisibilityClicked()
@@ -130,33 +128,6 @@ class RoomSettingsController @Inject constructor(
                 editable = data.actionPermissions.canChangeHistoryReadability,
                 action = { if (data.actionPermissions.canChangeHistoryReadability) callback?.onHistoryVisibilityClicked() }
         )
-
-        buildEncryptionAction(data.actionPermissions, roomSummary)
-    }
-
-    private fun buildEncryptionAction(actionPermissions: RoomSettingsViewState.ActionPermissions, roomSummary: RoomSummary) {
-        if (!actionPermissions.canEnableEncryption) {
-            return
-        }
-        if (roomSummary.isEncrypted) {
-            buildProfileAction(
-                    id = "encryption",
-                    title = stringProvider.getString(R.string.room_settings_addresses_e2e_enabled),
-                    dividerColor = dividerColor,
-                    divider = false,
-                    editable = false
-            )
-        } else {
-            buildProfileAction(
-                    id = "encryption",
-                    title = stringProvider.getString(R.string.room_settings_enable_encryption),
-                    subtitle = stringProvider.getString(R.string.room_settings_enable_encryption_warning),
-                    dividerColor = dividerColor,
-                    divider = false,
-                    editable = true,
-                    action = { callback?.onEnableEncryptionClicked() }
-            )
-        }
     }
 
     private fun formatRoomHistoryVisibilityEvent(event: Event): String? {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
index 57521f7d80..ab9d3c6896 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
@@ -127,17 +127,6 @@ class RoomSettingsFragment @Inject constructor(
         invalidateOptionsMenu()
     }
 
-    override fun onEnableEncryptionClicked() {
-        AlertDialog.Builder(requireActivity())
-                .setTitle(R.string.room_settings_enable_encryption_dialog_title)
-                .setMessage(R.string.room_settings_enable_encryption_dialog_content)
-                .setNegativeButton(R.string.cancel, null)
-                .setPositiveButton(R.string.room_settings_enable_encryption_dialog_submit) { _, _ ->
-                    viewModel.handle(RoomSettingsAction.EnableEncryption)
-                }
-                .show()
-    }
-
     override fun onNameChanged(name: String) {
         viewModel.handle(RoomSettingsAction.SetRoomName(name))
     }
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..05a75a585b 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
@@ -27,7 +27,6 @@ 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 org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.Session
@@ -117,8 +116,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
                             canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
                                     EventType.STATE_ROOM_CANONICAL_ALIAS),
                             canChangeHistoryReadability = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
-                                    EventType.STATE_ROOM_HISTORY_VISIBILITY),
-                            canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
+                                    EventType.STATE_ROOM_HISTORY_VISIBILITY)
                     )
                     setState { copy(actionPermissions = permissions) }
                 }
@@ -141,7 +139,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
 
     override fun handle(action: RoomSettingsAction) {
         when (action) {
-            is RoomSettingsAction.EnableEncryption         -> handleEnableEncryption()
             is RoomSettingsAction.SetAvatarAction          -> handleSetAvatarAction(action)
             is RoomSettingsAction.SetRoomName              -> setState { copy(newName = action.newName) }
             is RoomSettingsAction.SetRoomTopic             -> setState { copy(newTopic = action.newTopic) }
@@ -225,21 +222,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
                 )
     }
 
-    private fun handleEnableEncryption() {
-        postLoading(true)
-
-        room.enableEncryption(callback = object : MatrixCallback<Unit> {
-            override fun onFailure(failure: Throwable) {
-                postLoading(false)
-                _viewEvents.post(RoomSettingsViewEvents.Failure(failure))
-            }
-
-            override fun onSuccess(data: Unit) {
-                postLoading(false)
-            }
-        })
-    }
-
     private fun postLoading(isLoading: Boolean) {
         setState {
             copy(isLoading = isLoading)
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt
index f913bed382..2cadc8f798 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt
@@ -47,8 +47,7 @@ data class RoomSettingsViewState(
             val canChangeName: Boolean = false,
             val canChangeTopic: Boolean = false,
             val canChangeCanonicalAlias: Boolean = false,
-            val canChangeHistoryReadability: Boolean = false,
-            val canEnableEncryption: Boolean = false
+            val canChangeHistoryReadability: Boolean = false
     )
 
     sealed class AvatarAction {
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
index 67b5c03638..8d9f8d7170 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
@@ -15,12 +15,13 @@
  */
 package im.vector.app.features.settings
 
+import androidx.lifecycle.lifecycleScope
 import androidx.preference.Preference
 import im.vector.app.R
 import im.vector.app.core.preference.PushRulePreference
 import im.vector.app.core.preference.VectorPreference
 import im.vector.app.core.utils.toast
-import org.matrix.android.sdk.api.MatrixCallback
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.pushrules.RuleIds
 import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind
 import javax.inject.Inject
@@ -50,29 +51,25 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor()
                         if (newRule != null) {
                             displayLoadingView()
 
-                            session.updatePushRuleActions(
-                                    ruleAndKind.kind,
-                                    preference.ruleAndKind?.pushRule ?: ruleAndKind.pushRule,
-                                    newRule,
-                                    object : MatrixCallback<Unit> {
-                                        override fun onSuccess(data: Unit) {
-                                            if (!isAdded) {
-                                                return
-                                            }
-                                            preference.setPushRule(ruleAndKind.copy(pushRule = newRule))
-                                            hideLoadingView()
-                                        }
-
-                                        override fun onFailure(failure: Throwable) {
-                                            if (!isAdded) {
-                                                return
-                                            }
-                                            hideLoadingView()
-                                            // Restore the previous value
-                                            refreshDisplay()
-                                            activity?.toast(errorFormatter.toHumanReadable(failure))
-                                        }
-                                    })
+                            lifecycleScope.launch {
+                                val result = runCatching {
+                                    session.updatePushRuleActions(ruleAndKind.kind,
+                                            preference.ruleAndKind?.pushRule ?: ruleAndKind.pushRule,
+                                            newRule)
+                                }
+                                if (!isAdded) {
+                                    return@launch
+                                }
+                                hideLoadingView()
+                                result.onSuccess {
+                                    preference.setPushRule(ruleAndKind.copy(pushRule = newRule))
+                                }
+                                result.onFailure { failure ->
+                                    // Restore the previous value
+                                    refreshDisplay()
+                                    activity?.toast(errorFormatter.toHumanReadable(failure))
+                                }
+                            }
                         }
                         false
                     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt
index 4bee1ac0c8..47868eed51 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt
@@ -23,6 +23,7 @@ import android.media.RingtoneManager
 import android.net.Uri
 import android.os.Parcelable
 import android.widget.Toast
+import androidx.lifecycle.lifecycleScope
 import androidx.preference.Preference
 import androidx.preference.SwitchPreference
 import im.vector.app.R
@@ -37,6 +38,7 @@ import im.vector.app.core.utils.isIgnoringBatteryOptimizations
 import im.vector.app.core.utils.requestDisablingBatteryOptimization
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.push.fcm.FcmHelper
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.pushrules.RuleIds
@@ -318,24 +320,22 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
                 .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
                 ?.let {
                     // Trick, we must enable this room to disable notifications
-                    pushRuleService.updatePushRuleEnableStatus(RuleKind.OVERRIDE,
-                            it,
-                            !switchPref.isChecked,
-                            object : MatrixCallback<Unit> {
-                                override fun onSuccess(data: Unit) {
-                                    // Push rules will be updated from the sync
-                                }
+                    lifecycleScope.launch {
+                        try {
+                            pushRuleService.updatePushRuleEnableStatus(RuleKind.OVERRIDE,
+                                    it,
+                                    !switchPref.isChecked)
+                            // Push rules will be updated from the sync
+                        } catch (failure: Throwable) {
+                            if (!isAdded) {
+                                return@launch
+                            }
 
-                                override fun onFailure(failure: Throwable) {
-                                    if (!isAdded) {
-                                        return
-                                    }
-
-                                    // revert the check box
-                                    switchPref.isChecked = !switchPref.isChecked
-                                    Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
-                                }
-                            })
+                            // revert the check box
+                            switchPref.isChecked = !switchPref.isChecked
+                            Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
+                        }
+                    }
                 }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
index 0c3390d0b0..b78dba07f5 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
@@ -20,7 +20,8 @@ import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.resources.StringProvider
-import org.matrix.android.sdk.api.MatrixCallback
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.pushrules.RuleIds
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import javax.inject.Inject
@@ -48,16 +49,12 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String
                     override fun doFix() {
                         if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished
 
-                        session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled,
-                                object : MatrixCallback<Unit> {
-                                    override fun onSuccess(data: Unit) {
-                                        manager?.retry(activityResultLauncher)
-                                    }
-
-                                    override fun onFailure(failure: Throwable) {
-                                        manager?.retry(activityResultLauncher)
-                                    }
-                                })
+                        GlobalScope.launch {
+                            runCatching {
+                                session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
+                            }
+                            manager?.retry(activityResultLauncher)
+                        }
                     }
                 }
                 status = TestStatus.FAILED
diff --git a/vector/src/main/res/drawable/ic_add_people.xml b/vector/src/main/res/drawable/ic_add_people.xml
new file mode 100644
index 0000000000..3ec60095ff
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_add_people.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19.1001,9C18.7779,9 18.5168,8.7388 18.5168,8.4167V6.0833H16.1834C15.8613,6.0833 15.6001,5.8222 15.6001,5.5C15.6001,5.1778 15.8613,4.9167 16.1834,4.9167H18.5168V2.5833C18.5168,2.2612 18.7779,2 19.1001,2C19.4223,2 19.6834,2.2612 19.6834,2.5833V4.9167H22.0168C22.3389,4.9167 22.6001,5.1778 22.6001,5.5C22.6001,5.8222 22.3389,6.0833 22.0168,6.0833H19.6834V8.4167C19.6834,8.7388 19.4223,9 19.1001,9ZM19.6001,11C20.0669,11 20.5212,10.9467 20.9574,10.8458C21.1161,11.5383 21.2,12.2594 21.2,13C21.2,16.1409 19.6917,18.9294 17.3598,20.6808V20.6807C16.0014,21.7011 14.3635,22.3695 12.5815,22.5505C12.2588,22.5832 11.9314,22.6 11.6,22.6C6.2981,22.6 2,18.302 2,13C2,7.6981 6.2981,3.4 11.6,3.4C12.3407,3.4 13.0618,3.4839 13.7543,3.6427C13.6534,4.0788 13.6001,4.5332 13.6001,5C13.6001,8.3137 16.2864,11 19.6001,11ZM11.5999,20.68C13.6754,20.68 15.5585,19.8567 16.9407,18.5189C16.0859,16.4086 14.0167,14.92 11.5998,14.92C9.183,14.92 7.1138,16.4086 6.259,18.5189C7.6411,19.8567 9.5244,20.68 11.5999,20.68ZM11.7426,7.4117C10.3168,7.5417 9.2,8.7404 9.2,10.2C9.2,11.7464 10.4536,13 12,13C13.0308,13 13.9315,12.443 14.4176,11.6135C13.0673,10.6058 12.0929,9.1225 11.7426,7.4117Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/layout/fragment_contacts_book.xml b/vector/src/main/res/layout/fragment_contacts_book.xml
index eb90da1bbe..1f8566e05e 100644
--- a/vector/src/main/res/layout/fragment_contacts_book.xml
+++ b/vector/src/main/res/layout/fragment_contacts_book.xml
@@ -93,6 +93,27 @@
             app:layout_constraintTop_toBottomOf="@+id/phoneBookFilterContainer"
             tools:visibility="visible" />
 
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/phoneBookSearchForMatrixContacts"
+            style="@style/VectorButtonStyleText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/layout_horizontal_margin"
+            android:layout_marginTop="4dp"
+            android:layout_marginEnd="@dimen/layout_horizontal_margin"
+            android:text="@string/phone_book_perform_lookup"
+            android:visibility="gone"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/phoneBookFilterContainer"
+            tools:visibility="visible" />
+
+        <androidx.constraintlayout.widget.Barrier
+            android:id="@+id/phoneBookBottomBarrier"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:barrierDirection="bottom"
+            app:constraint_referenced_ids="phoneBookSearchForMatrixContacts,phoneBookOnlyBoundContacts" />
+
         <View
             android:id="@+id/phoneBookFilterDivider"
             android:layout_width="0dp"
@@ -101,7 +122,7 @@
             android:background="?attr/vctr_list_divider_color"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/phoneBookOnlyBoundContacts" />
+            app:layout_constraintTop_toBottomOf="@+id/phoneBookBottomBarrier" />
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/phoneBookRecyclerView"
diff --git a/vector/src/main/res/layout/fragment_matrix_profile.xml b/vector/src/main/res/layout/fragment_matrix_profile.xml
index c935ab5cee..c10185b2f3 100644
--- a/vector/src/main/res/layout/fragment_matrix_profile.xml
+++ b/vector/src/main/res/layout/fragment_matrix_profile.xml
@@ -95,7 +95,6 @@
 
         </com.google.android.material.appbar.CollapsingToolbarLayout>
 
-
     </com.google.android.material.appbar.AppBarLayout>
 
     <androidx.recyclerview.widget.RecyclerView
@@ -105,4 +104,6 @@
         app:layout_behavior="@string/appbar_scrolling_view_behavior"
         tools:listitem="@layout/item_profile_action" />
 
+    <include layout="@layout/merge_overlay_waiting_view" />
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml
index eefa387254..728b90b696 100644
--- a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml
@@ -1,31 +1,149 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:layout_height="wrap_content">
 
-    <FrameLayout
-        android:id="@+id/creationEncryptionTile"
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/creationTile"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_gravity="center"
-        android:layout_marginTop="2dp"
-        android:layout_marginEnd="52dp"
-        android:layout_marginBottom="2dp"
-        android:background="@drawable/rounded_rect_shape_8"
-        android:padding="8dp">
+        android:layout_height="wrap_content">
 
-        <include layout="@layout/item_timeline_event_status_tile_stub" />
+        <FrameLayout
+            android:id="@+id/creationEncryptionTile"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"
+            android:layout_marginEnd="52dp"
+            android:layout_marginBottom="2dp"
+            android:background="@drawable/rounded_rect_shape_8"
+            android:padding="8dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
 
-    </FrameLayout>
+            <include layout="@layout/item_timeline_event_status_tile_stub" />
+
+        </FrameLayout>
+
+        <ImageView
+            android:id="@+id/creationTileRoomAvatarImageView"
+            android:layout_width="60dp"
+            android:layout_height="60dp"
+            android:layout_marginTop="20dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/creationEncryptionTile"
+            tools:srcCompat="@tools:sample/avatars" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/creationTileSetAvatarButton"
+            style="@style/Widget.MaterialComponents.Button.Icon"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:backgroundTint="?riotx_bottom_nav_background_color"
+            android:contentDescription="@string/room_settings_set_avatar"
+            android:elevation="2dp"
+            android:insetLeft="0dp"
+            android:insetTop="0dp"
+            android:insetRight="0dp"
+            android:insetBottom="0dp"
+            android:padding="0dp"
+            app:cornerRadius="30dp"
+            app:icon="@drawable/ic_camera"
+            app:iconGravity="textStart"
+            app:iconPadding="0dp"
+            app:iconSize="20dp"
+            app:iconTint="?riot_primary_text_color"
+            app:layout_constraintCircle="@+id/creationTileRoomAvatarImageView"
+            app:layout_constraintCircleAngle="135"
+            app:layout_constraintCircleRadius="34dp"
+            tools:ignore="MissingConstraints" />
+
+        <TextView
+            android:id="@+id/roomNameTileText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:layout_marginEnd="16dp"
+            android:textColor="?riotx_text_primary"
+            android:textSize="20sp"
+            android:textStyle="bold"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/creationTileRoomAvatarImageView"
+            tools:text="@sample/matrix.json/data/roomName" />
+
+        <TextView
+            android:id="@+id/roomNameDescriptionText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="16dp"
+            android:textColor="?riotx_text_secondary"
+            android:textSize="15sp"
+            android:textStyle="normal"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/roomNameTileText"
+            tools:text="@string/this_is_the_beginning_of_room_no_name" />
+
+        <TextView
+            android:id="@+id/roomNameTopicText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="16dp"
+            android:textColor="?riotx_text_secondary"
+            android:textColorLink="@color/riotx_accent"
+            android:textSize="15sp"
+            android:textStyle="normal"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/roomNameDescriptionText"
+            tools:text="@string/room_created_summary_no_topic_creation_text" />
+
+        <LinearLayout
+            android:id="@+id/creationTileAddPeopleButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:background="?attr/selectableItemBackgroundBorderless"
+            android:clickable="true"
+            android:contentDescription="@string/add_people"
+            android:focusable="true"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/roomNameTopicText">
+
+            <ImageView
+                android:id="@+id/addPeopleButtonBg"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:background="@drawable/circle"
+                android:backgroundTint="@color/riotx_accent"
+                android:importantForAccessibility="no"
+                android:scaleType="center"
+                android:src="@drawable/ic_add_people" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:importantForAccessibility="no"
+                android:text="@string/add_people"
+                android:textColor="@color/riotx_accent" />
+
+        </LinearLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
     <RelativeLayout
         android:id="@+id/mergedSumContainer"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_below="@+id/creationEncryptionTile">
+        android:layout_below="@+id/creationTile"
+        android:layout_marginTop="8dp">
 
         <ImageView
             android:id="@+id/itemNoticeAvatarView"
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 45d9d40ba6..6a96305b21 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -1793,6 +1793,14 @@
     <string name="settings_discovery_confirm_mail">We sent you a confirm email to %s, check your email and click on the confirmation link</string>
     <string name="settings_discovery_confirm_mail_not_clicked">We sent you a confirm email to %s, please first check your email and click on the confirmation link</string>
     <string name="settings_discovery_mail_pending">Pending</string>
+    <string name="settings_discovery_consent_title">Send emails and phone numbers</string>
+    <string name="settings_discovery_consent_notice_on">You have given your consent to send emails and phone numbers to this identity server to discover other users from your contacts.</string>
+    <string name="settings_discovery_consent_notice_off">You have not given your consent to send emails and phone numbers to this identity server to discover other users from your contacts.</string>
+    <string name="settings_discovery_consent_action_revoke">Revoke my consent</string>
+    <string name="settings_discovery_consent_action_give_consent">Give consent</string>
+
+    <string name="identity_server_consent_dialog_title">Send emails and phone numbers</string>
+    <string name="identity_server_consent_dialog_content">In order to discover existing contacts you know, do you accept to send your contact data (phone numbers and/or emails) to the configured Identity Server (%1$s)?\n\nFor more privacy, the sent data will be hashed before being sent.</string>
 
     <string name="settings_discovery_enter_identity_server">Enter an identity server URL</string>
     <string name="settings_discovery_bad_identity_server">Could not connect to identity server</string>
@@ -2192,7 +2200,8 @@
     <!-- Title for category in the settings which affect the behavior of the message editor (ex: enable Markdown, send typing notification, etc.) -->
     <string name="settings_category_composer">Message editor</string>
 
-    <string name="room_settings_enable_encryption">Enable end-to-end encryption</string>
+    <string name="room_settings_enable_encryption">Enable end-to-end encryption…</string>
+    <string name="room_settings_enable_encryption_no_permission">You don\'t have permission to enable encryption in this room.</string>
     <string name="room_settings_enable_encryption_warning">Once enabled, encryption cannot be disabled.</string>
 
     <string name="room_settings_enable_encryption_dialog_title">Enable encryption?</string>
@@ -2395,6 +2404,13 @@
     <string name="room_created_summary_item_by_you">You created and configured the room.</string>
     <string name="direct_room_created_summary_item">%s joined.</string>
     <string name="direct_room_created_summary_item_by_you">You joined.</string>
+    <string name="this_is_the_beginning_of_room">This is the beginning of %s.</string>
+    <string name="this_is_the_beginning_of_room_no_name">This is the beginning of this conversation.</string>
+    <string name="this_is_the_beginning_of_dm">This is the beginning of your direct message history with %s.</string>
+    <!-- First param will be replaced by the value of add_a_topic_link_text, that will be clickable-->
+    <string name="room_created_summary_no_topic_creation_text">%s to let people know what this room is about.</string>
+    <string name="add_a_topic_link_text">Add a topic</string>
+    <string name="topic_prefix">"Topic: "</string>
 
     <string name="qr_code_scanned_self_verif_notice">Almost there! Is the other device showing the same shield?</string>
     <string name="qr_code_scanned_verif_waiting_notice">Almost there! Waiting for confirmation…</string>
@@ -2503,6 +2519,7 @@
     <string name="create_room_dm_failure">"We couldn't create your DM. Please check the users you want to invite and try again."</string>
 
     <string name="add_members_to_room">Add members</string>
+    <string name="add_people">Add people</string>
     <string name="invite_users_to_room_action_invite">INVITE</string>
     <string name="inviting_users_to_room">Inviting users…</string>
     <string name="invite_users_to_room_title">Invite Users</string>
@@ -2527,6 +2544,7 @@
     <string name="identity_server_error_bulk_sha256_not_supported">For your privacy, Element only supports sending hashed user emails and phone number.</string>
     <string name="identity_server_error_binding_error">The association has failed.</string>
     <string name="identity_server_error_no_current_binding_error">The is no current association with this identifier.</string>
+    <string name="identity_server_user_consent_not_provided">The user consent has not been provided.</string>
 
     <string name="identity_server_set_default_notice">Your homeserver (%1$s) proposes to use %2$s for your identity server</string>
     <string name="identity_server_set_default_submit">Use %1$s</string>
@@ -2566,6 +2584,8 @@
     <string name="room_settings_name_hint">Room Name</string>
     <string name="room_settings_topic_hint">Topic</string>
     <string name="room_settings_save_success">You changed room settings successfully</string>
+    <string name="room_settings_set_avatar">Set avatar</string>
+
 
     <string name="notice_crypto_unable_to_decrypt_final">You cannot access this message</string>
     <string name="notice_crypto_unable_to_decrypt_friendly">Waiting for this message, this may take a while</string>
@@ -2593,6 +2613,7 @@
     <string name="loading_contact_book">Retrieving your contacts…</string>
     <string name="empty_contact_book">Your contact book is empty</string>
     <string name="contacts_book_title">Contacts book</string>
+    <string name="phone_book_perform_lookup">Search for contacts on Matrix</string>
 
     <string name="three_pid_revoke_invite_dialog_title">Revoke invite</string>
     <string name="three_pid_revoke_invite_dialog_content">Revoke invite to %1$s?</string>