diff --git a/CHANGES.md b/CHANGES.md
index 27229361a7..b4691a907b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,11 +5,15 @@ Features ✨:
  - Enable url previews for notices (#2562)
 
 Improvements 🙌:
- -
+  - Add System theme option and set as default (#904) (#2387)
 
 Bugfix 🐛:
+ - Unspecced msgType field in m.sticker (#2580)
+ - Wait for all room members to be known before sending a message to a e2e room (#2518)
  - Url previews sometimes attached to wrong message (#2561)
  - Room Topic not displayed correctly after visiting a link (#2551)
+ - Hiding membership events works the exact opposite (#2603)
+ - Tapping drawer having more than 1 room in notifications gives "malformed link" error (#2605)
 
 Translations 🗣:
  -
diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
index ae095be41a..9b1345cd39 100644
--- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
+++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
@@ -40,13 +40,16 @@ import kotlin.math.abs
 
 abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
 
-    lateinit var pager2: ViewPager2
-    lateinit var imageTransitionView: ImageView
-    lateinit var transitionImageContainer: ViewGroup
+    protected val pager2: ViewPager2
+        get() = views.attachmentPager
+    protected val imageTransitionView: ImageView
+        get() = views.transitionImageView
+    protected val transitionImageContainer: ViewGroup
+        get() = views.transitionImageContainer
 
-    var topInset = 0
-    var bottomInset = 0
-    var systemUiVisibility = true
+    private var topInset = 0
+    private var bottomInset = 0
+    private var systemUiVisibility = true
 
     private var overlayView: View? = null
         set(value) {
@@ -65,14 +68,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
     private lateinit var gestureDetector: GestureDetectorCompat
 
     var currentPosition = 0
+        private set
 
     private var swipeDirection: SwipeDirection? = null
 
     private fun isScaled() = attachmentsAdapter.isScaled(currentPosition)
 
+    private val attachmentsAdapter = AttachmentsAdapter()
+
     private var wasScaled: Boolean = false
     private var isSwipeToDismissAllowed: Boolean = true
-    private lateinit var attachmentsAdapter: AttachmentsAdapter
     private var isOverlayWasClicked = false
 
 //    private val shouldDismissToBottom: Boolean
@@ -101,10 +106,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
         views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
         setContentView(views.root)
         views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
-        attachmentsAdapter = AttachmentsAdapter()
         views.attachmentPager.adapter = attachmentsAdapter
-        imageTransitionView = views.transitionImageView
-        pager2 = views.attachmentPager
         directionDetector = createSwipeDirectionDetector()
         gestureDetector = createGestureDetector()
 
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
index 0e7088a6a5..cb49ee8818 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
@@ -86,7 +86,7 @@ class CommonTestHelper(context: Context) {
      *
      * @param session    the session to sync
      */
-    fun syncSession(session: Session) {
+    fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
         val lock = CountDownLatch(1)
 
         val job = GlobalScope.launch(Dispatchers.Main) {
@@ -109,7 +109,7 @@ class CommonTestHelper(context: Context) {
         }
         GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
 
-        await(lock)
+        await(lock, timeout)
     }
 
     /**
@@ -119,7 +119,7 @@ class CommonTestHelper(context: Context) {
      * @param message      the message to send
      * @param nbOfMessages the number of time the message will be sent
      */
-    fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
+    fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
         val timeline = room.createTimeline(null, TimelineSettings(10))
         val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
         val latch = CountDownLatch(1)
@@ -151,7 +151,7 @@ class CommonTestHelper(context: Context) {
             room.sendTextMessage(message + " #" + (i + 1))
         }
         // Wait 3 second more per message
-        await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
+        await(latch, timeout = timeout + 3_000L * nbOfMessages)
         timeline.dispose()
 
         // Check that all events has been created
@@ -215,14 +215,14 @@ class CommonTestHelper(context: Context) {
                     .getLoginFlow(hs, it)
         }
 
-        doSync<RegistrationResult> {
+        doSync<RegistrationResult>(timeout = 60_000) {
             matrix.authenticationService
                     .getRegistrationWizard()
                     .createAccount(userName, password, null, it)
         }
 
         // Perform dummy step
-        val registrationResult = doSync<RegistrationResult> {
+        val registrationResult = doSync<RegistrationResult>(timeout = 60_000) {
             matrix.authenticationService
                     .getRegistrationWizard()
                     .dummy(it)
@@ -231,7 +231,7 @@ class CommonTestHelper(context: Context) {
         assertTrue(registrationResult is RegistrationResult.Success)
         val session = (registrationResult as RegistrationResult.Success).session
         if (sessionTestParams.withInitialSync) {
-            syncSession(session)
+            syncSession(session, 60_000)
         }
 
         return session
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
index 76e59d9a90..b6bedbd719 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
@@ -18,14 +18,21 @@ package org.matrix.android.sdk.common
 
 import org.matrix.android.sdk.api.session.Session
 
-data class CryptoTestData(val firstSession: Session,
-                          val roomId: String,
-                          val secondSession: Session? = null,
-                          val thirdSession: Session? = null) {
+data class CryptoTestData(val roomId: String,
+                          val sessions: List<Session>) {
+
+    val firstSession: Session
+        get() = sessions.first()
+
+    val secondSession: Session?
+        get() = sessions.getOrNull(1)
+
+    val thirdSession: Session?
+        get() = sessions.getOrNull(2)
 
     fun cleanUp(testHelper: CommonTestHelper) {
-        testHelper.signOutAndClose(firstSession)
-        secondSession?.let { testHelper.signOutAndClose(it) }
-        thirdSession?.let { testHelper.signOutAndClose(it) }
+        sessions.forEach {
+            testHelper.signOutAndClose(it)
+        }
     }
 }
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 cbb22daf0f..3d5856fc64 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
@@ -73,7 +73,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
 
-        return CryptoTestData(aliceSession, roomId)
+        return CryptoTestData(roomId, listOf(aliceSession))
     }
 
     /**
@@ -139,7 +139,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
 //        assertNotNull(roomFromBobPOV.powerLevels)
 //        assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
 
-        return CryptoTestData(aliceSession, aliceRoomId, bobSession)
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
     }
 
     /**
@@ -157,7 +157,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
         // wait the initial sync
         SystemClock.sleep(1000)
 
-        return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
     }
 
     /**
@@ -381,4 +381,30 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
     }
+
+    fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
+        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
+        aliceSession.cryptoService().setWarnOnUnknownDevices(false)
+
+        val roomId = mTestHelper.doSync<String> {
+            aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
+        }
+        val room = aliceSession.getRoom(roomId)!!
+
+        mTestHelper.runBlockingTest {
+            room.enableEncryption()
+        }
+
+        val sessions = mutableListOf(aliceSession)
+        for (index in 1 until numberOfMembers) {
+            val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
+            mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) }
+            println("TEST -> " + session.myUserId + " invited")
+            mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) }
+            println("TEST -> " + session.myUserId + " joined")
+            sessions.add(session)
+        }
+
+        return CryptoTestData(roomId, sessions)
+    }
 }
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
new file mode 100644
index 0000000000..ff07cf1d1d
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.session.room.timeline
+
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.runners.MethodSorters
+import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.message.MessageContent
+import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
+import org.matrix.android.sdk.common.CommonTestHelper
+import org.matrix.android.sdk.common.CryptoTestHelper
+import java.util.concurrent.CountDownLatch
+import kotlin.test.fail
+
+@RunWith(JUnit4::class)
+@FixMethodOrder(MethodSorters.JVM)
+class TimelineWithManyMembersTest : InstrumentedTest {
+
+    companion object {
+        private const val NUMBER_OF_MEMBERS = 6
+    }
+
+    private val commonTestHelper = CommonTestHelper(context())
+    private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
+
+    /**
+     * Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
+     */
+    @Test
+    fun everyone_should_decrypt_message_in_a_crowded_room() {
+        val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
+
+        val sessionForFirstMember = cryptoTestData.firstSession
+        val roomForFirstMember = sessionForFirstMember.getRoom(cryptoTestData.roomId)!!
+
+        val firstMessage = "First messages from Alice"
+        commonTestHelper.sendTextMessage(
+                roomForFirstMember,
+                firstMessage,
+                1,
+                600_000
+        )
+
+        for (index in 1 until cryptoTestData.sessions.size) {
+            val session = cryptoTestData.sessions[index]
+            val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
+            val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30))
+            timelineForCurrentMember.start()
+
+            session.startSync(true)
+
+            run {
+                val lock = CountDownLatch(1)
+                val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
+                    snapshot
+                            .find { it.isEncrypted() }
+                            ?.let {
+                                val body = it.root.getClearContent()?.toModel<MessageContent>()?.body
+                                if (body?.startsWith(firstMessage).orFalse()) {
+                                    println("User " + session.myUserId + " decrypted as " + body)
+                                    return@createEventListener true
+                                } else {
+                                    fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
+                                }
+                            } ?: return@createEventListener false
+                }
+                timelineForCurrentMember.addListener(eventsListener)
+                commonTestHelper.await(lock, 600_000)
+            }
+            session.stopSync()
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 360b955869..bf21941e0c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -41,6 +41,16 @@ interface AuthenticationService {
      */
     fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
 
+    /**
+     * Get a SSO url
+     */
+    fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
+
+    /**
+     * Get the sign in or sign up fallback URL
+     */
+    fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String?
+
     /**
      * Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
      */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
index aefc086b43..ac1d726d03 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
@@ -25,7 +25,6 @@ interface PermalinkService {
 
     companion object {
         const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
-        const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
     }
 
     /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
index 00fa68c0ac..280316d4b5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
@@ -27,6 +27,7 @@ data class MessageStickerContent(
         /**
          * Set in local, not from server
          */
+        @Transient
         override val msgType: String = MessageType.MSGTYPE_STICKER_LOCAL,
 
         /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
new file mode 100644
index 0000000000..17f27b2514
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.util
+
+import java.net.URLEncoder
+
+/**
+ * Append param and value to a Url, using "?" or "&". Value parameter will be encoded
+ * Return this for chaining purpose
+ */
+fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
+    if (contains("?")) {
+        append("&")
+    } else {
+        append("?")
+    }
+
+    append(param)
+    append("=")
+    append(URLEncoder.encode(value, "utf-8"))
+
+    return this
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
similarity index 69%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
index 7d18aba627..642279cc27 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
@@ -14,25 +14,25 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.api.auth
+package org.matrix.android.sdk.internal.auth
 
 /**
  * Path to use when the client does not supported any or all login flows
  * Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback
  */
-const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
+internal const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
 
 /**
  * Path to use when the client does not supported any or all registration flows
  * Not documented
  */
-const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
+internal const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
 
 /**
  * Path to use when the client want to connect using SSO
  * Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login
  */
-const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
-const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
+internal const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
+internal const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
 
-const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
+internal const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 55f053de8d..c99e9bd81c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.util.Cancelable
 import org.matrix.android.sdk.api.util.NoOpCancellable
+import org.matrix.android.sdk.api.util.appendParamToUrl
 import org.matrix.android.sdk.internal.SessionManager
 import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
 import org.matrix.android.sdk.internal.auth.data.RiotConfig
@@ -99,6 +100,52 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
+    override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
+        val homeServerUrlBase = getHomeServerUrlBase() ?: return null
+
+        return buildString {
+            append(homeServerUrlBase)
+            if (providerId != null) {
+                append(MSC2858_SSO_REDIRECT_PATH)
+                append("/$providerId")
+            } else {
+                append(SSO_REDIRECT_PATH)
+            }
+            // Set the redirect url
+            appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
+            deviceId?.takeIf { it.isNotBlank() }?.let {
+                // But https://github.com/matrix-org/synapse/issues/5755
+                appendParamToUrl("device_id", it)
+            }
+        }
+    }
+
+    override fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
+        val homeServerUrlBase = getHomeServerUrlBase() ?: return null
+
+        return buildString {
+            append(homeServerUrlBase)
+            if (forSignIn) {
+                append(LOGIN_FALLBACK_PATH)
+                deviceId?.takeIf { it.isNotBlank() }?.let {
+                    // But https://github.com/matrix-org/synapse/issues/5755
+                    appendParamToUrl("device_id", it)
+                }
+            } else {
+                // For sign up
+                append(REGISTER_FALLBACK_PATH)
+            }
+        }
+    }
+
+    private fun getHomeServerUrlBase(): String? {
+        return pendingSessionData
+                ?.homeServerConnectionConfig
+                ?.homeServerUri
+                ?.toString()
+                ?.trim { it == '/' }
+    }
+
     override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
         pendingSessionData = null
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
index 8b739c4b64..5c8c7dfb25 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
@@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
 import org.matrix.android.sdk.internal.session.room.send.SendResponse
 import org.matrix.android.sdk.internal.task.Task
@@ -35,11 +36,19 @@ internal interface SendEventTask : Task<SendEventTask.Params, String> {
 internal class DefaultSendEventTask @Inject constructor(
         private val localEchoRepository: LocalEchoRepository,
         private val encryptEventTask: DefaultEncryptEventTask,
+        private val loadRoomMembersTask: LoadRoomMembersTask,
         private val roomAPI: RoomAPI,
         private val eventBus: EventBus) : SendEventTask {
 
     override suspend fun execute(params: SendEventTask.Params): String {
         try {
+            // Make sure to load all members in the room before sending the event.
+            params.event.roomId
+                    ?.takeIf { params.encrypt }
+                    ?.let { roomId ->
+                        loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
+                    }
+
             val event = handleEncryption(params)
             val localId = event.eventId!!
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index b970ec60e2..57002b5a60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -21,6 +21,8 @@ import io.realm.RealmMigration
 import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
 import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
 import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
 import timber.log.Timber
 import javax.inject.Inject
@@ -28,7 +30,7 @@ import javax.inject.Inject
 class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
 
     companion object {
-        const val SESSION_STORE_SCHEMA_VERSION = 6L
+        const val SESSION_STORE_SCHEMA_VERSION = 7L
     }
 
     override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@@ -40,6 +42,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
         if (oldVersion <= 3) migrateTo4(realm)
         if (oldVersion <= 4) migrateTo5(realm)
         if (oldVersion <= 5) migrateTo6(realm)
+        if (oldVersion <= 6) migrateTo7(realm)
     }
 
     private fun migrateTo1(realm: DynamicRealm) {
@@ -105,4 +108,18 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
                 .addField(PreviewUrlCacheEntityFields.MXC_URL, String::class.java)
                 .addField(PreviewUrlCacheEntityFields.LAST_UPDATED_TIMESTAMP, Long::class.java)
     }
+
+    private fun migrateTo7(realm: DynamicRealm) {
+        Timber.d("Step 6 -> 7")
+        realm.schema.get("RoomEntity")
+                ?.addField(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, String::class.java)
+                ?.transform { obj ->
+                    if (obj.getBoolean("areAllMembersLoaded")) {
+                        obj.setString("membersLoadStatusStr", RoomMembersLoadStatusType.LOADED.name)
+                    } else {
+                        obj.setString("membersLoadStatusStr", RoomMembersLoadStatusType.NONE.name)
+                    }
+                }
+                ?.removeField("areAllMembersLoaded")
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
index 9af1646a4c..3ff2532604 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
@@ -23,8 +23,7 @@ import io.realm.annotations.PrimaryKey
 
 internal open class RoomEntity(@PrimaryKey var roomId: String = "",
                                var chunks: RealmList<ChunkEntity> = RealmList(),
-                               var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList(),
-                               var areAllMembersLoaded: Boolean = false
+                               var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList()
 ) : RealmObject() {
 
     private var membershipStr: String = Membership.NONE.name
@@ -36,5 +35,14 @@ internal open class RoomEntity(@PrimaryKey var roomId: String = "",
             membershipStr = value.name
         }
 
+    private var membersLoadStatusStr: String = RoomMembersLoadStatusType.NONE.name
+    var membersLoadStatus: RoomMembersLoadStatusType
+        get() {
+            return RoomMembersLoadStatusType.valueOf(membersLoadStatusStr)
+        }
+        set(value) {
+            membersLoadStatusStr = value.name
+        }
+
     companion object
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
new file mode 100644
index 0000000000..79fe17253b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.database.model
+
+internal enum class RoomMembersLoadStatusType {
+    NONE,
+    LOADING,
+    LOADED
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
index 627f927ad8..53fa73c624 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
@@ -17,12 +17,19 @@
 package org.matrix.android.sdk.internal.session.room.membership
 
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import io.realm.kotlin.createObject
+import kotlinx.coroutines.TimeoutCancellationException
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
 import org.matrix.android.sdk.internal.database.model.EventInsertType
 import org.matrix.android.sdk.internal.database.model.RoomEntity
+import org.matrix.android.sdk.internal.database.model.RoomEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.database.query.where
@@ -33,9 +40,7 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
 import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import io.realm.Realm
-import io.realm.kotlin.createObject
-import org.greenrobot.eventbus.EventBus
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
 internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit> {
@@ -56,13 +61,40 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
 ) : LoadRoomMembersTask {
 
     override suspend fun execute(params: LoadRoomMembersTask.Params) {
-        if (areAllMembersAlreadyLoaded(params.roomId)) {
-            return
+        when (getRoomMembersLoadStatus(params.roomId)) {
+            RoomMembersLoadStatusType.NONE    -> doRequest(params)
+            RoomMembersLoadStatusType.LOADING -> waitPreviousRequestToFinish(params)
+            RoomMembersLoadStatusType.LOADED  -> Unit
         }
+    }
+
+    private suspend fun waitPreviousRequestToFinish(params: LoadRoomMembersTask.Params) {
+        try {
+            awaitNotEmptyResult(monarchy.realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
+                realm.where(RoomEntity::class.java)
+                        .equalTo(RoomEntityFields.ROOM_ID, params.roomId)
+                        .equalTo(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, RoomMembersLoadStatusType.LOADED.name)
+            }
+        } catch (exception: TimeoutCancellationException) {
+            // Timeout, do the request anyway (?)
+            doRequest(params)
+        }
+    }
+
+    private suspend fun doRequest(params: LoadRoomMembersTask.Params) {
+        setRoomMembersLoadStatus(params.roomId, RoomMembersLoadStatusType.LOADING)
+
         val lastToken = syncTokenStore.getLastToken()
-        val response = executeRequest<RoomMembersResponse>(eventBus) {
-            apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
+        val response = try {
+            executeRequest<RoomMembersResponse>(eventBus) {
+                apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
+            }
+        } catch (throwable: Throwable) {
+            // Revert status to NONE
+            setRoomMembersLoadStatus(params.roomId, RoomMembersLoadStatusType.NONE)
+            throw throwable
         }
+        // This will also set the status to LOADED
         insertInDb(response, params.roomId)
     }
 
@@ -84,14 +116,23 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
                 }
                 roomMemberEventHandler.handle(realm, roomId, roomMemberEvent)
             }
-            roomEntity.areAllMembersLoaded = true
+            roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADED
             roomSummaryUpdater.update(realm, roomId, updateMembers = true)
         }
     }
 
-    private fun areAllMembersAlreadyLoaded(roomId: String): Boolean {
-        return Realm.getInstance(monarchy.realmConfiguration).use {
-            RoomEntity.where(it, roomId).findFirst()?.areAllMembersLoaded ?: false
+    private fun getRoomMembersLoadStatus(roomId: String): RoomMembersLoadStatusType {
+        var result: RoomMembersLoadStatusType?
+        Realm.getInstance(monarchy.realmConfiguration).use {
+            result = RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus
+        }
+        return result ?: RoomMembersLoadStatusType.NONE
+    }
+
+    private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) {
+        monarchy.awaitTransaction { realm ->
+            val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
+            roomEntity.membersLoadStatus = status
         }
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 86b0497bd0..dd58529412 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -27,6 +27,7 @@ import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.NoOpMatrixCallback
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.events.model.EventType
@@ -53,6 +54,7 @@ import org.matrix.android.sdk.internal.database.query.filterEvents
 import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.database.query.whereRoomId
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.task.configureWith
 import org.matrix.android.sdk.internal.util.Debouncer
@@ -81,7 +83,8 @@ internal class DefaultTimeline(
         private val hiddenReadReceipts: TimelineHiddenReadReceipts,
         private val eventBus: EventBus,
         private val eventDecryptor: TimelineEventDecryptor,
-        private val realmSessionProvider: RealmSessionProvider
+        private val realmSessionProvider: RealmSessionProvider,
+        private val loadRoomMembersTask: LoadRoomMembersTask
 ) : Timeline, TimelineHiddenReadReceipts.Delegate {
 
     data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
@@ -184,6 +187,13 @@ internal class DefaultTimeline(
                 if (settings.shouldHandleHiddenReadReceipts()) {
                     hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this)
                 }
+
+                loadRoomMembersTask
+                        .configureWith(LoadRoomMembersTask.Params(roomId)) {
+                            this.callback = NoOpMatrixCallback()
+                        }
+                        .executeBy(taskExecutor)
+
                 isReady.set(true)
             }
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index 783aa53ddf..d02e906d00 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.task.TaskExecutor
 
 internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
@@ -51,7 +52,8 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                                                                   private val paginationTask: PaginationTask,
                                                                   private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
                                                                   private val timelineEventMapper: TimelineEventMapper,
-                                                                  private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper
+                                                                  private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
+                                                                  private val loadRoomMembersTask: LoadRoomMembersTask
 ) : TimelineService {
 
     @AssistedInject.Factory
@@ -73,7 +75,8 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                 eventBus = eventBus,
                 eventDecryptor = eventDecryptor,
                 fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
-                realmSessionProvider = realmSessionProvider
+                realmSessionProvider = realmSessionProvider,
+                loadRoomMembersTask = loadRoomMembersTask
         )
     }
 
diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index b9929dfebe..2306eaed8b 100644
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1
 # android\.text\.TextUtils
 
 ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
-enum class===84
+enum class===85
 
 ### Do not import temporary legacy classes
 import org.matrix.android.sdk.internal.legacy.riot===3
diff --git a/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
index 38977d33ba..5037f78445 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
@@ -16,26 +16,6 @@
 
 package im.vector.app.core.extensions
 
-import java.net.URLEncoder
-
-/**
- * Append param and value to a Url, using "?" or "&". Value parameter will be encoded
- * Return this for chaining purpose
- */
-fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
-    if (contains("?")) {
-        append("&")
-    } else {
-        append("?")
-    }
-
-    append(param)
-    append("=")
-    append(URLEncoder.encode(value, "utf-8"))
-
-    return this
-}
-
 /**
  * Ex: "https://matrix.org/" -> "matrix.org"
  */
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
index 6f261ad717..f86825750a 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
@@ -28,7 +28,7 @@ import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemVerificationActionBinding
+import im.vector.app.databinding.ViewBottomSheetActionButtonBinding
 import im.vector.app.features.themes.ThemeUtils
 
 class BottomSheetActionButton @JvmOverloads constructor(
@@ -36,7 +36,7 @@ class BottomSheetActionButton @JvmOverloads constructor(
         attrs: AttributeSet? = null,
         defStyleAttr: Int = 0
 ) : FrameLayout(context, attrs, defStyleAttr) {
-    val views : ItemVerificationActionBinding
+    val views: ViewBottomSheetActionButtonBinding
 
     var title: String? = null
         set(value) {
@@ -97,8 +97,8 @@ class BottomSheetActionButton @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_verification_action, this)
-        views = ItemVerificationActionBinding.bind(this)
+        inflate(context, R.layout.view_bottom_sheet_action_button, this)
+        views = ViewBottomSheetActionButtonBinding.bind(this)
 
         context.withStyledAttributes(attrs, R.styleable.BottomSheetActionButton) {
             title = getString(R.styleable.BottomSheetActionButton_actionTitle) ?: ""
diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
index 5fdc70c539..9aa6ccd298 100644
--- a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
+++ b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
@@ -18,7 +18,7 @@ package im.vector.app.features.call
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.LinearLayout
+import android.widget.FrameLayout
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.databinding.ViewCallControlsBinding
@@ -28,7 +28,7 @@ import org.webrtc.PeerConnection
 
 class CallControlsView @JvmOverloads constructor(
         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
+) : FrameLayout(context, attrs, defStyleAttr) {
 
     private val views: ViewCallControlsBinding
 
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 9d7beb13a3..108e0512a7 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -39,7 +39,6 @@ import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.ToolbarConfigurable
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.pushers.PushersManager
-import im.vector.app.core.utils.toast
 import im.vector.app.databinding.ActivityHomeBinding
 import im.vector.app.features.disclaimer.showDisclaimerDialog
 import im.vector.app.features.matrixto.MatrixToBottomSheet
@@ -166,8 +165,8 @@ class HomeActivity :
     private fun handleIntent(intent: Intent?) {
         intent?.dataString?.let { deepLink ->
             val resolvedLink = when {
-                deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE)               -> deepLink
-                deepLink.startsWith(PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> {
+                deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> deepLink
+                deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE)    -> {
                     // This is a bit ugly, but for now just convert to matrix.to link for compatibility
                     when {
                         deepLink.startsWith(USER_LINK_PREFIX) -> deepLink.substring(USER_LINK_PREFIX.length)
@@ -177,7 +176,7 @@ class HomeActivity :
                         activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(it)
                     }
                 }
-                else                                                                   -> null
+                else                                                     -> return@let
             }
 
             permalinkHandler.launch(
@@ -190,7 +189,11 @@ class HomeActivity :
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe { isHandled ->
                         if (!isHandled) {
-                            toast(R.string.permalink_malformed)
+                            AlertDialog.Builder(this)
+                                    .setTitle(R.string.dialog_title_error)
+                                    .setMessage(R.string.permalink_malformed)
+                                    .setPositiveButton(R.string.ok, null)
+                                    .show()
                         }
                     }
                     .disposeOnDestroy()
@@ -410,7 +413,8 @@ class HomeActivity :
                     }
         }
 
-        private const val ROOM_LINK_PREFIX = "${PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
-        private const val USER_LINK_PREFIX = "${PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
+        private const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
+        private const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
+        private const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
     }
 }
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 e4e7177e4f..1e6e7c9d14 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
@@ -31,7 +31,6 @@ import im.vector.app.R
 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.core.utils.subscribeLogError
 import im.vector.app.features.call.WebRtcPeerConnectionManager
 import im.vector.app.features.command.CommandParser
 import im.vector.app.features.command.ParsedCommand
@@ -168,7 +167,6 @@ class RoomDetailViewModel @AssistedInject constructor(
         observePowerLevel()
         room.getRoomSummaryLive()
         room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT, NoOpMatrixCallback())
-        room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
         // Inform the SDK that the room is displayed
         session.onRoomDisplayed(initialState.roomId)
         chatEffectManager.delegate = this
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
index 1983b05ed3..01c7ad3986 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
@@ -57,7 +57,7 @@ class TimelineSettingsFactory @Inject constructor(
         return map {
             EventTypeFilter(
                     eventType = it,
-                    stateKey = if (it == EventType.STATE_ROOM_MEMBER && userPreferencesProvider.shouldShowRoomMemberStateEvents()) session.myUserId else null
+                    stateKey = if (it == EventType.STATE_ROOM_MEMBER && !userPreferencesProvider.shouldShowRoomMemberStateEvents()) session.myUserId else null
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
index d5996a65ba..aa864851cd 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
@@ -23,7 +23,7 @@ import android.widget.LinearLayout
 import androidx.core.content.withStyledAttributes
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemTimelineEventPollResultItemBinding
+import im.vector.app.databinding.ViewPollResultLineBinding
 
 class PollResultLineView @JvmOverloads constructor(
         context: Context,
@@ -31,7 +31,7 @@ class PollResultLineView @JvmOverloads constructor(
         defStyleAttr: Int = 0
 ) : LinearLayout(context, attrs, defStyleAttr) {
 
-    private val views: ItemTimelineEventPollResultItemBinding
+    private val views: ViewPollResultLineBinding
 
     var label: String? = null
         set(value) {
@@ -60,8 +60,8 @@ class PollResultLineView @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_timeline_event_poll_result_item, this)
-        views = ItemTimelineEventPollResultItemBinding.bind(this)
+        inflate(context, R.layout.view_poll_result_line, this)
+        views = ViewPollResultLineBinding.bind(this)
         orientation = HORIZONTAL
 
         context.withStyledAttributes(attrs, R.styleable.PollResultLineView) {
diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
index c20f4ddd23..3fc5037ae7 100644
--- a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
@@ -87,7 +87,12 @@ abstract class AbstractSSOLoginFragment<VB: ViewBinding> : AbstractLoginFragment
         withState(loginViewModel) { state ->
             if (state.loginMode.hasSso() && state.loginMode.ssoIdentityProviders().isNullOrEmpty()) {
                 // in this case we can prefetch (not other cases for privacy concerns)
-                prefetchUrl(state.getSsoUrl(null))
+                loginViewModel.getSsoUrl(
+                        redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                        deviceId = state.deviceId,
+                        providerId = null
+                )
+                        ?.let { prefetchUrl(it) }
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 503b6d74a6..803fd38983 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -360,6 +360,9 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarCo
 
         private const val EXTRA_CONFIG = "EXTRA_CONFIG"
 
+        // Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
+        const val VECTOR_REDIRECT_URL = "element://connect"
+
         fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
             return Intent(context, LoginActivity::class.java).apply {
                 putExtra(EXTRA_CONFIG, loginConfig)
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
index c396e61b1a..3b22e0f206 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
@@ -193,7 +193,12 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
                 views.loginSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders
                 views.loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
                     override fun onProviderSelected(id: String?) {
-                        openInCustomTab(state.getSsoUrl(id))
+                        loginViewModel.getSsoUrl(
+                                redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                                deviceId = state.deviceId,
+                                providerId = id
+                        )
+                                ?.let { openInCustomTab(it) }
                     }
                 }
             } else {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
index cdf6c64c9e..b25cea6fce 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
@@ -76,8 +76,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
                 views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders()
                 views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
                     override fun onProviderSelected(id: String?) {
-                        val url = withState(loginViewModel) { it.getSsoUrl(id) }
-                        openInCustomTab(url)
+                        loginViewModel.getSsoUrl(
+                                redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                                deviceId = state.deviceId,
+                                providerId = id
+                        )
+                                ?.let { openInCustomTab(it) }
                     }
                 }
             }
@@ -105,7 +109,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
 
     private fun submit() = withState(loginViewModel) { state ->
         if (state.loginMode is LoginMode.Sso) {
-            openInCustomTab(state.getSsoUrl(null))
+            loginViewModel.getSsoUrl(
+                    redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                    deviceId = state.deviceId,
+                    providerId = null
+            )
+                    ?.let { openInCustomTab(it) }
         } else {
             loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
         }
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 0a6dbcaae2..666bd21add 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
@@ -818,4 +818,12 @@ class LoginViewModel @AssistedInject constructor(
     fun getInitialHomeServerUrl(): String? {
         return loginConfig?.homeServerUrl
     }
+
+    fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
+        return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
+    }
+
+    fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
+        return authenticationService.getFallbackUrl(forSignIn, deviceId)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
index 5254abf1d9..37ac89794f 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
@@ -22,10 +22,6 @@ import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.PersistState
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.Uninitialized
-import im.vector.app.core.extensions.appendParamToUrl
-import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
 
 data class LoginViewState(
         val asyncLoginAction: Async<Unit> = Uninitialized,
@@ -69,27 +65,4 @@ data class LoginViewState(
     fun isUserLogged(): Boolean {
         return asyncLoginAction is Success
     }
-
-    fun getSsoUrl(providerId: String?): String {
-        return buildString {
-            append(homeServerUrl?.trim { it == '/' })
-            if (providerId != null) {
-                append(MSC2858_SSO_REDIRECT_PATH)
-                append("/$providerId")
-            } else {
-                append(SSO_REDIRECT_PATH)
-            }
-            // Set a redirect url we will intercept later
-            appendParamToUrl(SSO_REDIRECT_URL_PARAM, VECTOR_REDIRECT_URL)
-            deviceId?.takeIf { it.isNotBlank() }?.let {
-                // But https://github.com/matrix-org/synapse/issues/5755
-                appendParamToUrl("device_id", it)
-            }
-        }
-    }
-
-    companion object {
-        // Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
-        private const val VECTOR_REDIRECT_URL = "element://connect"
-    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
index acf4f706c5..4b03c93321 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
@@ -33,14 +33,11 @@ import android.webkit.WebViewClient
 import androidx.appcompat.app.AlertDialog
 import com.airbnb.mvrx.activityViewModel
 import im.vector.app.R
-import im.vector.app.core.extensions.appendParamToUrl
 import im.vector.app.core.utils.AssetReader
 import im.vector.app.databinding.FragmentLoginWebBinding
 import im.vector.app.features.signout.soft.SoftLogoutAction
 import im.vector.app.features.signout.soft.SoftLogoutViewModel
 
-import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
-import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import timber.log.Timber
@@ -119,19 +116,7 @@ class LoginWebFragment @Inject constructor(
     }
 
     private fun launchWebView(state: LoginViewState) {
-        val url = buildString {
-            append(state.homeServerUrl?.trim { it == '/' })
-            if (state.signMode == SignMode.SignIn) {
-                append(LOGIN_FALLBACK_PATH)
-                state.deviceId?.takeIf { it.isNotBlank() }?.let {
-                    // But https://github.com/matrix-org/synapse/issues/5755
-                    appendParamToUrl("device_id", it)
-                }
-            } else {
-                // MODE_REGISTER
-                append(REGISTER_FALLBACK_PATH)
-            }
-        }
+        val url = loginViewModel.getFallbackUrl(state.signMode == SignMode.SignIn, state.deviceId) ?: return
 
         views.loginWebWebView.loadUrl(url)
 
diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
index bba6b9c253..0856ac4b35 100644
--- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
@@ -18,6 +18,8 @@ package im.vector.app.features.themes
 
 import android.app.Activity
 import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
 import android.graphics.drawable.Drawable
 import android.util.TypedValue
 import android.view.Menu
@@ -39,10 +41,14 @@ object ThemeUtils {
     const val APPLICATION_THEME_KEY = "APPLICATION_THEME_KEY"
 
     // the theme possible values
+    private const val SYSTEM_THEME_VALUE = "system"
     private const val THEME_DARK_VALUE = "dark"
     private const val THEME_LIGHT_VALUE = "light"
     private const val THEME_BLACK_VALUE = "black"
 
+    // The default theme
+    private const val DEFAULT_THEME = SYSTEM_THEME_VALUE
+
     private var currentTheme = AtomicReference<String>(null)
 
     private val mColorByAttr = HashMap<Int, Int>()
@@ -54,13 +60,12 @@ object ThemeUtils {
     }
 
     /**
-     * @return true if current theme is Light or Status
+     * @return true if current theme is Light or current theme is System and system theme is light
      */
     fun isLightTheme(context: Context): Boolean {
-        return when (getApplicationTheme(context)) {
-            THEME_LIGHT_VALUE -> true
-            else              -> false
-        }
+        val theme = getApplicationTheme(context)
+        return theme == THEME_LIGHT_VALUE
+                || (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources))
     }
 
     /**
@@ -73,11 +78,11 @@ object ThemeUtils {
         val currentTheme = this.currentTheme.get()
         return if (currentTheme == null) {
             val prefs = DefaultSharedPreferences.getInstance(context)
-            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
+            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, DEFAULT_THEME) ?: DEFAULT_THEME
             if (themeFromPref == "status") {
-                // Migrate to light theme, which is the closest theme
-                themeFromPref = THEME_LIGHT_VALUE
-                prefs.edit { putString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) }
+                // Migrate to the default theme
+                themeFromPref = DEFAULT_THEME
+                prefs.edit { putString(APPLICATION_THEME_KEY, DEFAULT_THEME) }
             }
             this.currentTheme.set(themeFromPref)
             themeFromPref
@@ -86,6 +91,13 @@ object ThemeUtils {
         }
     }
 
+    /**
+     * @return true if system theme is dark
+     */
+    private fun isSystemDarkTheme(resources: Resources): Boolean {
+        return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+    }
+
     /**
      * Update the application theme
      *
@@ -93,11 +105,14 @@ object ThemeUtils {
      */
     fun setApplicationTheme(context: Context, aTheme: String) {
         currentTheme.set(aTheme)
-        when (aTheme) {
-            THEME_DARK_VALUE   -> context.setTheme(R.style.AppTheme_Dark)
-            THEME_BLACK_VALUE  -> context.setTheme(R.style.AppTheme_Black)
-            else               -> context.setTheme(R.style.AppTheme_Light)
-        }
+        context.setTheme(
+                when (aTheme) {
+                    SYSTEM_THEME_VALUE -> if (isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light
+                    THEME_DARK_VALUE   -> R.style.AppTheme_Dark
+                    THEME_BLACK_VALUE  -> R.style.AppTheme_Black
+                    else               -> R.style.AppTheme_Light
+                }
+        )
 
         // Clear the cache
         mColorByAttr.clear()
@@ -110,6 +125,7 @@ object ThemeUtils {
      */
     fun setActivityTheme(activity: Activity, otherThemes: ActivityOtherThemes) {
         when (getApplicationTheme(activity)) {
+            SYSTEM_THEME_VALUE -> if (isSystemDarkTheme(activity.resources)) activity.setTheme(otherThemes.dark)
             THEME_DARK_VALUE   -> activity.setTheme(otherThemes.dark)
             THEME_BLACK_VALUE  -> activity.setTheme(otherThemes.black)
         }
@@ -117,40 +133,6 @@ object ThemeUtils {
         mColorByAttr.clear()
     }
 
-    /**
-     * Set the TabLayout colors.
-     * It seems that there is no proper way to manage it with the manifest file.
-     *
-     * @param activity the activity
-     * @param layout   the layout
-     */
-    /*
-    fun setTabLayoutTheme(activity: Activity, layout: TabLayout) {
-        if (activity is VectorGroupDetailsActivity) {
-            val textColor: Int
-            val underlineColor: Int
-            val backgroundColor: Int
-
-            if (TextUtils.equals(getApplicationTheme(activity), THEME_LIGHT_VALUE)) {
-                textColor = ContextCompat.getColor(activity, android.R.color.white)
-                underlineColor = textColor
-                backgroundColor = ContextCompat.getColor(activity, R.color.tab_groups)
-            } else if (TextUtils.equals(getApplicationTheme(activity), THEME_STATUS_VALUE)) {
-                textColor = ContextCompat.getColor(activity, android.R.color.white)
-                underlineColor = textColor
-                backgroundColor = getColor(activity, R.attr.colorPrimary)
-            } else {
-                textColor = ContextCompat.getColor(activity, R.color.tab_groups)
-                underlineColor = textColor
-                backgroundColor = getColor(activity, R.attr.colorPrimary)
-            }
-
-            layout.setTabTextColors(textColor, textColor)
-            layout.setSelectedTabIndicatorColor(underlineColor)
-            layout.setBackgroundColor(backgroundColor)
-        }
-    }    */
-
     /**
      * Translates color attributes to colors
      *
diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
index 00df261095..61bef29d54 100644
--- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
+++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
@@ -20,19 +20,19 @@ import android.content.Context
 import android.content.res.ColorStateList
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
-import android.widget.LinearLayout
+import android.widget.FrameLayout
 import androidx.core.content.withStyledAttributes
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemSignoutActionBinding
+import im.vector.app.databinding.ViewSignOutBottomSheetActionButtonBinding
 import im.vector.app.features.themes.ThemeUtils
 
 class SignOutBottomSheetActionButton @JvmOverloads constructor(
         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
+) : FrameLayout(context, attrs, defStyleAttr) {
 
-    private val views: ItemSignoutActionBinding
+    private val views: ViewSignOutBottomSheetActionButtonBinding
 
     var action: (() -> Unit)? = null
 
@@ -67,8 +67,8 @@ class SignOutBottomSheetActionButton @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_signout_action, this)
-        views = ItemSignoutActionBinding.bind(this)
+        inflate(context, R.layout.view_sign_out_bottom_sheet_action_button, this)
+        views = ViewSignOutBottomSheetActionButtonBinding.bind(this)
 
         context.withStyledAttributes(attrs, R.styleable.SignOutBottomSheetActionButton) {
             title = getString(R.styleable.SignOutBottomSheetActionButton_actionTitle) ?: ""
diff --git a/vector/src/main/res/layout/item_signout_action.xml b/vector/src/main/res/layout/item_signout_action.xml
deleted file mode 100644
index b1eb8c1f62..0000000000
--- a/vector/src/main/res/layout/item_signout_action.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout 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:id="@+id/signedOutActionClickable"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:foreground="?attr/selectableItemBackground"
-    android:minHeight="50dp"
-    android:orientation="horizontal"
-    android:paddingStart="@dimen/layout_horizontal_margin"
-    android:paddingTop="8dp"
-    android:paddingEnd="@dimen/layout_horizontal_margin"
-    android:paddingBottom="8dp">
-
-    <ImageView
-        android:id="@+id/actionIconImageView"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_gravity="center_vertical"
-        android:layout_marginEnd="16dp"
-        android:scaleType="fitCenter"
-        android:src="@drawable/ic_secure_backup"
-        app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
-
-    <TextView
-        android:id="@+id/actionTitleText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:text="@string/secure_backup_setup"
-        android:textColor="?riotx_text_secondary"
-        android:textSize="17sp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_verification_action.xml b/vector/src/main/res/layout/item_verification_action.xml
index ae49893792..68ee392cff 100644
--- a/vector/src/main/res/layout/item_verification_action.xml
+++ b/vector/src/main/res/layout/item_verification_action.xml
@@ -24,10 +24,10 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@drawable/ic_share"
-        tools:visibility="visible"
         app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
+        tools:ignore="MissingPrefix"
+        tools:src="@drawable/ic_share"
+        tools:visibility="visible" />
 
 
     <TextView
@@ -70,8 +70,8 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@drawable/ic_arrow_right"
         app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
+        tools:ignore="MissingPrefix"
+        tools:src="@drawable/ic_arrow_right" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/view_bottom_sheet_action_button.xml b/vector/src/main/res/layout/view_bottom_sheet_action_button.xml
new file mode 100644
index 0000000000..c0f55df9e6
--- /dev/null
+++ b/vector/src/main/res/layout/view_bottom_sheet_action_button.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge 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"
+    tools:parentTag="android.widget.FrameLayout">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/itemVerificationClickableZone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?riotx_bottom_sheet_background"
+        android:clickable="true"
+        android:focusable="true"
+        android:foreground="?attr/selectableItemBackground"
+        android:minHeight="64dp"
+        android:paddingStart="@dimen/layout_horizontal_margin"
+        android:paddingTop="8dp"
+        android:paddingEnd="@dimen/layout_horizontal_margin"
+        android:paddingBottom="8dp">
+
+        <ImageView
+            android:id="@+id/itemVerificationLeftIcon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="center"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix"
+            tools:src="@drawable/ic_share"
+            tools:visibility="visible" />
+
+
+        <TextView
+            android:id="@+id/itemVerificationActionTitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:textColor="@color/riotx_accent"
+            android:textSize="16sp"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintBottom_toTopOf="@+id/itemVerificationActionSubTitle"
+            app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
+            app:layout_constraintStart_toEndOf="@+id/itemVerificationLeftIcon"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_goneMarginStart="0dp"
+            tools:text="@string/start_verification" />
+
+        <TextView
+            android:id="@+id/itemVerificationActionSubTitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:textColor="?riotx_text_secondary"
+            android:textSize="12sp"
+            android:visibility="gone"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
+            app:layout_constraintStart_toStartOf="@+id/itemVerificationActionTitle"
+            app:layout_constraintTop_toBottomOf="@+id/itemVerificationActionTitle"
+            tools:text="For maximum security, do this in person"
+            tools:visibility="visible" />
+
+        <ImageView
+            android:id="@+id/itemVerificationActionIcon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix"
+            tools:src="@drawable/ic_arrow_right" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</merge>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/view_call_controls.xml b/vector/src/main/res/layout/view_call_controls.xml
index 435520b9ef..2487f131e3 100644
--- a/vector/src/main/res/layout/view_call_controls.xml
+++ b/vector/src/main/res/layout/view_call_controls.xml
@@ -1,9 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<merge 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:layout_height="wrap_content"
+    tools:parentTag="android.widget.FrameLayout">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/ringingControls"
@@ -23,8 +24,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/ringingControlDecline"
@@ -36,8 +37,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call_end"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <androidx.constraintlayout.helper.widget.Flow
             android:layout_width="match_parent"
@@ -69,8 +70,8 @@
             android:padding="10dp"
             android:src="@drawable/ic_home_bottom_chat"
             app:backgroundTint="?attr/riotx_background"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            app:tint="?attr/riotx_text_primary"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/muteIcon"
@@ -82,10 +83,10 @@
             android:padding="16dp"
             android:src="@drawable/ic_microphone_off"
             app:backgroundTint="?attr/riotx_background"
+            app:tint="?attr/riotx_text_primary"
             tools:contentDescription="@string/a11y_mute_microphone"
             tools:ignore="MissingConstraints,MissingPrefix"
-            tools:src="@drawable/ic_microphone_on"
-            app:tint="?attr/riotx_text_primary" />
+            tools:src="@drawable/ic_microphone_on" />
 
         <ImageView
             android:id="@+id/iv_end_call"
@@ -97,8 +98,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call_end"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/videoToggleIcon"
@@ -110,9 +111,9 @@
             android:padding="16dp"
             android:src="@drawable/ic_call_videocam_off_default"
             app:backgroundTint="?attr/riotx_background"
+            app:tint="?attr/riotx_text_primary"
             tools:contentDescription="@string/a11y_stop_camera"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/iv_more"
@@ -125,8 +126,8 @@
             android:padding="8dp"
             android:src="@drawable/ic_more_vertical"
             app:backgroundTint="?attr/riotx_background"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            app:tint="?attr/riotx_text_primary"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <androidx.constraintlayout.helper.widget.Flow
             android:layout_width="match_parent"
@@ -202,4 +203,4 @@
     <!--        app:layout_constraintEnd_toEndOf="parent"-->
     <!--        app:layout_constraintTop_toTopOf="parent" />-->
 
-</FrameLayout>
\ No newline at end of file
+</merge>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_timeline_event_poll_result_item.xml b/vector/src/main/res/layout/view_poll_result_line.xml
similarity index 100%
rename from vector/src/main/res/layout/item_timeline_event_poll_result_item.xml
rename to vector/src/main/res/layout/view_poll_result_line.xml
diff --git a/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml b/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml
new file mode 100644
index 0000000000..6809cfd119
--- /dev/null
+++ b/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge 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"
+    tools:parentTag="android.widget.FrameLayout">
+
+    <LinearLayout
+        android:id="@+id/signedOutActionClickable"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:foreground="?attr/selectableItemBackground"
+        android:minHeight="50dp"
+        android:orientation="horizontal"
+        android:paddingStart="@dimen/layout_horizontal_margin"
+        android:paddingTop="8dp"
+        android:paddingEnd="@dimen/layout_horizontal_margin"
+        android:paddingBottom="8dp">
+
+        <ImageView
+            android:id="@+id/actionIconImageView"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="16dp"
+            android:scaleType="fitCenter"
+            android:src="@drawable/ic_secure_backup"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix" />
+
+        <TextView
+            android:id="@+id/actionTitleText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:text="@string/secure_backup_setup"
+            android:textColor="?riotx_text_secondary"
+            android:textSize="17sp" />
+
+    </LinearLayout>
+
+</merge>
diff --git a/vector/src/main/res/values/array.xml b/vector/src/main/res/values/array.xml
index e56844e2e8..2c4bc135e5 100644
--- a/vector/src/main/res/values/array.xml
+++ b/vector/src/main/res/values/array.xml
@@ -92,12 +92,14 @@
 
     <!-- Theme -->
     <string-array name="theme_entries">
+        <item>@string/system_theme</item>
         <item>@string/light_theme</item>
         <item>@string/dark_theme</item>
         <item>@string/black_them</item>
     </string-array>
 
     <string-array name="theme_values">
+        <item>system</item>
         <item>light</item>
         <item>dark</item>
         <item>black</item>
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 76541460d2..355ac4d6d6 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -8,6 +8,7 @@
     <string name="resources_script">Latn</string>
 
     <!-- theme -->
+    <string name="system_theme">System Default</string>
     <string name="light_theme">Light Theme</string>
     <string name="dark_theme">Dark Theme</string>
     <string name="black_them">Black Theme</string>
diff --git a/vector/src/main/res/xml/vector_settings_preferences.xml b/vector/src/main/res/xml/vector_settings_preferences.xml
index 74b1f882ee..6297b89e6c 100644
--- a/vector/src/main/res/xml/vector_settings_preferences.xml
+++ b/vector/src/main/res/xml/vector_settings_preferences.xml
@@ -13,7 +13,7 @@
             app:fragment="im.vector.app.features.settings.locale.LocalePickerFragment" />
 
         <im.vector.app.core.preference.VectorListPreference
-            android:defaultValue="light"
+            android:defaultValue="system"
             android:entries="@array/theme_entries"
             android:entryValues="@array/theme_values"
             android:key="APPLICATION_THEME_KEY"