diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml
index 10c12796c0..680a1d57cf 100644
--- a/.idea/dictionaries/bmarty.xml
+++ b/.idea/dictionaries/bmarty.xml
@@ -9,6 +9,8 @@
       <w>decryptor</w>
       <w>emoji</w>
       <w>emojis</w>
+      <w>fdroid</w>
+      <w>gplay</w>
       <w>hmac</w>
       <w>ktlint</w>
       <w>linkified</w>
diff --git a/AUTHORS.md b/AUTHORS.md
index e69de29bb2..d1898a3cf6 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -0,0 +1,35 @@
+A full developer contributors list can be found [here](https://github.com/vector-im/riotX-android/graphs/contributors). 
+
+# Core team:
+
+Even if we try to be able to work on all the functionalities, we have more knowledge about what we have developed ourselves.
+
+## Benoit: Android team leader
+
+[@benoit.marty:matrix.org](https://matrix.to/#/@benoit.marty:matrix.org)
+- Android team leader and project leader, Android developer, GitHub community manager.
+- Specialist of the account creation, and many other fun features.
+- Reviewing and polishing developed features, code quality manager, PRs reviewer, GitHub community manager.
+- Release manager on the Play Store
+
+## François: Software architect
+
+[@ganfra:matrix.org](https://matrix.to/#/@ganfra:matrix.org)
+- Software architect, Android developer
+- First developer on the project.
+- Work mainly on the global architecture of the project.
+- Specialist of the timeline, and lots of other features.
+
+## Valere: Product manager, Android developer
+
+[@valere35:matrix.org](https://matrix.to/#/@valere35:matrix.org)
+- Product manager, Android developer
+- Specialist on the crypto implementation.
+
+# Other contributors
+
+First of all, we thank all contributors who use RiotX and report problems on this GitHub project or via the integrated rageshake function.
+
+We do not forget all translators, for their work of translating RiotX into many languages. They are also the authors of RiotX.
+
+Feel free to add your name below, when you contribute to the project!
diff --git a/CHANGES.md b/CHANGES.md
index 657f553c36..7c235d8704 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,13 +2,16 @@ Changes in RiotX 0.14.0 (2020-XX-XX)
 ===================================================
 
 Features ✨:
- -
+ - Enable encryption in unencrypted rooms, from the room settings (#212)
+ - Enable e2e by default when creating DM, and give the possibility to enable encryption when creating room (#837)
 
 Improvements πŸ™Œ:
- -
+ - Sharing things to RiotX: sort list by recent room first (#771)
+ - Hide the algorithm when turning on e2e (#897)
+ - Sort room members by display names
 
 Other changes:
- -
+ - Add support for /rainbow and /rainbowme commands (#879)
 
 Bugfix πŸ›:
  -
@@ -17,7 +20,8 @@ Translations πŸ—£:
  -
 
 Build 🧱:
- -
+ - Ensure builds are reproducible (#842)
+ - F-Droid: fix the "-dev" issue in version name (#815)
 
 Changes in RiotX 0.13.0 (2020-01-17)
 ===================================================
diff --git a/README.md b/README.md
index d9f94454cd..b43bcf643c 100644
--- a/README.md
+++ b/README.md
@@ -12,12 +12,13 @@ RiotX is an Android Matrix Client currently in beta but in active development.
 It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-android) with a new user experience. RiotX will become the official replacement as soon as all features are implemented.
 
 [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.riotx)
+[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.riotx)
 
 Nightly build: [![Buildkite](https://badge.buildkite.com/657d3db27364448d69d54f66c690f7788bc6aa80a7628e37f3.svg?branch=develop)](https://buildkite.com/matrix-dot-org/riotx-android/builds?branch=develop)
 
 # New Android SDK
 
-RiotX is based on a new Android SDK fully written in Kotlin (like RiotX). In order to make the early development as fast as possible, RiotX and the new SDK currently share the same git repository. We will make separate repos once the API is stable enough.
+RiotX is based on a new Android SDK fully written in Kotlin (like RiotX). In order to make the early development as fast as possible, RiotX and the new SDK currently share the same git repository. We will make separate repos once the SDK is stable enough.
 
 
 # Roadmap
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 529737be37..ea27df8ab6 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -74,7 +74,7 @@ android {
 }
 
 static def gitRevision() {
-    def cmd = "git rev-parse --short HEAD"
+    def cmd = "git rev-parse --short=8 HEAD"
     return cmd.execute().text.trim()
 }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt
index 1f7a7b144a..7ec6254613 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt
@@ -45,7 +45,8 @@ data class RoomSummary(
         val readMarkerId: String? = null,
         val userDrafts: List<UserDraft> = emptyList(),
         var isEncrypted: Boolean,
-        val typingRoomMemberIds: List<String> = emptyList()
+        val typingRoomMemberIds: List<String> = emptyList(),
+        val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS
 ) {
 
     val isVersioned: Boolean
@@ -53,4 +54,8 @@ data class RoomSummary(
 
     val hasNewMessages: Boolean
         get() = notificationCount != 0
+
+    companion object {
+        const val NOT_IN_BREADCRUMBS = -1
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
index 6ef7c262fb..7c6a40e3bb 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
@@ -56,9 +56,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
 import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
 import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
 import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
-import im.vector.matrix.android.internal.crypto.tasks.*
+import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask
+import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceWithUserPasswordTask
+import im.vector.matrix.android.internal.crypto.tasks.GetDeviceInfoTask
+import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
+import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
+import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
 import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
 import im.vector.matrix.android.internal.database.model.EventEntity
+import im.vector.matrix.android.internal.database.model.EventEntityFields
 import im.vector.matrix.android.internal.database.query.where
 import im.vector.matrix.android.internal.database.query.whereType
 import im.vector.matrix.android.internal.di.MoshiProvider
@@ -72,7 +78,12 @@ import im.vector.matrix.android.internal.task.configureWith
 import im.vector.matrix.android.internal.util.JsonCanonicalizer
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
 import im.vector.matrix.android.internal.util.fetchCopied
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
 import org.matrix.olm.OlmManager
 import timber.log.Timber
 import java.util.concurrent.atomic.AtomicBoolean
@@ -476,14 +487,16 @@ internal class DefaultCryptoService @Inject constructor(
     }
 
     /**
-     * Tells if a room is encrypted
+     * Tells if a room is encrypted with MXCRYPTO_ALGORITHM_MEGOLM
      *
      * @param roomId the room id
-     * @return true if the room is encrypted
+     * @return true if the room is encrypted with algorithm MXCRYPTO_ALGORITHM_MEGOLM
      */
     override fun isRoomEncrypted(roomId: String): Boolean {
-        val encryptionEvent = monarchy.fetchCopied {
-            EventEntity.whereType(it, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION).findFirst()
+        val encryptionEvent = monarchy.fetchCopied { realm ->
+            EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
+                    .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
+                    .findFirst()
         }
         return encryptionEvent != null
     }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt
index 72d221aafe..896f994c99 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt
@@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.api.session.room.model.tag.RoomTag
 import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
-import java.util.*
+import java.util.UUID
 import javax.inject.Inject
 
 internal class RoomSummaryMapper @Inject constructor(
@@ -74,7 +74,8 @@ internal class RoomSummaryMapper @Inject constructor(
                 canonicalAlias = roomSummaryEntity.canonicalAlias,
                 aliases = roomSummaryEntity.aliases.toList(),
                 isEncrypted = roomSummaryEntity.isEncrypted,
-                typingRoomMemberIds = roomSummaryEntity.typingUserIds.toList()
+                typingRoomMemberIds = roomSummaryEntity.typingUserIds.toList(),
+                breadcrumbsIndex = roomSummaryEntity.breadcrumbsIndex
         )
     }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt
index d857d8810c..c56e9ba10e 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt
@@ -17,35 +17,37 @@
 package im.vector.matrix.android.internal.database.model
 
 import im.vector.matrix.android.api.session.room.model.Membership
+import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.api.session.room.model.VersioningState
 import io.realm.RealmList
 import io.realm.RealmObject
 import io.realm.annotations.PrimaryKey
 
-internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
-                                      var displayName: String? = "",
-                                      var avatarUrl: String? = "",
-                                      var topic: String? = "",
-                                      var latestPreviewableEvent: TimelineEventEntity? = null,
-                                      var heroes: RealmList<String> = RealmList(),
-                                      var joinedMembersCount: Int? = 0,
-                                      var invitedMembersCount: Int? = 0,
-                                      var isDirect: Boolean = false,
-                                      var directUserId: String? = null,
-                                      var otherMemberIds: RealmList<String> = RealmList(),
-                                      var notificationCount: Int = 0,
-                                      var highlightCount: Int = 0,
-                                      var readMarkerId: String? = null,
-                                      var hasUnreadMessages: Boolean = false,
-                                      var tags: RealmList<RoomTagEntity> = RealmList(),
-                                      var userDrafts: UserDraftsEntity? = null,
-                                      var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
-                                      var canonicalAlias: String? = null,
-                                      var aliases: RealmList<String> = RealmList(),
-                                      // this is required for querying
-                                      var flatAliases: String = "",
-                                      var isEncrypted: Boolean = false,
-                                      var typingUserIds: RealmList<String> = RealmList()
+internal open class RoomSummaryEntity(
+        @PrimaryKey var roomId: String = "",
+        var displayName: String? = "",
+        var avatarUrl: String? = "",
+        var topic: String? = "",
+        var latestPreviewableEvent: TimelineEventEntity? = null,
+        var heroes: RealmList<String> = RealmList(),
+        var joinedMembersCount: Int? = 0,
+        var invitedMembersCount: Int? = 0,
+        var isDirect: Boolean = false,
+        var directUserId: String? = null,
+        var otherMemberIds: RealmList<String> = RealmList(),
+        var notificationCount: Int = 0,
+        var highlightCount: Int = 0,
+        var readMarkerId: String? = null,
+        var hasUnreadMessages: Boolean = false,
+        var tags: RealmList<RoomTagEntity> = RealmList(),
+        var userDrafts: UserDraftsEntity? = null,
+        var breadcrumbsIndex: Int = RoomSummary.NOT_IN_BREADCRUMBS,
+        var canonicalAlias: String? = null,
+        var aliases: RealmList<String> = RealmList(),
+        // this is required for querying
+        var flatAliases: String = "",
+        var isEncrypted: Boolean = false,
+        var typingUserIds: RealmList<String> = RealmList()
 ) : RealmObject() {
 
     private var membershipStr: String = Membership.NONE.name
@@ -66,7 +68,5 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
             versioningStateStr = value.name
         }
 
-    companion object {
-        const val NOT_IN_BREADCRUMBS = -1
-    }
+    companion object
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt
index 0cfc5aad3c..cf4c858bbd 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt
@@ -130,7 +130,7 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
         return RoomSummaryEntity.where(realm)
                 .isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
                 .notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
-                .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummaryEntity.NOT_IN_BREADCRUMBS)
+                .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummary.NOT_IN_BREADCRUMBS)
                 .sort(RoomSummaryEntityFields.BREADCRUMBS_INDEX)
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt
index bca04bbba4..5063bd3908 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt
@@ -23,8 +23,12 @@ import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
 import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
 import im.vector.matrix.android.api.session.room.model.RoomTopicContent
+import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
+import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
 import im.vector.matrix.android.internal.database.mapper.ContentMapper
 import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
+import im.vector.matrix.android.internal.database.model.EventEntity
+import im.vector.matrix.android.internal.database.model.EventEntityFields
 import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
 import im.vector.matrix.android.internal.database.model.TimelineEventEntity
@@ -32,6 +36,8 @@ import im.vector.matrix.android.internal.database.query.getOrCreate
 import im.vector.matrix.android.internal.database.query.getOrNull
 import im.vector.matrix.android.internal.database.query.isEventRead
 import im.vector.matrix.android.internal.database.query.latestEvent
+import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.database.query.whereType
 import im.vector.matrix.android.internal.di.UserId
 import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
 import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
@@ -94,10 +100,15 @@ internal class RoomSummaryUpdater @Inject constructor(
         }
 
         val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
+
         val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
         val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
         val lastAliasesEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root
-        val encryptionEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ENCRYPTION, stateKey = "")?.root
+
+        // Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
+        val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
+                .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
+                .findFirst()
 
         roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
                 // avoid this call if we are sure there are unread events
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
index 07e8664102..f76c2ff448 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
@@ -21,16 +21,26 @@ import im.vector.matrix.android.api.pushrules.RuleScope
 import im.vector.matrix.android.api.pushrules.RuleSetKey
 import im.vector.matrix.android.api.session.events.model.toModel
 import im.vector.matrix.android.api.session.room.model.RoomMemberContent
+import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
 import im.vector.matrix.android.internal.database.mapper.asDomain
-import im.vector.matrix.android.internal.database.model.*
+import im.vector.matrix.android.internal.database.model.BreadcrumbsEntity
+import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
+import im.vector.matrix.android.internal.database.model.PushRulesEntity
+import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
+import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
 import im.vector.matrix.android.internal.database.query.getDirectRooms
 import im.vector.matrix.android.internal.database.query.getOrCreate
 import im.vector.matrix.android.internal.database.query.where
 import im.vector.matrix.android.internal.di.UserId
 import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
 import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
-import im.vector.matrix.android.internal.session.sync.model.accountdata.*
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataBreadcrumbs
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
 import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
 import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
 import io.realm.Realm
@@ -177,10 +187,10 @@ internal class UserAccountDataSyncHandler @Inject constructor(
         // Update the room summaries
         // Reset all the indexes...
         RoomSummaryEntity.where(realm)
-                .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummaryEntity.NOT_IN_BREADCRUMBS)
+                .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummary.NOT_IN_BREADCRUMBS)
                 .findAll()
                 .forEach {
-                    it.breadcrumbsIndex = RoomSummaryEntity.NOT_IN_BREADCRUMBS
+                    it.breadcrumbsIndex = RoomSummary.NOT_IN_BREADCRUMBS
                 }
 
         // ...and apply new indexes
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveBreadcrumbsTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveBreadcrumbsTask.kt
index 008dd1d652..ecdcdb8768 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveBreadcrumbsTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveBreadcrumbsTask.kt
@@ -16,6 +16,7 @@
 package im.vector.matrix.android.internal.session.user.accountdata
 
 import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.internal.database.model.BreadcrumbsEntity
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
@@ -50,10 +51,10 @@ internal class DefaultSaveBreadcrumbsTask @Inject constructor(
             // Update the room summaries
             // Reset all the indexes...
             RoomSummaryEntity.where(realm)
-                    .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummaryEntity.NOT_IN_BREADCRUMBS)
+                    .greaterThan(RoomSummaryEntityFields.BREADCRUMBS_INDEX, RoomSummary.NOT_IN_BREADCRUMBS)
                     .findAll()
                     .forEach {
-                        it.breadcrumbsIndex = RoomSummaryEntity.NOT_IN_BREADCRUMBS
+                        it.breadcrumbsIndex = RoomSummary.NOT_IN_BREADCRUMBS
                     }
 
             // ...and apply new indexes
diff --git a/matrix-sdk-android/src/main/res/values/strings_RiotX.xml b/matrix-sdk-android/src/main/res/values/strings_RiotX.xml
index d781ec5f1e..d030b857cf 100644
--- a/matrix-sdk-android/src/main/res/values/strings_RiotX.xml
+++ b/matrix-sdk-android/src/main/res/values/strings_RiotX.xml
@@ -1,4 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
+    <string name="notice_end_to_end_ok">%1$s turned on end-to-end encryption.</string>
+    <string name="notice_end_to_end_unknown_algorithm">%1$s turned on end-to-end encryption (unrecognised algorithm %2$s).</string>
+
 </resources>
diff --git a/vector/build.gradle b/vector/build.gradle
index ac7fa3f735..bb2edea5d5 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -27,7 +27,7 @@ static def generateVersionCodeFromTimestamp() {
     // It's unix timestamp, minus timestamp of October 3rd 2018 (first commit date) divided by 100: It's incremented by one every 100 seconds.
     // plus 20_000_000 for compatibility reason with the previous way the Version Code was computed
     // Note that the result will be multiplied by 10 when adding the digit for the arch
-    return ((getGitTimestamp() - 1_538_524_800 ) / 100).toInteger() + 20_000_000
+    return ((getGitTimestamp() - 1_538_524_800) / 100).toInteger() + 20_000_000
 }
 
 def generateVersionCodeFromVersionName() {
@@ -45,7 +45,7 @@ def getVersionCode() {
 }
 
 static def gitRevision() {
-    def cmd = "git rev-parse --short HEAD"
+    def cmd = "git rev-parse --short=8 HEAD"
     return cmd.execute().text.trim()
 }
 
@@ -66,7 +66,8 @@ static def gitBranchName() {
     }
 }
 
-static def getVersionSuffix() {
+// For Google Play build, build on any other branch than master will have a "-dev" suffix
+static def getGplayVersionSuffix() {
     if (gitBranchName() == "master") {
         return ""
     } else {
@@ -74,6 +75,20 @@ static def getVersionSuffix() {
     }
 }
 
+static def gitTag() {
+    def cmd = "git describe --exact-match --tags"
+    return cmd.execute().text.trim()
+}
+
+// For F-Droid build, build on a not tagged commit will have a "-dev" suffix
+static def getFdroidVersionSuffix() {
+    if (gitTag() == "") {
+        return "-dev"
+    } else {
+        return ""
+    }
+}
+
 project.android.buildTypes.all { buildType ->
     buildType.javaCompileOptions.annotationProcessorOptions.arguments =
             [
@@ -102,8 +117,6 @@ android {
         // Other branches (master, features, etc.) will have version code based on application version.
         versionCode project.getVersionCode()
 
-        versionName "${versionMajor}.${versionMinor}.${versionPatch}${getVersionSuffix()}"
-
         buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
         resValue "string", "git_revision", "\"${gitRevision()}\""
 
@@ -190,6 +203,8 @@ android {
         gplay {
             dimension "store"
 
+            versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}"
+
             resValue "bool", "isGplay", "true"
             buildConfigField "boolean", "ALLOW_FCM_USE", "true"
             buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"G\""
@@ -199,6 +214,8 @@ android {
         fdroid {
             dimension "store"
 
+            versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}"
+
             resValue "bool", "isGplay", "false"
             buildConfigField "boolean", "ALLOW_FCM_USE", "false"
             buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\""
diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
index b99268d6d4..4fdd2f1f73 100644
--- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
+++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
@@ -22,32 +22,49 @@ import androidx.fragment.app.FragmentFactory
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoMap
+import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
+import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
 import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
 import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
 import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment
 import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment
 import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment
+import im.vector.riotx.features.grouplist.GroupListFragment
 import im.vector.riotx.features.home.HomeDetailFragment
 import im.vector.riotx.features.home.HomeDrawerFragment
 import im.vector.riotx.features.home.LoadingFragment
-import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
-import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
-import im.vector.riotx.features.grouplist.GroupListFragment
 import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment
 import im.vector.riotx.features.home.room.detail.RoomDetailFragment
 import im.vector.riotx.features.home.room.list.RoomListFragment
-import im.vector.riotx.features.login.*
+import im.vector.riotx.features.login.LoginCaptchaFragment
+import im.vector.riotx.features.login.LoginFragment
+import im.vector.riotx.features.login.LoginGenericTextInputFormFragment
+import im.vector.riotx.features.login.LoginResetPasswordFragment
+import im.vector.riotx.features.login.LoginResetPasswordMailConfirmationFragment
+import im.vector.riotx.features.login.LoginResetPasswordSuccessFragment
+import im.vector.riotx.features.login.LoginServerSelectionFragment
+import im.vector.riotx.features.login.LoginServerUrlFormFragment
+import im.vector.riotx.features.login.LoginSignUpSignInSelectionFragment
+import im.vector.riotx.features.login.LoginSplashFragment
+import im.vector.riotx.features.login.LoginWaitForEmailFragment
+import im.vector.riotx.features.login.LoginWebFragment
 import im.vector.riotx.features.login.terms.LoginTermsFragment
-import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
 import im.vector.riotx.features.reactions.EmojiChooserFragment
 import im.vector.riotx.features.reactions.EmojiSearchResultFragment
 import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
 import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
 import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
 import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
+import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
 import im.vector.riotx.features.roomprofile.RoomProfileFragment
 import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
-import im.vector.riotx.features.settings.*
+import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
+import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
+import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
+import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment
+import im.vector.riotx.features.settings.VectorSettingsNotificationsTroubleshootFragment
+import im.vector.riotx.features.settings.VectorSettingsPreferencesFragment
+import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
 import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
 import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
 import im.vector.riotx.features.settings.push.PushGatewaysFragment
@@ -272,6 +289,11 @@ interface FragmentModule {
     @FragmentKey(RoomMemberListFragment::class)
     fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment
 
+    @Binds
+    @IntoMap
+    @FragmentKey(RoomSettingsFragment::class)
+    fun bindRoomSettingsFragment(fragment: RoomSettingsFragment): Fragment
+
     @Binds
     @IntoMap
     @FragmentKey(RoomMemberProfileFragment::class)
diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt
index 0a7dcb0de7..404570568d 100644
--- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt
+++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt
@@ -43,11 +43,14 @@ abstract class ProfileActionItem : VectorEpoxyModel<ProfileActionItem.Holder>()
     @EpoxyAttribute
     var destructive: Boolean = false
     @EpoxyAttribute
-    lateinit var listener: View.OnClickListener
+    var listener: View.OnClickListener? = null
 
     override fun bind(holder: Holder) {
         super.bind(holder)
         holder.view.setOnClickListener(listener)
+        if (listener == null) {
+            holder.view.isClickable = false
+        }
         holder.editable.isVisible = editable
         holder.title.text = title
         val tintColor = if (destructive) {
diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt
index 32060f577d..ab68bdc4ce 100644
--- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt
+++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt
@@ -19,6 +19,7 @@ package im.vector.riotx.core.epoxy.profiles
 
 import androidx.annotation.DrawableRes
 import com.airbnb.epoxy.EpoxyController
+import im.vector.riotx.core.epoxy.ClickListener
 import im.vector.riotx.core.epoxy.dividerItem
 
 fun EpoxyController.buildProfileSection(title: String) {
@@ -37,7 +38,7 @@ fun EpoxyController.buildProfileAction(
         @DrawableRes icon: Int = 0,
         destructive: Boolean = false,
         divider: Boolean = true,
-        action: () -> Unit
+        action: ClickListener? = null
 ) {
     profileActionItem {
         iconRes(icon)
@@ -47,7 +48,7 @@ fun EpoxyController.buildProfileAction(
         destructive(destructive)
         title(title)
         listener { _ ->
-            action()
+            action?.invoke()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Exhaustive.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Exhaustive.kt
new file mode 100644
index 0000000000..ac6e14392e
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/core/extensions/Exhaustive.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.core.extensions
+
+// Trick to ensure that when block is exhaustive
+val <T> T.exhaustive: T get() = this
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
index 93a48ed925..6ce3b7449d 100644
--- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
@@ -22,10 +22,15 @@ import android.app.ProgressDialog
 import android.content.Context
 import android.os.Bundle
 import android.os.Parcelable
-import android.view.*
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.View
+import android.view.ViewGroup
 import androidx.annotation.CallSuper
 import androidx.annotation.LayoutRes
 import androidx.annotation.MainThread
+import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.widget.Toolbar
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProviders
@@ -35,11 +40,13 @@ import com.airbnb.mvrx.BaseMvRxFragment
 import com.airbnb.mvrx.MvRx
 import com.bumptech.glide.util.Util.assertMainThread
 import com.google.android.material.snackbar.Snackbar
+import im.vector.riotx.R
 import im.vector.riotx.core.di.DaggerScreenComponent
 import im.vector.riotx.core.di.HasScreenInjector
 import im.vector.riotx.core.di.ScreenComponent
 import im.vector.riotx.core.error.ErrorFormatter
 import im.vector.riotx.features.navigation.Navigator
+import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.disposables.CompositeDisposable
 import io.reactivex.disposables.Disposable
 import timber.log.Timber
@@ -120,6 +127,14 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
         mUnBinder = ButterKnife.bind(this, view)
     }
 
+    open fun showLoading(message: CharSequence?) {
+        showLoadingDialog(message)
+    }
+
+    open fun showFailure(throwable: Throwable) {
+        displayErrorDialog(throwable)
+    }
+
     @CallSuper
     override fun onDestroyView() {
         super.onDestroyView()
@@ -182,10 +197,10 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
         }
     }
 
-    protected fun showLoadingDialog(message: CharSequence, cancelable: Boolean = false) {
+    protected fun showLoadingDialog(message: CharSequence? = null, cancelable: Boolean = false) {
         progress = ProgressDialog(requireContext()).apply {
             setCancelable(cancelable)
-            setMessage(message)
+            setMessage(message ?: getString(R.string.please_wait))
             setProgressStyle(ProgressDialog.STYLE_SPINNER)
             show()
         }
@@ -220,6 +235,21 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
         return this
     }
 
+    /* ==========================================================================================
+     * ViewEvents
+     * ========================================================================================== */
+
+    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
+        viewEvents
+                .observe()
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe {
+                    dismissLoadingDialog()
+                    observer(it)
+                }
+                .disposeOnDestroyView()
+    }
+
     /* ==========================================================================================
      * MENU MANAGEMENT
      * ========================================================================================== */
@@ -233,4 +263,16 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
             inflater.inflate(menuRes, menu)
         }
     }
+
+    /* ==========================================================================================
+     * Common Dialogs
+     * ========================================================================================== */
+
+    protected fun displayErrorDialog(throwable: Throwable) {
+        AlertDialog.Builder(requireActivity())
+                .setTitle(R.string.dialog_title_error)
+                .setMessage(errorFormatter.toHumanReadable(throwable))
+                .setPositiveButton(R.string.ok, null)
+                .show()
+    }
 }
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewEvents.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewEvents.kt
new file mode 100644
index 0000000000..b5c191f81a
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewEvents.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.core.platform
+
+/**
+ * Interface for View Events
+ */
+interface VectorViewEvents
+
+/**
+ * To use when no view events is associated to the ViewModel
+ */
+object EmptyViewEvents : VectorViewEvents
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
index 74b18be3c2..bda4426c45 100644
--- a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
@@ -16,20 +16,23 @@
 
 package im.vector.riotx.core.platform
 
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.airbnb.mvrx.*
-import im.vector.riotx.core.utils.LiveEvent
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.BaseMvRxViewModel
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.Success
+import im.vector.riotx.core.utils.DataSource
+import im.vector.riotx.core.utils.PublishDataSource
 import io.reactivex.Observable
 import io.reactivex.Single
 
-abstract class VectorViewModel<S : MvRxState, A : VectorViewModelAction>(initialState: S)
+abstract class VectorViewModel<S : MvRxState, VA : VectorViewModelAction, VE : VectorViewEvents>(initialState: S)
     : BaseMvRxViewModel<S>(initialState, false) {
 
-    // Generic handling of any request error
-    protected val _requestErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
-    val requestErrorLiveData: LiveData<LiveEvent<Throwable>>
-        get() = _requestErrorLiveData
+    // Used to post transient events to the View
+    protected val _viewEvents = PublishDataSource<VE>()
+    val viewEvents: DataSource<VE> = _viewEvents
 
     /**
      * This method does the same thing as the execute function, but it doesn't subscribe to the stream
@@ -53,5 +56,5 @@ abstract class VectorViewModel<S : MvRxState, A : VectorViewModelAction>(initial
                 .doOnNext { setState { stateReducer(it) } }
     }
 
-    abstract fun handle(action: A)
+    abstract fun handle(action: VA)
 }
diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt
index f9e5654726..e91a2896bc 100644
--- a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt
+++ b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt
@@ -113,3 +113,39 @@ fun containsOnlyEmojis(str: String?): Boolean {
 
     return res
 }
+
+/**
+ * Same as split, but considering emojis
+ */
+fun CharSequence.splitEmoji(): List<CharSequence> {
+    val result = mutableListOf<CharSequence>()
+
+    var index = 0
+
+    while (index < length) {
+        val firstChar = get(index)
+
+        if (firstChar.toInt() == 0x200e) {
+            // Left to right mark. What should I do with it?
+        } else if (firstChar.toInt() in 0xD800..0xDBFF && index + 1 < length) {
+            // We have the start of a surrogate pair
+            val secondChar = get(index + 1)
+
+            if (secondChar.toInt() in 0xDC00..0xDFFF) {
+                // We have an emoji
+                result.add("$firstChar$secondChar")
+                index++
+            } else {
+                // Not sure what we have here...
+                result.add("$firstChar")
+            }
+        } else {
+            // Regular char
+            result.add("$firstChar")
+        }
+
+        index++
+    }
+
+    return result
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/command/Command.kt b/vector/src/main/java/im/vector/riotx/features/command/Command.kt
index 8b72ffa4a6..6151ae0d66 100644
--- a/vector/src/main/java/im/vector/riotx/features/command/Command.kt
+++ b/vector/src/main/java/im/vector/riotx/features/command/Command.kt
@@ -37,6 +37,8 @@ enum class Command(val command: String, val parameters: String, @StringRes val d
     KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user),
     CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick),
     MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown),
+    RAINBOW("/rainbow", "<message>", R.string.command_description_rainbow),
+    RAINBOW_EMOTE("/rainbowme", "<message>", R.string.command_description_rainbow_emote),
     CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token),
     SPOILER("/spoiler", "<message>", R.string.command_description_spoiler);
 
diff --git a/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt b/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt
index 359f2c1f13..58671df539 100644
--- a/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt
+++ b/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt
@@ -80,6 +80,16 @@ object CommandParser {
 
                     ParsedCommand.SendEmote(message)
                 }
+                Command.RAINBOW.command                  -> {
+                    val message = textMessage.subSequence(Command.RAINBOW.command.length, textMessage.length).trim()
+
+                    ParsedCommand.SendRainbow(message)
+                }
+                Command.RAINBOW_EMOTE.command                  -> {
+                    val message = textMessage.subSequence(Command.RAINBOW_EMOTE.command.length, textMessage.length).trim()
+
+                    ParsedCommand.SendRainbowEmote(message)
+                }
                 Command.JOIN_ROOM.command              -> {
                     if (messageParts.size >= 2) {
                         val roomAlias = messageParts[1]
diff --git a/vector/src/main/java/im/vector/riotx/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/riotx/features/command/ParsedCommand.kt
index dd7c0c7e86..c43b78d71c 100644
--- a/vector/src/main/java/im/vector/riotx/features/command/ParsedCommand.kt
+++ b/vector/src/main/java/im/vector/riotx/features/command/ParsedCommand.kt
@@ -34,6 +34,8 @@ sealed class ParsedCommand {
     // Valid commands:
 
     class SendEmote(val message: CharSequence) : ParsedCommand()
+    class SendRainbow(val message: CharSequence) : ParsedCommand()
+    class SendRainbowEmote(val message: CharSequence) : ParsedCommand()
     class BanUser(val userId: String, val reason: String?) : ParsedCommand()
     class UnbanUser(val userId: String, val reason: String?) : ParsedCommand()
     class SetUserPowerLevel(val userId: String, val powerLevel: Int) : ParsedCommand()
diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomDirectoryUsersFragment.kt
index 77cee5fd6f..ecfe054767 100644
--- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomDirectoryUsersFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomDirectoryUsersFragment.kt
@@ -23,7 +23,11 @@ import com.airbnb.mvrx.withState
 import com.jakewharton.rxbinding3.widget.textChanges
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotx.R
-import im.vector.riotx.core.extensions.*
+import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.extensions.hideKeyboard
+import im.vector.riotx.core.extensions.setupAsSearch
+import im.vector.riotx.core.extensions.showKeyboard
 import im.vector.riotx.core.platform.VectorBaseFragment
 import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
 import javax.inject.Inject
diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt
index 9023312530..fd56aacee4 100644
--- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt
@@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.user.model.User
 import im.vector.matrix.android.api.util.toMatrixItem
 import im.vector.matrix.rx.rx
 import im.vector.riotx.core.extensions.postLiveEvent
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.utils.LiveEvent
 import io.reactivex.Single
@@ -51,7 +52,7 @@ data class SelectUserAction(
 class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
                                                             initialState: CreateDirectRoomViewState,
                                                             private val session: Session)
-    : VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction>(initialState) {
+    : VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
index 4acb318033..bed18ae99d 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
@@ -24,11 +24,12 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 
 class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
                                                               session: Session
-) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsAction>(initialState),
+) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsAction, EmptyViewEvents>(initialState),
         KeysBackupStateListener {
 
     @AssistedInject.Factory
diff --git a/vector/src/main/java/im/vector/riotx/features/form/FormSwitchItem.kt b/vector/src/main/java/im/vector/riotx/features/form/FormSwitchItem.kt
index 2e48a8b709..1f3c7c81bb 100644
--- a/vector/src/main/java/im/vector/riotx/features/form/FormSwitchItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/form/FormSwitchItem.kt
@@ -44,6 +44,12 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
     var summary: String? = null
 
     override fun bind(holder: Holder) {
+        holder.view.setOnClickListener {
+            if (enabled) {
+                holder.switchView.toggle()
+            }
+        }
+
         holder.titleView.text = title
         holder.summaryView.setTextOrHide(summary)
 
diff --git a/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt
index 816f721040..c31a30b432 100644
--- a/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/grouplist/GroupListViewModel.kt
@@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.rx.rx
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.postLiveEvent
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.core.utils.LiveEvent
@@ -45,7 +46,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
                                                      private val selectedGroupStore: SelectedGroupDataSource,
                                                      private val session: Session,
                                                      private val stringProvider: StringProvider
-) : VectorViewModel<GroupListViewState, GroupListAction>(initialState) {
+) : VectorViewModel<GroupListViewState, GroupListAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt
index d6d8f06e60..6b322a2b48 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailViewModel.kt
@@ -24,6 +24,7 @@ import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.rx.rx
 import im.vector.riotx.core.di.HasScreenInjector
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.features.grouplist.SelectedGroupDataSource
@@ -40,7 +41,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
                                                       private val selectedGroupStore: SelectedGroupDataSource,
                                                       private val homeRoomListStore: HomeRoomListDataSource,
                                                       private val stringProvider: StringProvider)
-    : VectorViewModel<HomeDetailViewState, HomeDetailAction>(initialState) {
+    : VectorViewModel<HomeDetailViewState, HomeDetailAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
index 83e9e0fb3f..f8a1e302af 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt
@@ -24,12 +24,13 @@ import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.rx.rx
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import io.reactivex.schedulers.Schedulers
 
 class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState,
                                                        private val session: Session)
-    : VectorViewModel<BreadcrumbsViewState, EmptyAction>(initialState) {
+    : VectorViewModel<BreadcrumbsViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
index bb54d55cc3..e3f57fc25e 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
@@ -85,6 +85,7 @@ import im.vector.riotx.R
 import im.vector.riotx.core.dialogs.withColoredButton
 import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
 import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.extensions.hideKeyboard
 import im.vector.riotx.core.extensions.observeEvent
 import im.vector.riotx.core.extensions.setTextOrHide
@@ -306,16 +307,13 @@ class RoomDetailFragment @Inject constructor(
             displayRoomDetailActionResult(it)
         }
 
-        roomDetailViewModel.viewEvents
-                .observe()
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe {
-                    when (it) {
-                        is RoomDetailViewEvents.Failure             -> showErrorInSnackbar(it.throwable)
-                        is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
-                    }
-                }
-                .disposeOnDestroyView()
+        roomDetailViewModel.observeViewEvents {
+            when (it) {
+                is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
+                is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
+            }.exhaustive
+        }
+
     }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewEvents.kt
index 4de399c838..6470e0c338 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewEvents.kt
@@ -16,10 +16,12 @@
 
 package im.vector.riotx.features.home.room.detail
 
+import im.vector.riotx.core.platform.VectorViewEvents
+
 /**
  * Transient events for RoomDetail
  */
-sealed class RoomDetailViewEvents {
+sealed class RoomDetailViewEvents : VectorViewEvents {
     data class Failure(val throwable: Throwable) : RoomDetailViewEvents()
     data class OnNewTimelineEvents(val eventIds: List<String>) : RoomDetailViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
index 60443f6412..51808567cb 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
@@ -60,17 +60,17 @@ import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventConten
 import im.vector.matrix.rx.rx
 import im.vector.matrix.rx.unwrap
 import im.vector.riotx.R
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.extensions.postLiveEvent
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.core.resources.UserPreferencesProvider
-import im.vector.riotx.core.utils.DataSource
 import im.vector.riotx.core.utils.LiveEvent
 import im.vector.riotx.core.utils.NoOpMatrixCallback
-import im.vector.riotx.core.utils.PublishDataSource
 import im.vector.riotx.core.utils.subscribeLogError
 import im.vector.riotx.features.command.CommandParser
 import im.vector.riotx.features.command.ParsedCommand
+import im.vector.riotx.features.home.room.detail.composer.rainbow.RainbowGenerator
 import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
 import im.vector.riotx.features.home.room.typing.TypingHelper
 import im.vector.riotx.features.settings.VectorPreferences
@@ -90,8 +90,9 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                                                       private val vectorPreferences: VectorPreferences,
                                                       private val stringProvider: StringProvider,
                                                       private val typingHelper: TypingHelper,
+                                                      private val rainbowGenerator: RainbowGenerator,
                                                       private val session: Session
-) : VectorViewModel<RoomDetailViewState, RoomDetailAction>(initialState), Timeline.Listener {
+) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
 
     private val room = session.getRoom(initialState.roomId)!!
     private val eventId = initialState.eventId
@@ -114,9 +115,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
     var timeline = room.createTimeline(eventId, timelineSettings)
         private set
 
-    private val _viewEvents = PublishDataSource<RoomDetailViewEvents>()
-    val viewEvents: DataSource<RoomDetailViewEvents> = _viewEvents
-
     // Can be used for several actions, for a one shot result
     private val _requestLiveData = MutableLiveData<LiveEvent<Async<RoomDetailAction>>>()
     val requestLiveData: LiveData<LiveEvent<Async<RoomDetailAction>>>
@@ -317,6 +315,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
         }
     }
 
+    // TODO Cleanup this and use ViewEvents
     private val _nonBlockingPopAlert = MutableLiveData<LiveEvent<Pair<Int, List<Any>>>>()
     val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>>
         get() = _nonBlockingPopAlert
@@ -411,6 +410,20 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                             _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
                             popDraft()
                         }
+                        is ParsedCommand.SendRainbow              -> {
+                            slashCommandResult.message.toString().let {
+                                room.sendFormattedTextMessage(it, rainbowGenerator.generate(it))
+                            }
+                            _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
+                            popDraft()
+                        }
+                        is ParsedCommand.SendRainbowEmote         -> {
+                            slashCommandResult.message.toString().let {
+                                room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE)
+                            }
+                            _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
+                            popDraft()
+                        }
                         is ParsedCommand.SendSpoiler              -> {
                             room.sendFormattedTextMessage(
                                     "[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})",
@@ -427,7 +440,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                             // TODO
                             _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
                         }
-                    }
+                    }.exhaustive
                 }
                 is SendMode.EDIT    -> {
                     // is original event a reply?
@@ -485,7 +498,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                         popDraft()
                     }
                 }
-            }
+            }.exhaustive
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGenerator.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGenerator.kt
new file mode 100644
index 0000000000..3868be4e2e
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGenerator.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.home.room.detail.composer.rainbow
+
+import im.vector.riotx.core.utils.splitEmoji
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlin.math.roundToInt
+
+/**
+ * Inspired from React-Sdk
+ * Ref: https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/utils/colour.js
+ */
+class RainbowGenerator @Inject constructor() {
+
+    fun generate(text: String): String {
+        val split = text.splitEmoji()
+        val frequency = 360f / split.size
+
+        return split
+                .mapIndexed { idx, letter ->
+                    // Do better than React-Sdk: Avoid adding font color for spaces
+                    if (letter == " ") {
+                        "$letter"
+                    } else {
+                        val dashColor = hueToRGB(idx * frequency, 1.0f, 0.5f).toDashColor()
+                        "<font color=\"$dashColor\">$letter</font>"
+                    }
+                }
+                .joinToString(separator = "")
+    }
+
+    private fun hueToRGB(h: Float, s: Float, l: Float): RgbColor {
+        val c = s * (1 - abs(2 * l - 1))
+        val x = c * (1 - abs((h / 60) % 2 - 1))
+        val m = l - c / 2
+
+        var r = 0f
+        var g = 0f
+        var b = 0f
+
+        when {
+            h < 60f  -> {
+                r = c
+                g = x
+            }
+            h < 120f -> {
+                r = x
+                g = c
+            }
+            h < 180f -> {
+                g = c
+                b = x
+            }
+            h < 240f -> {
+                g = x
+                b = c
+            }
+            h < 300f -> {
+                r = x
+                b = c
+            }
+            else     -> {
+                r = c
+                b = x
+            }
+        }
+
+        return RgbColor(
+                ((r + m) * 255).roundToInt(),
+                ((g + m) * 255).roundToInt(),
+                ((b + m) * 255).roundToInt()
+        )
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RgbColor.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RgbColor.kt
new file mode 100644
index 0000000000..bf2e808a36
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RgbColor.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.home.room.detail.composer.rainbow
+
+data class RgbColor(
+        val r: Int,
+        val g: Int,
+        val b: Int
+)
+
+fun RgbColor.toDashColor(): String {
+    return listOf(r, g, b)
+            .joinToString(separator = "", prefix = "#") {
+                it.toString(16).padStart(2, '0')
+            }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
index f90dbed95e..bdfdb02be1 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
@@ -46,17 +46,14 @@ class MessageActionsEpoxyController @Inject constructor(
 
     override fun buildModels(state: MessageActionState) {
         // Message preview
-        val body = state.messageBody
-        if (body != null) {
-            bottomSheetMessagePreviewItem {
-                id("preview")
-                avatarRenderer(avatarRenderer)
-                matrixItem(state.informationData.matrixItem)
-                movementMethod(createLinkMovementMethod(listener))
-                userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) }
-                body(body.linkify(listener))
-                time(state.time())
-            }
+        bottomSheetMessagePreviewItem {
+            id("preview")
+            avatarRenderer(avatarRenderer)
+            matrixItem(state.informationData.matrixItem)
+            movementMethod(createLinkMovementMethod(listener))
+            userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) }
+            body(state.messageBody.linkify(listener))
+            time(state.time())
         }
 
         // Send state
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index 3f0e8b041f..936bc263e7 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -15,7 +15,12 @@
  */
 package im.vector.riotx.features.home.room.detail.timeline.action
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import dagger.Lazy
@@ -35,6 +40,7 @@ import im.vector.matrix.rx.rx
 import im.vector.matrix.rx.unwrap
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.canReact
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
@@ -44,7 +50,8 @@ import im.vector.riotx.features.html.VectorHtmlCompressor
 import im.vector.riotx.features.reactions.data.EmojiDataSource
 import im.vector.riotx.features.settings.VectorPreferences
 import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Date
+import java.util.Locale
 
 /**
  * Quick reactions state
@@ -59,7 +66,7 @@ data class MessageActionState(
         val eventId: String,
         val informationData: MessageInformationData,
         val timelineEvent: Async<TimelineEvent> = Uninitialized,
-        val messageBody: CharSequence? = null,
+        val messageBody: CharSequence = "",
         // For quick reactions
         val quickStates: Async<List<ToggleState>> = Uninitialized,
         // For actions
@@ -89,7 +96,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
                                                           private val noticeEventFormatter: NoticeEventFormatter,
                                                           private val stringProvider: StringProvider,
                                                           private val vectorPreferences: VectorPreferences
-) : VectorViewModel<MessageActionState, MessageActionsAction>(initialState) {
+) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
 
     private val eventId = initialState.eventId
     private val informationData = initialState.informationData
@@ -154,13 +161,16 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
 
     private fun observeTimelineEventState() {
         asyncSubscribe(MessageActionState::timelineEvent) { timelineEvent ->
-            val computedMessage = computeMessageBody(timelineEvent)
-            val actions = actionsForEvent(timelineEvent)
-            setState { copy(messageBody = computedMessage, actions = actions) }
+            setState {
+                copy(
+                        messageBody = computeMessageBody(timelineEvent),
+                        actions = actionsForEvent(timelineEvent)
+                )
+            }
         }
     }
 
-    private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence? {
+    private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence {
         return when (timelineEvent.root.getClearType()) {
             EventType.MESSAGE,
             EventType.STICKER     -> {
@@ -188,7 +198,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
                 noticeEventFormatter.format(timelineEvent)
             }
             else                  -> null
-        }
+        } ?: ""
     }
 
     private fun actionsForEvent(timelineEvent: TimelineEvent): List<EventSharedAction> {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
index 64d8950420..3c98d24ccf 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
@@ -15,7 +15,15 @@
  */
 package im.vector.riotx.features.home.room.detail.timeline.edithistory
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
@@ -28,10 +36,11 @@ import im.vector.matrix.android.api.session.room.model.message.isReply
 import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
 import im.vector.riotx.core.date.VectorDateFormatter
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
 import timber.log.Timber
-import java.util.*
+import java.util.UUID
 
 data class ViewEditHistoryViewState(
         val eventId: String,
@@ -47,7 +56,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
                                                            initialState: ViewEditHistoryViewState,
                                                            val session: Session,
                                                            val dateFormatter: VectorDateFormatter
-) : VectorViewModel<ViewEditHistoryViewState, EmptyAction>(initialState) {
+) : VectorViewModel<ViewEditHistoryViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     private val roomId = initialState.roomId
     private val eventId = initialState.eventId
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
index d9bed98b1f..89e21e04a2 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
@@ -16,10 +16,13 @@
 
 package im.vector.riotx.features.home.room.detail.timeline.factory
 
+import android.view.View
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
-import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.riotx.R
+import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.features.home.AvatarRenderer
 import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
+import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
 import im.vector.riotx.features.home.room.detail.timeline.helper.MessageInformationDataFactory
 import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
 import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_
@@ -28,20 +31,26 @@ import javax.inject.Inject
 
 class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: AvatarSizeProvider,
                                              private val avatarRenderer: AvatarRenderer,
+                                             private val stringProvider: StringProvider,
                                              private val informationDataFactory: MessageInformationDataFactory) {
 
     fun create(text: String,
                informationData: MessageInformationData,
                highlight: Boolean,
                callback: TimelineEventController.Callback?): DefaultItem {
+        val attributes = DefaultItem.Attributes(
+                avatarRenderer = avatarRenderer,
+                informationData = informationData,
+                text = text,
+                itemLongClickListener = View.OnLongClickListener { view ->
+                    callback?.onEventLongClicked(informationData, null, view) ?: false
+                },
+                readReceiptsCallback = callback
+        )
         return DefaultItem_()
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .highlighted(highlight)
-                .text(text)
-                .avatarRenderer(avatarRenderer)
-                .informationData(informationData)
-                .baseCallback(callback)
-                .readReceiptsCallback(callback)
+                .attributes(attributes)
     }
 
     fun create(event: TimelineEvent,
@@ -49,9 +58,9 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
                callback: TimelineEventController.Callback?,
                throwable: Throwable? = null): DefaultItem {
         val text = if (throwable == null) {
-            "${event.root.getClearType()} events are not yet handled"
+            stringProvider.getString(R.string.rendering_event_error_type_of_event_not_handled, event.root.getClearType())
         } else {
-            "an exception occurred when rendering the event ${event.root.eventId}"
+            stringProvider.getString(R.string.rendering_event_error_exception, event.root.eventId)
         }
         val informationData = informationDataFactory.create(event, null)
         return create(text, informationData, highlight, callback)
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 9e05cdcc18..3febf19208 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -26,7 +26,16 @@ import android.view.View
 import dagger.Lazy
 import im.vector.matrix.android.api.session.events.model.RelationType
 import im.vector.matrix.android.api.session.events.model.toModel
-import im.vector.matrix.android.api.session.room.model.message.*
+import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
+import im.vector.matrix.android.api.session.room.model.message.MessageContent
+import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent
+import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
+import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent
+import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
+import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
+import im.vector.matrix.android.api.session.room.model.message.MessageType
+import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
+import im.vector.matrix.android.api.session.room.model.message.getFileUrl
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
 import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
@@ -40,8 +49,24 @@ import im.vector.riotx.core.utils.DimensionConverter
 import im.vector.riotx.core.utils.containsOnlyEmojis
 import im.vector.riotx.core.utils.isLocalFile
 import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
-import im.vector.riotx.features.home.room.detail.timeline.helper.*
-import im.vector.riotx.features.home.room.detail.timeline.item.*
+import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
+import im.vector.riotx.features.home.room.detail.timeline.helper.MessageInformationDataFactory
+import im.vector.riotx.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
+import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
+import im.vector.riotx.features.home.room.detail.timeline.item.AbsMessageItem
+import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem_
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem_
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem_
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem_
+import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem
+import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem_
 import im.vector.riotx.features.home.room.detail.timeline.tools.createLinkMovementMethod
 import im.vector.riotx.features.home.room.detail.timeline.tools.linkify
 import im.vector.riotx.features.html.CodeVisitor
@@ -153,7 +178,7 @@ class MessageItemFactory @Inject constructor(
                                            informationData: MessageInformationData,
                                            highlight: Boolean,
                                            callback: TimelineEventController.Callback?): DefaultItem? {
-        val text = "${messageContent.type} message events are not yet handled"
+        val text = stringProvider.getString(R.string.rendering_event_error_type_of_message_not_handled, messageContent.type)
         return defaultItemFactory.create(text, informationData, highlight, callback)
     }
 
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
index 563a970cfb..16275e6a73 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
@@ -19,9 +19,21 @@ package im.vector.riotx.features.home.room.detail.timeline.format
 import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.api.session.events.model.toModel
-import im.vector.matrix.android.api.session.room.model.*
+import im.vector.matrix.android.api.session.room.model.GuestAccess
+import im.vector.matrix.android.api.session.room.model.Membership
+import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
+import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
+import im.vector.matrix.android.api.session.room.model.RoomGuestAccessContent
+import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
+import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
+import im.vector.matrix.android.api.session.room.model.RoomJoinRules
+import im.vector.matrix.android.api.session.room.model.RoomJoinRulesContent
+import im.vector.matrix.android.api.session.room.model.RoomMemberContent
+import im.vector.matrix.android.api.session.room.model.RoomNameContent
+import im.vector.matrix.android.api.session.room.model.RoomTopicContent
 import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
+import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
 import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
 import im.vector.riotx.R
 import im.vector.riotx.core.di.ActiveSessionHolder
@@ -180,7 +192,11 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
 
     private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? {
         val content = event.content.toModel<EncryptionEventContent>() ?: return null
-        return sp.getString(R.string.notice_end_to_end, senderName, content.algorithm)
+        return if (content.algorithm == MXCRYPTO_ALGORITHM_MEGOLM) {
+            sp.getString(R.string.notice_end_to_end_ok, senderName)
+        } else {
+            sp.getString(R.string.notice_end_to_end_unknown_algorithm, senderName, content.algorithm)
+        }
     }
 
     private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
index 02b7341c72..f674cfa0f4 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
@@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.item
 import android.view.View
 import android.view.ViewStub
 import android.widget.RelativeLayout
+import androidx.annotation.CallSuper
 import androidx.annotation.IdRes
 import androidx.core.view.updateLayoutParams
 import com.airbnb.epoxy.EpoxyAttribute
@@ -42,6 +43,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
     @EpoxyAttribute
     lateinit var dimensionConverter: DimensionConverter
 
+    @CallSuper
     override fun bind(holder: H) {
         super.bind(holder)
         holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
index dc52293292..0ccc982c4c 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
@@ -17,6 +17,7 @@
 package im.vector.riotx.features.home.room.detail.timeline.item
 
 import android.view.View
+import android.widget.ImageView
 import android.widget.TextView
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
@@ -29,42 +30,39 @@ import im.vector.riotx.features.home.room.detail.timeline.TimelineEventControlle
 abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
 
     @EpoxyAttribute
-    lateinit var informationData: MessageInformationData
-    @EpoxyAttribute
-    lateinit var avatarRenderer: AvatarRenderer
-    @EpoxyAttribute
-    var baseCallback: TimelineEventController.BaseCallback? = null
-
-    private var longClickListener = View.OnLongClickListener {
-        return@OnLongClickListener baseCallback?.onEventLongClicked(informationData, null, it) == true
-    }
-
-    @EpoxyAttribute
-    var readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null
+    lateinit var attributes: Attributes
 
     private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
-        readReceiptsCallback?.onReadReceiptsClicked(informationData.readReceipts)
+        attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts)
     })
 
-    @EpoxyAttribute
-    var text: CharSequence? = null
-
     override fun bind(holder: Holder) {
-        holder.messageView.text = text
-        holder.view.setOnLongClickListener(longClickListener)
-        holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
+        super.bind(holder)
+        holder.messageTextView.text = attributes.text
+        attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
+        holder.view.setOnLongClickListener(attributes.itemLongClickListener)
+        holder.readReceiptsView.render(attributes.informationData.readReceipts, attributes.avatarRenderer, _readReceiptsClickListener)
     }
 
     override fun getEventIds(): List<String> {
-        return listOf(informationData.eventId)
+        return listOf(attributes.informationData.eventId)
     }
 
     override fun getViewType() = STUB_ID
 
     class Holder : BaseHolder(STUB_ID) {
-        val messageView by bind<TextView>(R.id.stateMessageView)
+        val avatarImageView by bind<ImageView>(R.id.itemDefaultAvatarView)
+        val messageTextView by bind<TextView>(R.id.itemDefaultTextView)
     }
 
+    data class Attributes(
+            val avatarRenderer: AvatarRenderer,
+            val informationData: MessageInformationData,
+            val text: CharSequence,
+            val itemLongClickListener: View.OnLongClickListener? = null,
+            val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null
+    )
+
     companion object {
         private const val STUB_ID = R.id.messageContentDefaultStub
     }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
index 761e80dd59..05cdbc0fd8 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt
@@ -16,7 +16,12 @@
 
 package im.vector.riotx.features.home.room.detail.timeline.reactions
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
@@ -25,6 +30,7 @@ import im.vector.matrix.rx.RxRoom
 import im.vector.matrix.rx.unwrap
 import im.vector.riotx.core.date.VectorDateFormatter
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
 import io.reactivex.Observable
@@ -54,7 +60,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
                                                          initialState: DisplayReactionsViewState,
                                                          private val session: Session,
                                                          private val dateFormatter: VectorDateFormatter
-) : VectorViewModel<DisplayReactionsViewState, EmptyAction>(initialState) {
+) : VectorViewModel<DisplayReactionsViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     private val roomId = initialState.roomId
     private val eventId = initialState.eventId
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/BreadcrumbsRoomComparator.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/BreadcrumbsRoomComparator.kt
new file mode 100644
index 0000000000..3b858b39fb
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/BreadcrumbsRoomComparator.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.home.room.list
+
+import im.vector.matrix.android.api.session.room.model.RoomSummary
+import javax.inject.Inject
+
+class BreadcrumbsRoomComparator @Inject constructor(
+        private val chronologicalRoomComparator: ChronologicalRoomComparator
+) : Comparator<RoomSummary> {
+
+    override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
+        val leftBreadcrumbsIndex = leftRoomSummary?.breadcrumbsIndex ?: RoomSummary.NOT_IN_BREADCRUMBS
+        val rightBreadcrumbsIndex = rightRoomSummary?.breadcrumbsIndex ?: RoomSummary.NOT_IN_BREADCRUMBS
+
+        return if (leftBreadcrumbsIndex == RoomSummary.NOT_IN_BREADCRUMBS) {
+            if (rightBreadcrumbsIndex == RoomSummary.NOT_IN_BREADCRUMBS) {
+                chronologicalRoomComparator.compare(leftRoomSummary, rightRoomSummary)
+            } else {
+                1
+            }
+        } else {
+            if (rightBreadcrumbsIndex == RoomSummary.NOT_IN_BREADCRUMBS) {
+                -1
+            } else {
+                leftBreadcrumbsIndex - rightBreadcrumbsIndex
+            }
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt
index 047a518974..618d6c901b 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt
@@ -22,26 +22,20 @@ import javax.inject.Inject
 class ChronologicalRoomComparator @Inject constructor() : Comparator<RoomSummary> {
 
     override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
-        var rightTimestamp = 0L
-        var leftTimestamp = 0L
-        if (null != leftRoomSummary) {
-            leftTimestamp = leftRoomSummary.latestPreviewableEvent?.root?.originServerTs ?: 0
-        }
-        if (null != rightRoomSummary) {
-            rightTimestamp = rightRoomSummary.latestPreviewableEvent?.root?.originServerTs ?: 0
-        }
-        return if (rightRoomSummary?.latestPreviewableEvent?.root == null) {
-            -1
-        } else if (leftRoomSummary?.latestPreviewableEvent?.root == null) {
-            1
-        } else {
-            val deltaTimestamp = rightTimestamp - leftTimestamp
-            if (deltaTimestamp > 0) {
-                1
-            } else if (deltaTimestamp < 0) {
-                -1
-            } else {
-                0
+        return when {
+            rightRoomSummary?.latestPreviewableEvent?.root == null -> -1
+            leftRoomSummary?.latestPreviewableEvent?.root == null  -> 1
+            else                                                   -> {
+                val rightTimestamp = rightRoomSummary.latestPreviewableEvent?.root?.originServerTs ?: 0
+                val leftTimestamp = leftRoomSummary.latestPreviewableEvent?.root?.originServerTs ?: 0
+
+                val deltaTimestamp = rightTimestamp - leftTimestamp
+
+                when {
+                    deltaTimestamp > 0 -> 1
+                    deltaTimestamp < 0 -> -1
+                    else               -> 0
+                }
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
index 4d9f5fb847..5a32f4b8b3 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
@@ -27,7 +27,11 @@ import androidx.core.view.isVisible
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.airbnb.epoxy.OnModelBuildFinishedListener
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Incomplete
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.args
+import com.airbnb.mvrx.fragmentViewModel
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.model.RoomSummary
@@ -35,6 +39,7 @@ import im.vector.matrix.android.api.session.room.notification.RoomNotificationSt
 import im.vector.riotx.R
 import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
 import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.platform.OnBackPressed
 import im.vector.riotx.core.platform.StateView
 import im.vector.riotx.core.platform.VectorBaseFragment
@@ -46,7 +51,6 @@ import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsShare
 import im.vector.riotx.features.home.room.list.widget.FabMenuView
 import im.vector.riotx.features.notifications.NotificationDrawerManager
 import im.vector.riotx.features.share.SharedData
-import io.reactivex.android.schedulers.AndroidSchedulers
 import kotlinx.android.parcel.Parcelize
 import kotlinx.android.synthetic.main.fragment_room_list.*
 import javax.inject.Inject
@@ -98,16 +102,13 @@ class RoomListFragment @Inject constructor(
         setupRecyclerView()
         sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
         roomListViewModel.subscribe { renderState(it) }
-        roomListViewModel.viewEvents
-                .observe()
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe {
-                    when (it) {
-                        is RoomListViewEvents.SelectRoom -> openSelectedRoom(it)
-                        is RoomListViewEvents.Failure    -> showErrorInSnackbar(it.throwable)
-                    }
-                }
-                .disposeOnDestroyView()
+        roomListViewModel.observeViewEvents {
+            when (it) {
+                is RoomListViewEvents.Loading    -> showLoading(it.message)
+                is RoomListViewEvents.Failure    -> showFailure(it.throwable)
+                is RoomListViewEvents.SelectRoom -> openSelectedRoom(it)
+            }.exhaustive
+        }
 
         createChatFabMenu.listener = this
 
@@ -117,6 +118,10 @@ class RoomListFragment @Inject constructor(
                 .disposeOnDestroyView()
     }
 
+    override fun showFailure(throwable: Throwable) {
+        showErrorInSnackbar(throwable)
+    }
+
     override fun onDestroyView() {
         roomController.removeModelBuildListener(modelBuildListener)
         modelBuildListener = null
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt
index 1181236da2..2e147293ec 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt
@@ -17,10 +17,14 @@
 
 package im.vector.riotx.features.home.room.list
 
+import im.vector.riotx.core.platform.VectorViewEvents
+
 /**
  * Transient events for RoomList
  */
-sealed class RoomListViewEvents {
+sealed class RoomListViewEvents : VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : RoomListViewEvents()
     data class Failure(val throwable: Throwable) : RoomListViewEvents()
+
     data class SelectRoom(val roomId: String) : RoomListViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt
index a9ea831723..397df50c2b 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt
@@ -26,7 +26,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.api.session.room.model.tag.RoomTag
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.utils.DataSource
-import im.vector.riotx.core.utils.PublishDataSource
+import im.vector.riotx.features.home.RoomListDisplayMode
 import io.reactivex.schedulers.Schedulers
 import timber.log.Timber
 import javax.inject.Inject
@@ -34,7 +34,7 @@ import javax.inject.Inject
 class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
                                             private val session: Session,
                                             private val roomSummariesSource: DataSource<List<RoomSummary>>)
-    : VectorViewModel<RoomListViewState, RoomListAction>(initialState) {
+    : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
 
     interface Factory {
         fun create(initialState: RoomListViewState): RoomListViewModel
@@ -52,9 +52,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
     private val displayMode = initialState.displayMode
     private val roomListDisplayModeFilter = RoomListDisplayModeFilter(displayMode)
 
-    private val _viewEvents = PublishDataSource<RoomListViewEvents>()
-    val viewEvents: DataSource<RoomListViewEvents> = _viewEvents
-
     init {
         observeRoomSummaries()
     }
@@ -197,6 +194,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
     }
 
     private fun handleLeaveRoom(action: RoomListAction.LeaveRoom) {
+        _viewEvents.post(RoomListViewEvents.Loading(null))
         session.getRoom(action.roomId)?.leave(null, object : MatrixCallback<Unit> {
             override fun onFailure(failure: Throwable) {
                 _viewEvents.post(RoomListViewEvents.Failure(failure))
@@ -205,35 +203,54 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
     }
 
     private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
-        // Set up init size on directChats and groupRooms as they are the biggest ones
-        val invites = ArrayList<RoomSummary>()
-        val favourites = ArrayList<RoomSummary>()
-        val directChats = ArrayList<RoomSummary>(rooms.size)
-        val groupRooms = ArrayList<RoomSummary>(rooms.size)
-        val lowPriorities = ArrayList<RoomSummary>()
-        val serverNotices = ArrayList<RoomSummary>()
+        if (displayMode == RoomListDisplayMode.SHARE) {
+            val recentRooms = ArrayList<RoomSummary>(20)
+            val otherRooms = ArrayList<RoomSummary>(rooms.size)
 
-        rooms
-                .filter { roomListDisplayModeFilter.test(it) }
-                .forEach { room ->
-                    val tags = room.tags.map { it.name }
-                    when {
-                        room.membership == Membership.INVITE          -> invites.add(room)
-                        tags.contains(RoomTag.ROOM_TAG_SERVER_NOTICE) -> serverNotices.add(room)
-                        tags.contains(RoomTag.ROOM_TAG_FAVOURITE)     -> favourites.add(room)
-                        tags.contains(RoomTag.ROOM_TAG_LOW_PRIORITY)  -> lowPriorities.add(room)
-                        room.isDirect                                 -> directChats.add(room)
-                        else                                          -> groupRooms.add(room)
+            rooms
+                    .filter { roomListDisplayModeFilter.test(it) }
+                    .forEach { room ->
+                        when (room.breadcrumbsIndex) {
+                            RoomSummary.NOT_IN_BREADCRUMBS -> otherRooms.add(room)
+                            else                           -> recentRooms.add(room)
+                        }
                     }
-                }
 
-        return RoomSummaries().apply {
-            put(RoomCategory.INVITE, invites)
-            put(RoomCategory.FAVOURITE, favourites)
-            put(RoomCategory.DIRECT, directChats)
-            put(RoomCategory.GROUP, groupRooms)
-            put(RoomCategory.LOW_PRIORITY, lowPriorities)
-            put(RoomCategory.SERVER_NOTICE, serverNotices)
+            return RoomSummaries().apply {
+                put(RoomCategory.RECENT_ROOMS, recentRooms)
+                put(RoomCategory.OTHER_ROOMS, otherRooms)
+            }
+        } else {
+            // Set up init size on directChats and groupRooms as they are the biggest ones
+            val invites = ArrayList<RoomSummary>()
+            val favourites = ArrayList<RoomSummary>()
+            val directChats = ArrayList<RoomSummary>(rooms.size)
+            val groupRooms = ArrayList<RoomSummary>(rooms.size)
+            val lowPriorities = ArrayList<RoomSummary>()
+            val serverNotices = ArrayList<RoomSummary>()
+
+            rooms
+                    .filter { roomListDisplayModeFilter.test(it) }
+                    .forEach { room ->
+                        val tags = room.tags.map { it.name }
+                        when {
+                            room.membership == Membership.INVITE          -> invites.add(room)
+                            tags.contains(RoomTag.ROOM_TAG_SERVER_NOTICE) -> serverNotices.add(room)
+                            tags.contains(RoomTag.ROOM_TAG_FAVOURITE)     -> favourites.add(room)
+                            tags.contains(RoomTag.ROOM_TAG_LOW_PRIORITY)  -> lowPriorities.add(room)
+                            room.isDirect                                 -> directChats.add(room)
+                            else                                          -> groupRooms.add(room)
+                        }
+                    }
+
+            return RoomSummaries().apply {
+                put(RoomCategory.INVITE, invites)
+                put(RoomCategory.FAVOURITE, favourites)
+                put(RoomCategory.DIRECT, directChats)
+                put(RoomCategory.GROUP, groupRooms)
+                put(RoomCategory.LOW_PRIORITY, lowPriorities)
+                put(RoomCategory.SERVER_NOTICE, serverNotices)
+            }
         }
     }
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt
index b41b4b9eeb..c127fa10e2 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt
@@ -43,7 +43,10 @@ data class RoomListViewState(
         val isDirectRoomsExpanded: Boolean = true,
         val isGroupRoomsExpanded: Boolean = true,
         val isLowPriorityRoomsExpanded: Boolean = true,
-        val isServerNoticeRoomsExpanded: Boolean = true
+        val isServerNoticeRoomsExpanded: Boolean = true,
+        // For sharing
+        val isRecentExpanded: Boolean = true,
+        val isOtherExpanded: Boolean = true
 ) : MvRxState {
 
     constructor(args: RoomListParams) : this(displayMode = args.displayMode)
@@ -56,6 +59,8 @@ data class RoomListViewState(
             RoomCategory.GROUP         -> isGroupRoomsExpanded
             RoomCategory.LOW_PRIORITY  -> isLowPriorityRoomsExpanded
             RoomCategory.SERVER_NOTICE -> isServerNoticeRoomsExpanded
+            RoomCategory.RECENT_ROOMS  -> isRecentExpanded
+            RoomCategory.OTHER_ROOMS   -> isOtherExpanded
         }
     }
 
@@ -67,6 +72,8 @@ data class RoomListViewState(
             RoomCategory.GROUP         -> copy(isGroupRoomsExpanded = !isGroupRoomsExpanded)
             RoomCategory.LOW_PRIORITY  -> copy(isLowPriorityRoomsExpanded = !isLowPriorityRoomsExpanded)
             RoomCategory.SERVER_NOTICE -> copy(isServerNoticeRoomsExpanded = !isServerNoticeRoomsExpanded)
+            RoomCategory.RECENT_ROOMS  -> copy(isRecentExpanded = !isRecentExpanded)
+            RoomCategory.OTHER_ROOMS   -> copy(isOtherExpanded = !isOtherExpanded)
         }
     }
 
@@ -86,7 +93,11 @@ enum class RoomCategory(@StringRes val titleRes: Int) {
     DIRECT(R.string.bottom_action_people_x),
     GROUP(R.string.bottom_action_rooms),
     LOW_PRIORITY(R.string.low_priority_header),
-    SERVER_NOTICE(R.string.system_alerts_header)
+    SERVER_NOTICE(R.string.system_alerts_header),
+
+    // For Sharing
+    RECENT_ROOMS(R.string.room_list_sharing_header_recent_rooms),
+    OTHER_ROOMS(R.string.room_list_sharing_header_other_rooms)
 }
 
 fun RoomSummaries?.isNullOrEmpty(): Boolean {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt
index 6ffe37cb15..c4afd442ab 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt
@@ -59,39 +59,9 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
     override fun buildModels() {
         val nonNullViewState = viewState ?: return
         when (nonNullViewState.displayMode) {
-            RoomListDisplayMode.FILTERED,
-            RoomListDisplayMode.SHARE -> {
-                buildFilteredRooms(nonNullViewState)
-            }
-            else                      -> {
-                var showHelp = false
-                val roomSummaries = nonNullViewState.asyncFilteredRooms()
-                roomSummaries?.forEach { (category, summaries) ->
-                    if (summaries.isEmpty()) {
-                        return@forEach
-                    } else {
-                        val isExpanded = nonNullViewState.isCategoryExpanded(category)
-                        buildRoomCategory(nonNullViewState, summaries, category.titleRes, nonNullViewState.isCategoryExpanded(category)) {
-                            listener?.onToggleRoomCategory(category)
-                        }
-                        if (isExpanded) {
-                            buildRoomModels(summaries,
-                                    nonNullViewState.joiningRoomsIds,
-                                    nonNullViewState.joiningErrorRoomsIds,
-                                    nonNullViewState.rejectingRoomsIds,
-                                    nonNullViewState.rejectingErrorRoomsIds)
-                            // Never set showHelp to true for invitation
-                            if (category != RoomCategory.INVITE) {
-                                showHelp = userPreferencesProvider.shouldShowLongClickOnRoomHelp()
-                            }
-                        }
-                    }
-                }
-
-                if (showHelp) {
-                    buildLongClickHelp()
-                }
-            }
+            RoomListDisplayMode.FILTERED -> buildFilteredRooms(nonNullViewState)
+            RoomListDisplayMode.SHARE    -> buildShareRooms(nonNullViewState)
+            else                         -> buildRooms(nonNullViewState)
         }
     }
 
@@ -109,9 +79,69 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
                 viewState.rejectingRoomsIds,
                 viewState.rejectingErrorRoomsIds)
 
-        when {
-            viewState.displayMode == RoomListDisplayMode.FILTERED -> addFilterFooter(viewState)
-            filteredSummaries.isEmpty()                           -> addEmptyFooter()
+        addFilterFooter(viewState)
+    }
+
+    private fun buildShareRooms(viewState: RoomListViewState) {
+        var hasResult = false
+        val roomSummaries = viewState.asyncFilteredRooms()
+
+        roomListNameFilter.filter = viewState.roomFilter
+
+        roomSummaries?.forEach { (category, summaries) ->
+            val filteredSummaries = summaries
+                    .filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
+
+            if (filteredSummaries.isEmpty()) {
+                return@forEach
+            } else {
+                hasResult = true
+                val isExpanded = viewState.isCategoryExpanded(category)
+                buildRoomCategory(viewState, emptyList(), category.titleRes, viewState.isCategoryExpanded(category)) {
+                    listener?.onToggleRoomCategory(category)
+                }
+                if (isExpanded) {
+                    buildRoomModels(filteredSummaries,
+                            emptySet(),
+                            emptySet(),
+                            emptySet(),
+                            emptySet()
+                    )
+                }
+            }
+        }
+        if (!hasResult) {
+            addNoResultItem()
+        }
+    }
+
+    private fun buildRooms(viewState: RoomListViewState) {
+        var showHelp = false
+        val roomSummaries = viewState.asyncFilteredRooms()
+        roomSummaries?.forEach { (category, summaries) ->
+            if (summaries.isEmpty()) {
+                return@forEach
+            } else {
+                val isExpanded = viewState.isCategoryExpanded(category)
+                buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
+                    listener?.onToggleRoomCategory(category)
+                }
+                if (isExpanded) {
+                    buildRoomModels(summaries,
+                            viewState.joiningRoomsIds,
+                            viewState.joiningErrorRoomsIds,
+                            viewState.rejectingRoomsIds,
+                            viewState.rejectingErrorRoomsIds)
+                    // Never set showHelp to true for invitation
+                    if (category != RoomCategory.INVITE) {
+                        showHelp = userPreferencesProvider.shouldShowLongClickOnRoomHelp()
+                    }
+                }
+            }
+        }
+
+        if (showHelp) {
+            buildLongClickHelp()
         }
     }
 
@@ -130,7 +160,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
         }
     }
 
-    private fun addEmptyFooter() {
+    private fun addNoResultItem() {
         noResultItem {
             id("no_result")
             text(stringProvider.getString(R.string.no_result_placeholder))
@@ -142,9 +172,6 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
                                   @StringRes titleRes: Int,
                                   isExpanded: Boolean,
                                   mutateExpandedState: () -> Unit) {
-        if (summaries.isEmpty()) {
-            return
-        }
         // TODO should add some business logic later
         val unreadCount = if (summaries.isEmpty()) {
             0
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
index 1c4d414f18..40f773b253 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
@@ -24,11 +24,12 @@ import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.rx.rx
 import im.vector.matrix.rx.unwrap
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 
 class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
                                                                 session: Session
-) : VectorViewModel<RoomListQuickActionsState, EmptyAction>(initialState) {
+) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt
index d7e37f762b..83263d05a2 100644
--- a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt
@@ -27,9 +27,9 @@ import com.airbnb.mvrx.withState
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.riotx.R
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.platform.OnBackPressed
 import im.vector.riotx.core.platform.VectorBaseFragment
-import io.reactivex.android.schedulers.AndroidSchedulers
 import javax.net.ssl.HttpsURLConnection
 
 /**
@@ -59,25 +59,21 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
 
         loginSharedActionViewModel = activityViewModelProvider.get(LoginSharedActionViewModel::class.java)
 
-        loginViewModel.viewEvents
-                .observe()
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe {
-                    handleLoginViewEvents(it)
-                }
-                .disposeOnDestroyView()
+        loginViewModel.observeViewEvents {
+            handleLoginViewEvents(it)
+        }
     }
 
     private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) {
         when (loginViewEvents) {
-            is LoginViewEvents.Error -> showError(loginViewEvents.throwable)
-            else                     ->
+            is LoginViewEvents.Failure -> showFailure(loginViewEvents.throwable)
+            else                       ->
                 // This is handled by the Activity
                 Unit
-        }
+        }.exhaustive
     }
 
-    private fun showError(throwable: Throwable) {
+    override fun showFailure(throwable: Throwable) {
         when (throwable) {
             is Failure.ServerError -> {
                 if (throwable.error.code == MatrixError.M_FORBIDDEN
@@ -96,11 +92,7 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
     }
 
     open fun onError(throwable: Throwable) {
-        AlertDialog.Builder(requireActivity())
-                .setTitle(R.string.dialog_title_error)
-                .setMessage(errorFormatter.toHumanReadable(throwable))
-                .setPositiveButton(R.string.ok, null)
-                .show()
+        super.showFailure(throwable)
     }
 
     override fun onBackPressed(toolbarButton: Boolean): Boolean {
diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt
index d879212c3d..908a9c6370 100644
--- a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt
@@ -209,7 +209,7 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
                         .setMessage(R.string.login_error_outdated_homeserver_content)
                         .setPositiveButton(R.string.ok, null)
                         .show()
-            is LoginViewEvents.Error                  ->
+            is LoginViewEvents.Failure                ->
                 // This is handled by the Fragments
                 Unit
         }
diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewEvents.kt
index 4c089174f4..25747df3d4 100644
--- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewEvents.kt
@@ -18,12 +18,15 @@
 package im.vector.riotx.features.login
 
 import im.vector.matrix.android.api.auth.registration.FlowResult
+import im.vector.riotx.core.platform.VectorViewEvents
 
 /**
  * Transient events for Login
  */
-sealed class LoginViewEvents {
+sealed class LoginViewEvents: VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : LoginViewEvents()
+    data class Failure(val throwable: Throwable) : LoginViewEvents()
+
     data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : LoginViewEvents()
-    data class Error(val throwable: Throwable) : LoginViewEvents()
     object OutdatedHomeserver : LoginViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt
index 8eb4652da5..b38b1d3ee2 100644
--- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt
@@ -41,8 +41,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
 import im.vector.riotx.core.di.ActiveSessionHolder
 import im.vector.riotx.core.extensions.configureAndStart
 import im.vector.riotx.core.platform.VectorViewModel
-import im.vector.riotx.core.utils.DataSource
-import im.vector.riotx.core.utils.PublishDataSource
 import im.vector.riotx.features.notifications.PushRuleTriggerListener
 import im.vector.riotx.features.session.SessionListener
 import im.vector.riotx.features.signout.soft.SoftLogoutActivity
@@ -59,7 +57,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
                                                  private val pushRuleTriggerListener: PushRuleTriggerListener,
                                                  private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
                                                  private val sessionListener: SessionListener)
-    : VectorViewModel<LoginViewState, LoginAction>(initialState) {
+    : VectorViewModel<LoginViewState, LoginAction, LoginViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -95,9 +93,6 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
 
     private var currentTask: Cancelable? = null
 
-    private val _viewEvents = PublishDataSource<LoginViewEvents>()
-    val viewEvents: DataSource<LoginViewEvents> = _viewEvents
-
     override fun handle(action: LoginAction) {
         when (action) {
             is LoginAction.UpdateServerType           -> handleUpdateServerType(action)
@@ -179,7 +174,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
 
         override fun onFailure(failure: Throwable) {
             if (failure !is CancellationException) {
-                _viewEvents.post(LoginViewEvents.Error(failure))
+                _viewEvents.post(LoginViewEvents.Failure(failure))
             }
             setState {
                 copy(
@@ -201,7 +196,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
             }
 
             override fun onFailure(failure: Throwable) {
-                _viewEvents.post(LoginViewEvents.Error(failure))
+                _viewEvents.post(LoginViewEvents.Failure(failure))
                 setState {
                     copy(
                             asyncRegistration = Uninitialized
@@ -223,7 +218,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
             }
 
             override fun onFailure(failure: Throwable) {
-                _viewEvents.post(LoginViewEvents.Error(failure))
+                _viewEvents.post(LoginViewEvents.Failure(failure))
                 setState {
                     copy(
                             asyncRegistration = Uninitialized
@@ -526,7 +521,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
 
         if (homeServerConnectionConfig == null) {
             // This is invalid
-            _viewEvents.post(LoginViewEvents.Error(Throwable("Unable to create a HomeServerConnectionConfig")))
+            _viewEvents.post(LoginViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
         } else {
             currentTask?.cancel()
             currentTask = null
@@ -540,7 +535,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
 
             currentTask = authenticationService.getLoginFlow(homeServerConnectionConfig, object : MatrixCallback<LoginFlowResult> {
                 override fun onFailure(failure: Throwable) {
-                    _viewEvents.post(LoginViewEvents.Error(failure))
+                    _viewEvents.post(LoginViewEvents.Failure(failure))
                     setState {
                         copy(
                                 asyncHomeServerLoginFlowRequest = Uninitialized
diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt
index 8aa03d9b22..9856693d22 100644
--- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt
@@ -21,6 +21,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.features.reactions.data.EmojiDataSource
 import im.vector.riotx.features.reactions.data.EmojiItem
@@ -33,7 +34,7 @@ data class EmojiSearchResultViewState(
 class EmojiSearchResultViewModel @AssistedInject constructor(
         @Assisted initialState: EmojiSearchResultViewState,
         private val dataSource: EmojiDataSource)
-    : VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction>(initialState) {
+    : VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt
index 1e625cff75..580844ddfa 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt
@@ -75,6 +75,7 @@ class PublicRoomsFragment @Inject constructor(
             sharedActionViewModel.post(RoomDirectorySharedAction.CreateRoom)
         }
 
+        // TODO remove this, replace by ViewEvents
         viewModel.joinRoomErrorLiveData.observeEvent(this) { throwable ->
             Snackbar.make(publicRoomsCoordinator, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT)
                     .show()
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt
index c4a91a520a..bab07795e9 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt
@@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.rx.rx
 import im.vector.riotx.core.extensions.postLiveEvent
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.utils.LiveEvent
 import timber.log.Timber
@@ -41,7 +42,7 @@ private const val PUBLIC_ROOMS_LIMIT = 20
 
 class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
                                                          private val session: Session)
-    : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction>(initialState) {
+    : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomAction.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomAction.kt
index 333834ca3c..8986db180a 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomAction.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomAction.kt
@@ -22,5 +22,6 @@ sealed class CreateRoomAction : VectorViewModelAction {
     data class SetName(val name: String) : CreateRoomAction()
     data class SetIsPublic(val isPublic: Boolean) : CreateRoomAction()
     data class SetIsInRoomDirectory(val isInRoomDirectory: Boolean) : CreateRoomAction()
+    data class SetIsEncrypted(val isEncrypted: Boolean) : CreateRoomAction()
     object Create : CreateRoomAction()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomController.kt
index 2477e6fab0..92e178c628 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomController.kt
@@ -39,9 +39,7 @@ class CreateRoomController @Inject constructor(private val stringProvider: Strin
     var index = 0
 
     override fun buildModels(viewState: CreateRoomViewState) {
-        val asyncCreateRoom = viewState.asyncCreateRoomRequest
-
-        when (asyncCreateRoom) {
+        when (val asyncCreateRoom = viewState.asyncCreateRoomRequest) {
             is Success       -> {
                 // Nothing to display, the screen will be closed
             }
@@ -101,12 +99,24 @@ class CreateRoomController @Inject constructor(private val stringProvider: Strin
                 listener?.setIsInRoomDirectory(value)
             }
         }
+        formSwitchItem {
+            id("encryption")
+            enabled(enableFormElement)
+            title(stringProvider.getString(R.string.create_room_encryption_title))
+            summary(stringProvider.getString(R.string.create_room_encryption_description))
+            switchChecked(viewState.isEncrypted)
+
+            listener { value ->
+                listener?.setIsEncrypted(value)
+            }
+        }
     }
 
     interface Listener {
         fun onNameChange(newName: String)
         fun setIsPublic(isPublic: Boolean)
         fun setIsInRoomDirectory(isInRoomDirectory: Boolean)
+        fun setIsEncrypted(isEncrypted: Boolean)
         fun retry()
     }
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt
index aacc21916a..827db96783 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt
@@ -85,6 +85,10 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
         viewModel.handle(CreateRoomAction.SetIsInRoomDirectory(isInRoomDirectory))
     }
 
+    override fun setIsEncrypted(isEncrypted: Boolean) {
+        viewModel.handle(CreateRoomAction.SetIsEncrypted(isEncrypted))
+    }
+
     override fun retry() {
         Timber.v("Retry")
         viewModel.handle(CreateRoomAction.Create)
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt
index ff4aa332f4..ee170de0e0 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt
@@ -17,7 +17,12 @@
 package im.vector.riotx.features.roomdirectory.createroom
 
 import androidx.fragment.app.FragmentActivity
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
@@ -25,12 +30,14 @@ import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
 import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
 import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
+import im.vector.riotx.core.platform.EmptyViewEvents
+import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
 
 class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
                                                       private val session: Session
-) : VectorViewModel<CreateRoomViewState, CreateRoomAction>(initialState) {
+) : VectorViewModel<CreateRoomViewState, CreateRoomAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -56,6 +63,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
             is CreateRoomAction.SetName              -> setName(action)
             is CreateRoomAction.SetIsPublic          -> setIsPublic(action)
             is CreateRoomAction.SetIsInRoomDirectory -> setIsInRoomDirectory(action)
+            is CreateRoomAction.SetIsEncrypted       -> setIsEncrypted(action)
             is CreateRoomAction.Create               -> doCreateRoom()
         }
     }
@@ -66,6 +74,8 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
 
     private fun setIsInRoomDirectory(action: CreateRoomAction.SetIsInRoomDirectory) = setState { copy(isInRoomDirectory = action.isInRoomDirectory) }
 
+    private fun setIsEncrypted(action: CreateRoomAction.SetIsEncrypted) = setState { copy(isEncrypted = action.isEncrypted) }
+
     private fun doCreateRoom() = withState { state ->
         if (state.asyncCreateRoomRequest is Loading || state.asyncCreateRoomRequest is Success) {
             return@withState
@@ -83,6 +93,11 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
 
             // Public room
             preset = if (state.isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT
+
+            // Encryption
+            if (state.isEncrypted) {
+                enableEncryptionWithAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM)
+            }
         }
 
         session.createRoom(createRoomParams, object : MatrixCallback<String> {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewState.kt
index 363d31edc6..810319d54f 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewState.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewState.kt
@@ -24,5 +24,6 @@ data class CreateRoomViewState(
         val roomName: String = "",
         val isPublic: Boolean = false,
         val isInRoomDirectory: Boolean = false,
+        val isEncrypted: Boolean = false,
         val asyncCreateRoomRequest: Async<String> = Uninitialized
 ) : MvRxState
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
index b7bdaea495..54f93b5404 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
@@ -16,17 +16,22 @@
 
 package im.vector.riotx.features.roomdirectory.picker
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 
 class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState,
                                                                private val session: Session)
-    : VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerAction>(initialState) {
+    : VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
index 3de5cb4334..b4aae0d116 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
@@ -26,13 +26,14 @@ import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
 import im.vector.matrix.rx.rx
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.features.roomdirectory.JoinState
 import timber.log.Timber
 
 class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
                                                        private val session: Session)
-    : VectorViewModel<RoomPreviewViewState, RoomPreviewAction>(initialState) {
+    : VectorViewModel<RoomPreviewViewState, RoomPreviewAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt
index 23db6b53a8..3d1455042c 100644
--- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt
@@ -20,13 +20,19 @@ package im.vector.riotx.features.roommemberprofile
 import android.os.Bundle
 import android.os.Parcelable
 import android.view.View
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Incomplete
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.args
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
 import im.vector.matrix.android.api.util.MatrixItem
 import im.vector.riotx.R
 import im.vector.riotx.core.animations.AppBarStateChangeListener
 import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener
 import im.vector.riotx.core.extensions.cleanup
 import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.extensions.setTextOrHide
 import im.vector.riotx.core.platform.StateView
 import im.vector.riotx.core.platform.VectorBaseFragment
@@ -73,16 +79,13 @@ class RoomMemberProfileFragment @Inject constructor(
         appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView,
                 matrixProfileToolbarTitleView))
         matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
-        viewModel.viewEvents
-                .observe()
-                .subscribe {
-                    dismissLoadingDialog()
-                    when (it) {
-                        is RoomMemberProfileViewEvents.Loading -> showLoadingDialog(it.message)
-                        is RoomMemberProfileViewEvents.Failure -> showErrorInSnackbar(it.throwable)
-                    }
-                }
-                .disposeOnDestroyView()
+        viewModel.observeViewEvents {
+            when (it) {
+                is RoomMemberProfileViewEvents.Loading               -> showLoading(it.message)
+                is RoomMemberProfileViewEvents.Failure               -> showFailure(it.throwable)
+                is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit
+            }.exhaustive
+        }
     }
 
     override fun onDestroyView() {
diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt
index 093e54989b..4647c124a6 100644
--- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt
@@ -16,11 +16,14 @@
 
 package im.vector.riotx.features.roommemberprofile
 
+import im.vector.riotx.core.platform.VectorViewEvents
+
 /**
  * Transient events for RoomMemberProfile
  */
-sealed class RoomMemberProfileViewEvents {
-    data class Loading(val message: CharSequence) : RoomMemberProfileViewEvents()
-    object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
+sealed class RoomMemberProfileViewEvents : VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : RoomMemberProfileViewEvents()
     data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents()
+
+    object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
index e4a15724b4..ab28f6079c 100644
--- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
@@ -44,8 +44,6 @@ import im.vector.matrix.rx.unwrap
 import im.vector.riotx.R
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
-import im.vector.riotx.core.utils.DataSource
-import im.vector.riotx.core.utils.PublishDataSource
 import io.reactivex.Observable
 import io.reactivex.functions.BiFunction
 import kotlinx.coroutines.Dispatchers
@@ -55,7 +53,7 @@ import kotlinx.coroutines.withContext
 class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState,
                                                              private val stringProvider: StringProvider,
                                                              private val session: Session)
-    : VectorViewModel<RoomMemberProfileViewState, RoomMemberProfileAction>(initialState) {
+    : VectorViewModel<RoomMemberProfileViewState, RoomMemberProfileAction, RoomMemberProfileViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -71,9 +69,6 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
         }
     }
 
-    private val _viewEvents = PublishDataSource<RoomMemberProfileViewEvents>()
-    val viewEvents: DataSource<RoomMemberProfileViewEvents> = _viewEvents
-
     private val room = if (initialState.roomId != null) {
         session.getRoom(initialState.roomId)
     } else {
@@ -183,7 +178,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
 
     private fun handleIgnoreAction() = withState { state ->
         val isIgnored = state.isIgnored() ?: return@withState
-        _viewEvents.post(RoomMemberProfileViewEvents.Loading(stringProvider.getString(R.string.please_wait)))
+        _viewEvents.post(RoomMemberProfileViewEvents.Loading())
         val ignoreActionCallback = object : MatrixCallback<Unit> {
             override fun onSuccess(data: Unit) {
                 _viewEvents.post(RoomMemberProfileViewEvents.OnIgnoreActionSuccess)
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt
index 160d2341ed..1a9b268b90 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt
@@ -26,6 +26,7 @@ import im.vector.riotx.core.extensions.addFragmentToBackstack
 import im.vector.riotx.core.platform.ToolbarConfigurable
 import im.vector.riotx.core.platform.VectorBaseActivity
 import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
+import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
 
 class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
 
@@ -69,7 +70,7 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
     }
 
     private fun openRoomSettings() {
-        notImplemented("Open room settings")
+        addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs)
     }
 
     private fun openRoomMembers() {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt
index da161efd6a..4e86738271 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt
@@ -31,6 +31,7 @@ import im.vector.riotx.core.animations.AppBarStateChangeListener
 import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener
 import im.vector.riotx.core.extensions.cleanup
 import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.extensions.setTextOrHide
 import im.vector.riotx.core.platform.VectorBaseFragment
 import im.vector.riotx.features.home.AvatarRenderer
@@ -77,17 +78,13 @@ class RoomProfileFragment @Inject constructor(
         appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView,
                 matrixProfileToolbarTitleView))
         matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
-        roomProfileViewModel.viewEvents
-                .observe()
-                .subscribe {
-                    dismissLoadingDialog()
-                    when (it) {
-                        is RoomProfileViewEvents.Loading         -> showLoadingDialog(it.message)
-                        RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom()
-                        is RoomProfileViewEvents.Failure         -> showError(it.throwable)
-                    }
-                }
-                .disposeOnDestroyView()
+        roomProfileViewModel.observeViewEvents {
+            when (it) {
+                is RoomProfileViewEvents.Loading            -> showLoading(it.message)
+                is RoomProfileViewEvents.Failure            -> showFailure(it.throwable)
+                is RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom()
+            }.exhaustive
+        }
         roomListQuickActionsSharedActionViewModel
                 .observe()
                 .subscribe { handleQuickActions(it) }
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt
index 3834247b52..50b3c136e5 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt
@@ -15,11 +15,15 @@
  */
 
 package im.vector.riotx.features.roomprofile
+
+import im.vector.riotx.core.platform.VectorViewEvents
+
 /**
  * Transient events for RoomProfile
  */
-sealed class RoomProfileViewEvents {
-    data class Loading(val message: CharSequence): RoomProfileViewEvents()
-    object OnLeaveRoomSuccess: RoomProfileViewEvents()
+sealed class RoomProfileViewEvents : VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
     data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
+
+    object OnLeaveRoomSuccess : RoomProfileViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt
index 8134c93b4f..6c66ac67b2 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt
@@ -29,13 +29,11 @@ import im.vector.matrix.rx.unwrap
 import im.vector.riotx.R
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.resources.StringProvider
-import im.vector.riotx.core.utils.DataSource
-import im.vector.riotx.core.utils.PublishDataSource
 
 class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState,
                                                        private val stringProvider: StringProvider,
                                                        private val session: Session)
-    : VectorViewModel<RoomProfileViewState, RoomProfileAction>(initialState) {
+    : VectorViewModel<RoomProfileViewState, RoomProfileAction, RoomProfileViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -51,9 +49,6 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R
         }
     }
 
-    private val _viewEvents = PublishDataSource<RoomProfileViewEvents>()
-    val viewEvents: DataSource<RoomProfileViewEvents> = _viewEvents
-
     private val room = session.getRoom(initialState.roomId)!!
 
     init {
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt
index 4919fa39e3..e6e54d6771 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt
@@ -29,7 +29,7 @@ import im.vector.riotx.core.extensions.configureWith
 import im.vector.riotx.core.platform.VectorBaseFragment
 import im.vector.riotx.features.home.AvatarRenderer
 import im.vector.riotx.features.roomprofile.RoomProfileArgs
-import kotlinx.android.synthetic.main.fragment_room_member_list.*
+import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
 import javax.inject.Inject
 
 class RoomMemberListFragment @Inject constructor(
@@ -41,12 +41,12 @@ class RoomMemberListFragment @Inject constructor(
     private val viewModel: RoomMemberListViewModel by fragmentViewModel()
     private val roomProfileArgs: RoomProfileArgs by args()
 
-    override fun getLayoutResId() = R.layout.fragment_room_member_list
+    override fun getLayoutResId() = R.layout.fragment_room_setting_generic
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         roomMemberListController.callback = this
-        setupToolbar(roomMemberListToolbar)
+        setupToolbar(roomSettingsToolbar)
         recyclerView.configureWith(roomMemberListController, hasFixedSize = true)
     }
 
@@ -66,8 +66,8 @@ class RoomMemberListFragment @Inject constructor(
 
     private fun renderRoomSummary(state: RoomMemberListViewState) {
         state.roomSummary()?.let {
-            roomMemberListToolbarTitleView.text = it.displayName
-            avatarRenderer.render(it.toMatrixItem(), roomMemberListToolbarAvatarImageView)
+            roomSettingsToolbarTitleView.text = it.displayName
+            avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
         }
     }
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
index c27afaceb0..ad06838fbd 100644
--- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
@@ -34,13 +34,15 @@ import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
 import im.vector.matrix.rx.mapOptional
 import im.vector.matrix.rx.rx
 import im.vector.matrix.rx.unwrap
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 import io.reactivex.Observable
 import io.reactivex.functions.BiFunction
 
 class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState,
+                                                          private val roomMemberSummaryComparator: RoomMemberSummaryComparator,
                                                           private val session: Session)
-    : VectorViewModel<RoomMemberListViewState, RoomMemberListAction>(initialState) {
+    : VectorViewModel<RoomMemberListViewState, RoomMemberListAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -112,11 +114,11 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
                 }
 
         return listOf(
-                PowerLevelCategory.ADMIN to admins,
-                PowerLevelCategory.MODERATOR to moderators,
-                PowerLevelCategory.CUSTOM to customs,
-                PowerLevelCategory.INVITE to invites,
-                PowerLevelCategory.USER to users
+                PowerLevelCategory.ADMIN to admins.sortedWith(roomMemberSummaryComparator),
+                PowerLevelCategory.MODERATOR to moderators.sortedWith(roomMemberSummaryComparator),
+                PowerLevelCategory.CUSTOM to customs.sortedWith(roomMemberSummaryComparator),
+                PowerLevelCategory.INVITE to invites.sortedWith(roomMemberSummaryComparator),
+                PowerLevelCategory.USER to users.sortedWith(roomMemberSummaryComparator)
         )
     }
 
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberSummaryComparator.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberSummaryComparator.kt
new file mode 100644
index 0000000000..cc1dd29d13
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberSummaryComparator.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.members
+
+import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
+import javax.inject.Inject
+
+class RoomMemberSummaryComparator @Inject constructor() : Comparator<RoomMemberSummary> {
+
+    override fun compare(leftRoomMemberSummary: RoomMemberSummary?, rightRoomMemberSummary: RoomMemberSummary?): Int {
+        return when (leftRoomMemberSummary) {
+            null ->
+                when (rightRoomMemberSummary) {
+                    null -> 0
+                    else -> 1
+                }
+            else ->
+                when (rightRoomMemberSummary) {
+                    null -> -1
+                    else ->
+                        when {
+                            leftRoomMemberSummary.displayName.isNullOrBlank() ->
+                                when {
+                                    rightRoomMemberSummary.displayName.isNullOrBlank() -> {
+                                        // No display names, compare ids
+                                        leftRoomMemberSummary.userId.compareTo(rightRoomMemberSummary.userId)
+                                    }
+                                    else                                               -> 1
+                                }
+                            else                                              ->
+                                when {
+                                    rightRoomMemberSummary.displayName.isNullOrBlank() -> -1
+                                    else                                               -> {
+                                        when (leftRoomMemberSummary.displayName) {
+                                            rightRoomMemberSummary.displayName ->
+                                                // Same display name, compare id
+                                                leftRoomMemberSummary.userId.compareTo(rightRoomMemberSummary.userId)
+                                            else                               ->
+                                                leftRoomMemberSummary.displayName!!.compareTo(rightRoomMemberSummary.displayName!!, true)
+                                        }
+                                    }
+                                }
+                        }
+                }
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsAction.kt
new file mode 100644
index 0000000000..3c1b10cf8e
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsAction.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import im.vector.riotx.core.platform.VectorViewModelAction
+
+sealed class RoomSettingsAction : VectorViewModelAction {
+    data class SetRoomName(val newName: String) : RoomSettingsAction()
+    data class SetRoomTopic(val newTopic: String) : RoomSettingsAction()
+    data class SetRoomAvatar(val newAvatarUrl: String) : RoomSettingsAction()
+    object EnableEncryption : RoomSettingsAction()
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsController.kt
new file mode 100644
index 0000000000..fdd127e68b
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsController.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import com.airbnb.epoxy.TypedEpoxyController
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.profiles.buildProfileAction
+import im.vector.riotx.core.epoxy.profiles.buildProfileSection
+import im.vector.riotx.core.resources.ColorProvider
+import im.vector.riotx.core.resources.StringProvider
+import javax.inject.Inject
+
+// TODO Add other feature here (waiting for design)
+class RoomSettingsController @Inject constructor(
+        private val stringProvider: StringProvider,
+        colorProvider: ColorProvider
+) : TypedEpoxyController<RoomSettingsViewState>() {
+
+    interface Callback {
+        fun onEnableEncryptionClicked()
+    }
+
+    private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
+
+    var callback: Callback? = null
+
+    init {
+        setData(null)
+    }
+
+    override fun buildModels(data: RoomSettingsViewState?) {
+        val roomSummary = data?.roomSummary?.invoke() ?: return
+
+        buildProfileSection(
+                stringProvider.getString(R.string.settings)
+        )
+
+        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() }
+            )
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsFragment.kt
new file mode 100644
index 0000000000..d2e06f30e7
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsFragment.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.args
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.matrix.android.api.util.toMatrixItem
+import im.vector.riotx.R
+import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.extensions.exhaustive
+import im.vector.riotx.core.platform.VectorBaseFragment
+import im.vector.riotx.features.home.AvatarRenderer
+import im.vector.riotx.features.roomprofile.RoomProfileArgs
+import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
+import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
+import javax.inject.Inject
+
+class RoomSettingsFragment @Inject constructor(
+        val viewModelFactory: RoomSettingsViewModel.Factory,
+        private val controller: RoomSettingsController,
+        private val avatarRenderer: AvatarRenderer
+) : VectorBaseFragment(), RoomSettingsController.Callback {
+
+    private val viewModel: RoomSettingsViewModel by fragmentViewModel()
+    private val roomProfileArgs: RoomProfileArgs by args()
+
+    override fun getLayoutResId() = R.layout.fragment_room_setting_generic
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        controller.callback = this
+        setupToolbar(roomSettingsToolbar)
+        recyclerView.configureWith(controller, hasFixedSize = true)
+        waiting_view_status_text.setText(R.string.please_wait)
+        waiting_view_status_text.isVisible = true
+
+        viewModel.observeViewEvents {
+            when (it) {
+                is RoomSettingsViewEvents.Failure -> showFailure(it.throwable)
+            }.exhaustive
+        }
+    }
+
+    override fun onDestroyView() {
+        recyclerView.cleanup()
+        super.onDestroyView()
+    }
+
+    override fun invalidate() = withState(viewModel) { viewState ->
+        controller.setData(viewState)
+        renderRoomSummary(viewState)
+    }
+
+    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()
+    }
+
+    private fun renderRoomSummary(state: RoomSettingsViewState) {
+        waiting_view.isVisible = state.isLoading
+
+        state.roomSummary()?.let {
+            roomSettingsToolbarTitleView.text = it.displayName
+            avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewEvents.kt
new file mode 100644
index 0000000000..4856a935a8
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewEvents.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import im.vector.riotx.core.platform.VectorViewEvents
+
+/**
+ * Transient events for room settings screen
+ */
+sealed class RoomSettingsViewEvents : VectorViewEvents {
+    data class Failure(val throwable: Throwable) : RoomSettingsViewEvents()
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewModel.kt
new file mode 100644
index 0000000000..ee6413b6a4
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewModel.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
+import im.vector.matrix.rx.rx
+import im.vector.matrix.rx.unwrap
+import im.vector.riotx.core.platform.VectorViewModel
+
+class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState,
+                                                        private val session: Session)
+    : VectorViewModel<RoomSettingsViewState, RoomSettingsAction, RoomSettingsViewEvents>(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel
+    }
+
+    companion object : MvRxViewModelFactory<RoomSettingsViewModel, RoomSettingsViewState> {
+
+        @JvmStatic
+        override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? {
+            val fragment: RoomSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.viewModelFactory.create(state)
+        }
+    }
+
+    private val room = session.getRoom(initialState.roomId)!!
+
+    init {
+        observeRoomSummary()
+    }
+
+    private fun observeRoomSummary() {
+        room.rx().liveRoomSummary()
+                .unwrap()
+                .execute { async ->
+                    copy(roomSummary = async)
+                }
+    }
+
+    override fun handle(action: RoomSettingsAction) {
+        when (action) {
+            is RoomSettingsAction.EnableEncryption -> handleEnableEncryption()
+        }
+    }
+
+    private fun handleEnableEncryption() {
+        setState {
+            copy(isLoading = true)
+        }
+
+        room.enableEncryption(MXCRYPTO_ALGORITHM_MEGOLM, object : MatrixCallback<Unit> {
+            override fun onFailure(failure: Throwable) {
+                setState {
+                    copy(isLoading = false)
+                }
+
+                _viewEvents.post(RoomSettingsViewEvents.Failure(failure))
+            }
+
+            override fun onSuccess(data: Unit) {
+                setState {
+                    copy(isLoading = false)
+                }
+            }
+        })
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewState.kt
new file mode 100644
index 0000000000..d68ed6853e
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/settings/RoomSettingsViewState.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.roomprofile.settings
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.Uninitialized
+import im.vector.matrix.android.api.session.room.model.RoomSummary
+import im.vector.riotx.features.roomprofile.RoomProfileArgs
+
+data class RoomSettingsViewState(
+        val roomId: String,
+        val roomSummary: Async<RoomSummary> = Uninitialized,
+        val isLoading: Boolean = false
+) : MvRxState {
+
+    constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesAction.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesAction.kt
new file mode 100644
index 0000000000..550703039f
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesAction.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.settings.devices
+
+import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
+import im.vector.riotx.core.platform.VectorViewModelAction
+
+sealed class DevicesAction : VectorViewModelAction {
+    object Retry : DevicesAction()
+    data class Delete(val deviceInfo: DeviceInfo) : DevicesAction()
+    data class Password(val password: String) : DevicesAction()
+    data class Rename(val deviceInfo: DeviceInfo, val newName: String) : DevicesAction()
+    data class ToggleDevice(val deviceInfo: DeviceInfo) : DevicesAction()
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewEvents.kt
new file mode 100644
index 0000000000..b42738439a
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewEvents.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.settings.devices
+
+import im.vector.riotx.core.platform.VectorViewEvents
+
+/**
+ * Transient events for Ignored users screen
+ */
+sealed class DevicesViewEvents : VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : DevicesViewEvents()
+    data class Failure(val throwable: Throwable) : DevicesViewEvents()
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
index b2b015a3f0..0324036347 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
@@ -18,7 +18,15 @@ package im.vector.riotx.features.settings.devices
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
@@ -29,7 +37,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
 import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
 import im.vector.riotx.core.extensions.postLiveEvent
 import im.vector.riotx.core.platform.VectorViewModel
-import im.vector.riotx.core.platform.VectorViewModelAction
 import im.vector.riotx.core.utils.LiveEvent
 import timber.log.Timber
 
@@ -37,20 +44,13 @@ data class DevicesViewState(
         val myDeviceId: String = "",
         val devices: Async<List<DeviceInfo>> = Uninitialized,
         val currentExpandedDeviceId: String? = null,
+        // TODO Replace by isLoading boolean
         val request: Async<Unit> = Uninitialized
 ) : MvRxState
 
-sealed class DevicesAction : VectorViewModelAction {
-    object Retry : DevicesAction()
-    data class Delete(val deviceInfo: DeviceInfo) : DevicesAction()
-    data class Password(val password: String) : DevicesAction()
-    data class Rename(val deviceInfo: DeviceInfo, val newName: String) : DevicesAction()
-    data class ToggleDevice(val deviceInfo: DeviceInfo) : DevicesAction()
-}
-
 class DevicesViewModel @AssistedInject constructor(@Assisted initialState: DevicesViewState,
                                                    private val session: Session)
-    : VectorViewModel<DevicesViewState, DevicesAction>(initialState) {
+    : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -153,7 +153,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                     )
                 }
 
-                _requestErrorLiveData.postLiveEvent(failure)
+                _viewEvents.post(DevicesViewEvents.Failure(failure))
             }
         })
     }
@@ -207,7 +207,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                         )
                     }
 
-                    _requestErrorLiveData.postLiveEvent(failure)
+                    _viewEvents.post(DevicesViewEvents.Failure(failure))
                 }
             }
 
@@ -261,7 +261,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                     )
                 }
 
-                _requestErrorLiveData.postLiveEvent(failure)
+                _viewEvents.post(DevicesViewEvents.Failure(failure))
             }
         })
     }
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt
index 465b3ba0fb..f24fb2f542 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt
@@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.cleanup
 import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.extensions.observeEvent
 import im.vector.riotx.core.platform.VectorBaseActivity
 import im.vector.riotx.core.platform.VectorBaseFragment
@@ -52,7 +53,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
 
     override fun getLayoutResId() = R.layout.fragment_generic_recycler
 
-    private val devicesViewModel: DevicesViewModel by fragmentViewModel()
+    private val viewModel: DevicesViewModel by fragmentViewModel()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
@@ -61,16 +62,24 @@ class VectorSettingsDevicesFragment @Inject constructor(
         waiting_view_status_text.isVisible = true
         devicesController.callback = this
         recyclerView.configureWith(devicesController, showDivider = true)
-        devicesViewModel.requestErrorLiveData.observeEvent(this) {
-            displayErrorDialog(it)
-            // Password is maybe not good, for safety measure, reset it here
-            mAccountPassword = ""
+        viewModel.observeViewEvents {
+            when (it) {
+                is DevicesViewEvents.Loading -> showLoading(it.message)
+                is DevicesViewEvents.Failure -> showFailure(it.throwable)
+            }.exhaustive
         }
-        devicesViewModel.requestPasswordLiveData.observeEvent(this) {
+        viewModel.requestPasswordLiveData.observeEvent(this) {
             maybeShowDeleteDeviceWithPasswordDialog()
         }
     }
 
+    override fun showFailure(throwable: Throwable) {
+        super.showFailure(throwable)
+
+        // Password is maybe not good, for safety measure, reset it here
+        mAccountPassword = ""
+    }
+
     override fun onDestroyView() {
         devicesController.callback = null
         recyclerView.cleanup()
@@ -83,20 +92,12 @@ class VectorSettingsDevicesFragment @Inject constructor(
         (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_devices_list)
     }
 
-    private fun displayErrorDialog(throwable: Throwable) {
-        AlertDialog.Builder(requireActivity())
-                .setTitle(R.string.dialog_title_error)
-                .setMessage(errorFormatter.toHumanReadable(throwable))
-                .setPositiveButton(R.string.ok, null)
-                .show()
-    }
-
     override fun onDeviceClicked(deviceInfo: DeviceInfo) {
-        devicesViewModel.handle(DevicesAction.ToggleDevice(deviceInfo))
+        viewModel.handle(DevicesAction.ToggleDevice(deviceInfo))
     }
 
     override fun onDeleteDevice(deviceInfo: DeviceInfo) {
-        devicesViewModel.handle(DevicesAction.Delete(deviceInfo))
+        viewModel.handle(DevicesAction.Delete(deviceInfo))
     }
 
     override fun onRenameDevice(deviceInfo: DeviceInfo) {
@@ -104,7 +105,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
     }
 
     override fun retry() {
-        devicesViewModel.handle(DevicesAction.Retry)
+        viewModel.handle(DevicesAction.Retry)
     }
 
     /**
@@ -125,7 +126,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
                 .setPositiveButton(R.string.ok) { _, _ ->
                     val newName = input.text.toString()
 
-                    devicesViewModel.handle(DevicesAction.Rename(deviceInfo, newName))
+                    viewModel.handle(DevicesAction.Rename(deviceInfo, newName))
                 }
                 .setNegativeButton(R.string.cancel, null)
                 .show()
@@ -136,7 +137,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
      */
     private fun maybeShowDeleteDeviceWithPasswordDialog() {
         if (mAccountPassword.isNotEmpty()) {
-            devicesViewModel.handle(DevicesAction.Password(mAccountPassword))
+            viewModel.handle(DevicesAction.Password(mAccountPassword))
         } else {
             val inflater = requireActivity().layoutInflater
             val layout = inflater.inflate(R.layout.dialog_device_delete, null)
@@ -152,7 +153,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
                             return@OnClickListener
                         }
                         mAccountPassword = passwordEditText.text.toString()
-                        devicesViewModel.handle(DevicesAction.Password(mAccountPassword))
+                        viewModel.handle(DevicesAction.Password(mAccountPassword))
                     })
                     .setNegativeButton(R.string.cancel, null)
                     .setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
@@ -166,7 +167,7 @@ class VectorSettingsDevicesFragment @Inject constructor(
         }
     }
 
-    override fun invalidate() = withState(devicesViewModel) { state ->
+    override fun invalidate() = withState(viewModel) { state ->
         devicesController.update(state)
 
         handleRequestStatus(state.request)
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewEvents.kt
new file mode 100644
index 0000000000..3b98159d1a
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewEvents.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.settings.ignored
+
+import im.vector.riotx.core.platform.VectorViewEvents
+
+/**
+ * Transient events for Ignored users screen
+ */
+sealed class IgnoredUsersViewEvents : VectorViewEvents {
+    data class Loading(val message: CharSequence? = null) : IgnoredUsersViewEvents()
+    data class Failure(val throwable: Throwable) : IgnoredUsersViewEvents()
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt
index e77660746c..e9c02618b0 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt
@@ -16,14 +16,21 @@
 
 package im.vector.riotx.features.settings.ignored
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.matrix.rx.rx
-import im.vector.riotx.core.extensions.postLiveEvent
 import im.vector.riotx.core.platform.VectorViewModel
 import im.vector.riotx.core.platform.VectorViewModelAction
 
@@ -38,7 +45,7 @@ sealed class IgnoredUsersAction : VectorViewModelAction {
 
 class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
                                                         private val session: Session)
-    : VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction>(initialState) {
+    : VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction, IgnoredUsersViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -89,7 +96,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState:
                     )
                 }
 
-                _requestErrorLiveData.postLiveEvent(failure)
+                _viewEvents.post(IgnoredUsersViewEvents.Failure(failure))
             }
 
             override fun onSuccess(data: Unit) {
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt
index 6435f43d87..c211d14edc 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt
@@ -27,7 +27,7 @@ import com.airbnb.mvrx.withState
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.cleanup
 import im.vector.riotx.core.extensions.configureWith
-import im.vector.riotx.core.extensions.observeEvent
+import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.platform.VectorBaseActivity
 import im.vector.riotx.core.platform.VectorBaseFragment
 import kotlinx.android.synthetic.main.fragment_generic_recycler.*
@@ -41,7 +41,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
 
     override fun getLayoutResId() = R.layout.fragment_generic_recycler
 
-    private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel()
+    private val viewModel: IgnoredUsersViewModel by fragmentViewModel()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
@@ -50,8 +50,11 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
         waiting_view_status_text.isVisible = true
         ignoredUsersController.callback = this
         recyclerView.configureWith(ignoredUsersController)
-        ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) {
-            displayErrorDialog(it)
+        viewModel.observeViewEvents {
+            when (it) {
+                is IgnoredUsersViewEvents.Loading -> showLoading(it.message)
+                is IgnoredUsersViewEvents.Failure -> showFailure(it.throwable)
+            }.exhaustive
         }
     }
 
@@ -71,25 +74,17 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
         AlertDialog.Builder(requireActivity())
                 .setMessage(getString(R.string.settings_unignore_user, userId))
                 .setPositiveButton(R.string.yes) { _, _ ->
-                    ignoredUsersViewModel.handle(IgnoredUsersAction.UnIgnore(userId))
+                    viewModel.handle(IgnoredUsersAction.UnIgnore(userId))
                 }
                 .setNegativeButton(R.string.no, null)
                 .show()
     }
 
-    private fun displayErrorDialog(throwable: Throwable) {
-        AlertDialog.Builder(requireActivity())
-                .setTitle(R.string.dialog_title_error)
-                .setMessage(errorFormatter.toHumanReadable(throwable))
-                .setPositiveButton(R.string.ok, null)
-                .show()
-    }
-
     // ==============================================================================================================
     // ignored users list management
     // ==============================================================================================================
 
-    override fun invalidate() = withState(ignoredUsersViewModel) { state ->
+    override fun invalidate() = withState(viewModel) { state ->
         ignoredUsersController.update(state)
 
         handleUnIgnoreRequestStatus(state.unIgnoreRequest)
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt
index db4586dff5..672e8e7874 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt
@@ -16,13 +16,19 @@
 
 package im.vector.riotx.features.settings.push
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.pushers.Pusher
 import im.vector.matrix.rx.RxSession
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 
 data class PushGatewayViewState(
@@ -31,7 +37,7 @@ data class PushGatewayViewState(
 
 class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
                                                         private val session: Session)
-    : VectorViewModel<PushGatewayViewState, EmptyAction>(initialState) {
+    : VectorViewModel<PushGatewayViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesViewModel.kt
index e376b7ed6f..3c71238b33 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesViewModel.kt
@@ -21,6 +21,7 @@ import com.airbnb.mvrx.ViewModelContext
 import im.vector.matrix.android.api.pushrules.rest.PushRule
 import im.vector.riotx.core.di.HasScreenInjector
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
 
 data class PushRulesViewState(
@@ -28,7 +29,7 @@ data class PushRulesViewState(
 ) : MvRxState
 
 class PushRulesViewModel(initialState: PushRulesViewState)
-    : VectorViewModel<PushRulesViewState, EmptyAction>(initialState) {
+    : VectorViewModel<PushRulesViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {
 
diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt
index e48c8246d2..3669a51937 100644
--- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt
@@ -44,7 +44,9 @@ class IncomingShareActivity :
     @Inject lateinit var sessionHolder: ActiveSessionHolder
     @Inject lateinit var incomingShareViewModelFactory: IncomingShareViewModel.Factory
     private lateinit var attachmentsHelper: AttachmentsHelper
-    private val incomingShareViewModel: IncomingShareViewModel by viewModel()
+    // Do not remove, even if not used, it instantiates the view model
+    @Suppress("unused")
+    private val viewModel: IncomingShareViewModel by viewModel()
     private val roomListFragment: RoomListFragment?
         get() {
             return supportFragmentManager.findFragmentById(R.id.shareRoomListFragmentContainer) as? RoomListFragment
diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
index bfe08a5c52..8b791fbf1b 100644
--- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
@@ -26,7 +26,9 @@ import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
 import im.vector.matrix.rx.rx
 import im.vector.riotx.ActiveSessionDataSource
 import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
+import im.vector.riotx.features.home.room.list.BreadcrumbsRoomComparator
 import io.reactivex.Observable
 import io.reactivex.android.schedulers.AndroidSchedulers
 import java.util.concurrent.TimeUnit
@@ -38,8 +40,9 @@ data class IncomingShareState(private val dummy: Boolean = false) : MvRxState
  */
 class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState,
                                                          private val sessionObservableStore: ActiveSessionDataSource,
-                                                         private val shareRoomListObservableStore: ShareRoomListDataSource)
-    : VectorViewModel<IncomingShareState, EmptyAction>(initialState) {
+                                                         private val shareRoomListObservableStore: ShareRoomListDataSource,
+                                                         private val breadcrumbsRoomComparator: BreadcrumbsRoomComparator)
+    : VectorViewModel<IncomingShareState, EmptyAction, EmptyViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -68,6 +71,9 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
                             ?: Observable.just(emptyList())
                 }
                 .throttleLast(300, TimeUnit.MILLISECONDS)
+                .map {
+                    it.sortedWith(breadcrumbsRoomComparator)
+                }
                 .subscribe {
                     shareRoomListObservableStore.post(it)
                 }
diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt
index 8d61fb00b5..96dbbaf102 100644
--- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt
@@ -73,7 +73,7 @@ class SoftLogoutActivity : LoginActivity() {
 
     private fun handleSoftLogoutViewEvents(softLogoutViewEvents: SoftLogoutViewEvents) {
         when (softLogoutViewEvents) {
-            is SoftLogoutViewEvents.Error            ->
+            is SoftLogoutViewEvents.Failure          ->
                 showError(errorFormatter.toHumanReadable(softLogoutViewEvents.throwable))
             is SoftLogoutViewEvents.ErrorNotSameUser -> {
                 // Pop the backstack
diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt
index 1e48fb2a25..314d6b0747 100644
--- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt
+++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt
@@ -17,11 +17,14 @@
 
 package im.vector.riotx.features.signout.soft
 
+import im.vector.riotx.core.platform.VectorViewEvents
+
 /**
  * Transient events for SoftLogout
  */
-sealed class SoftLogoutViewEvents {
+sealed class SoftLogoutViewEvents : VectorViewEvents {
+    data class Failure(val throwable: Throwable) : SoftLogoutViewEvents()
+
     data class ErrorNotSameUser(val currentUserId: String, val newUserId: String) : SoftLogoutViewEvents()
-    data class Error(val throwable: Throwable) : SoftLogoutViewEvents()
     object ClearData : SoftLogoutViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt
index baf208636b..5ee7b6e8a3 100644
--- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt
@@ -16,7 +16,13 @@
 
 package im.vector.riotx.features.signout.soft
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
@@ -28,8 +34,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
 import im.vector.riotx.core.di.ActiveSessionHolder
 import im.vector.riotx.core.extensions.hasUnsavedKeys
 import im.vector.riotx.core.platform.VectorViewModel
-import im.vector.riotx.core.utils.DataSource
-import im.vector.riotx.core.utils.PublishDataSource
 import im.vector.riotx.features.login.LoginMode
 import timber.log.Timber
 
@@ -41,7 +45,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
         private val session: Session,
         private val activeSessionHolder: ActiveSessionHolder,
         private val authenticationService: AuthenticationService
-) : VectorViewModel<SoftLogoutViewState, SoftLogoutAction>(initialState) {
+) : VectorViewModel<SoftLogoutViewState, SoftLogoutAction, SoftLogoutViewEvents>(initialState) {
 
     @AssistedInject.Factory
     interface Factory {
@@ -71,9 +75,6 @@ class SoftLogoutViewModel @AssistedInject constructor(
 
     private var currentTask: Cancelable? = null
 
-    private val _viewEvents = PublishDataSource<SoftLogoutViewEvents>()
-    val viewEvents: DataSource<SoftLogoutViewEvents> = _viewEvents
-
     init {
         // Get the supported login flow
         getSupportedLoginFlow()
@@ -192,7 +193,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
                 currentTask = session.updateCredentials(action.credentials,
                         object : MatrixCallback<Unit> {
                             override fun onFailure(failure: Throwable) {
-                                _viewEvents.post(SoftLogoutViewEvents.Error(failure))
+                                _viewEvents.post(SoftLogoutViewEvents.Failure(failure))
                                 setState {
                                     copy(
                                             asyncLoginAction = Uninitialized
diff --git a/vector/src/main/res/layout/fragment_room_member_list.xml b/vector/src/main/res/layout/fragment_room_setting_generic.xml
similarity index 80%
rename from vector/src/main/res/layout/fragment_room_member_list.xml
rename to vector/src/main/res/layout/fragment_room_setting_generic.xml
index e1f7f7f20c..b28d987215 100644
--- a/vector/src/main/res/layout/fragment_room_member_list.xml
+++ b/vector/src/main/res/layout/fragment_room_setting_generic.xml
@@ -4,25 +4,26 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/rootConstraintLayout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="?riotx_header_panel_background">
 
     <androidx.appcompat.widget.Toolbar
-        android:id="@+id/roomMemberListToolbar"
+        android:id="@+id/roomSettingsToolbar"
         style="@style/VectorToolbarStyle"
-        android:elevation="4dp"
         android:layout_width="0dp"
         android:layout_height="?actionBarSize"
+        android:elevation="4dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent">
 
         <androidx.constraintlayout.widget.ConstraintLayout
-            android:id="@+id/roomMemberListToolbarContentView"
+            android:id="@+id/roomSettingsToolbarContentView"
             android:layout_width="match_parent"
             android:layout_height="match_parent">
 
             <ImageView
-                android:id="@+id/roomMemberListToolbarAvatarImageView"
+                android:id="@+id/roomSettingsToolbarAvatarImageView"
                 android:layout_width="40dp"
                 android:layout_height="40dp"
                 android:layout_marginTop="8dp"
@@ -33,7 +34,7 @@
                 tools:src="@tools:sample/avatars" />
 
             <im.vector.riotx.core.platform.EllipsizingTextView
-                android:id="@+id/roomMemberListToolbarTitleView"
+                android:id="@+id/roomSettingsToolbarTitleView"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="8dp"
@@ -42,9 +43,9 @@
                 android:maxLines="1"
                 android:textColor="?vctr_toolbar_primary_text_color"
                 android:textSize="18sp"
-                app:layout_constraintStart_toEndOf="@+id/roomMemberListToolbarAvatarImageView"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toEndOf="@+id/roomSettingsToolbarAvatarImageView"
                 app:layout_constraintTop_toTopOf="parent"
                 tools:text="@sample/matrix.json/data/roomName" />
 
@@ -57,10 +58,12 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:overScrollMode="always"
-        app:layout_constraintTop_toBottomOf="@+id/roomMemberListToolbar"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        tools:listitem="@layout/item_autocomplete_matrix_item" />
+        app:layout_constraintTop_toBottomOf="@+id/roomSettingsToolbar"
+        tools:listitem="@layout/item_profile_action" />
+
+    <include layout="@layout/merge_overlay_waiting_view" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/item_form_switch.xml b/vector/src/main/res/layout/item_form_switch.xml
index 0d0adde748..63004365dc 100644
--- a/vector/src/main/res/layout/item_form_switch.xml
+++ b/vector/src/main/res/layout/item_form_switch.xml
@@ -5,6 +5,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="?riotx_background"
+    android:foreground="?attr/selectableItemBackground"
     android:minHeight="@dimen/item_form_min_height">
 
     <im.vector.riotx.core.platform.EllipsizingTextView
diff --git a/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml b/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml
index c1987dccb2..ebc32baa57 100644
--- a/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml
+++ b/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml
@@ -36,8 +36,9 @@
         <ViewStub
             android:id="@+id/messageContentDefaultStub"
             style="@style/TimelineContentStubBaseParams"
-            android:inflatedId="@+id/stateMessageView"
-            android:layout="@layout/item_timeline_event_default_stub" />
+            android:layout="@layout/item_timeline_event_default_stub"
+            tools:layout_marginTop="80dp"
+            tools:visibility="visible" />
 
         <ViewStub
             android:id="@+id/messageContentBlankStub"
@@ -49,7 +50,9 @@
         <ViewStub
             android:id="@+id/messageContentMergedHeaderStub"
             style="@style/TimelineContentStubBaseParams"
-            android:layout="@layout/item_timeline_event_merged_header_stub" />
+            android:layout="@layout/item_timeline_event_merged_header_stub"
+            tools:layout_marginTop="160dp"
+            tools:visibility="visible" />
 
     </FrameLayout>
 
diff --git a/vector/src/main/res/layout/item_timeline_event_default_stub.xml b/vector/src/main/res/layout/item_timeline_event_default_stub.xml
index 345bda0b7e..68c8936b32 100644
--- a/vector/src/main/res/layout/item_timeline_event_default_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_default_stub.xml
@@ -1,12 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<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/stateMessageView"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:gravity="center_vertical"
-    android:padding="8dp"
-    android:textColor="?attr/colorAccent"
-    android:textSize="14sp"
-    android:textStyle="italic"
-    tools:text="Mon item" />
\ No newline at end of file
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/itemDefaultAvatarView"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:srcCompat="@tools:sample/avatars" />
+
+    <TextView
+        android:id="@+id/itemDefaultTextView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="8dp"
+        android:textColor="?attr/colorAccent"
+        android:textSize="14sp"
+        android:textStyle="italic"
+        tools:text="@string/rendering_event_error_type_of_event_not_handled" />
+
+</LinearLayout>
+
diff --git a/vector/src/main/res/layout/item_timeline_event_notice_stub.xml b/vector/src/main/res/layout/item_timeline_event_notice_stub.xml
index 76190062b1..9aacf357f1 100644
--- a/vector/src/main/res/layout/item_timeline_event_notice_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_notice_stub.xml
@@ -3,8 +3,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:orientation="horizontal"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
 
     <ImageView
         android:id="@+id/itemNoticeAvatarView"
@@ -15,16 +15,16 @@
         tools:srcCompat="@tools:sample/avatars" />
 
     <TextView
-        android:layout_gravity="top"
         android:id="@+id/itemNoticeTextView"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_gravity="top"
         android:layout_marginStart="8dp"
         android:layout_marginEnd="8dp"
         android:layout_marginBottom="8dp"
         android:textColor="?riotx_text_secondary"
         android:textSize="14sp"
         android:textStyle="italic"
-        tools:text="John doe changed their avatar" />
+        tools:text="@string/notice_avatar_url_changed" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/merge_overlay_waiting_view.xml b/vector/src/main/res/layout/merge_overlay_waiting_view.xml
index b7e7bf41c7..3ee572cd71 100644
--- a/vector/src/main/res/layout/merge_overlay_waiting_view.xml
+++ b/vector/src/main/res/layout/merge_overlay_waiting_view.xml
@@ -10,6 +10,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="?vctr_waiting_background_color"
+        android:clickable="true"
+        android:focusable="true"
         android:visibility="gone"
         tools:visibility="visible">
 
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index 7fff839e7e..11d3ffa5d6 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -3,6 +3,9 @@
 
     <!-- Strings not defined in Riot -->
 
+    <string name="create_room_encryption_title">"Enable encryption"</string>
+    <string name="create_room_encryption_description">"Once enabled, encryption cannot be disabled."</string>
+
     <string name="login_error_threepid_denied">Your email domain is not authorized to register on this server</string>
 
     <string name="room_profile_not_encrypted_subtitle">Messages in this room are not end-to-end encrypted.</string>
@@ -32,13 +35,29 @@
 
     <string name="room_member_jump_to_read_receipt">Jump to read receipt</string>
 
+    <string name="rendering_event_error_type_of_event_not_handled">"RiotX does not handle events of type '%1$s' (yet)"</string>
+    <string name="rendering_event_error_type_of_message_not_handled">"RiotX does not handle message of type '%1$s' (yet)"</string>
+    <string name="rendering_event_error_exception">"RiotX encountered an issue when rendering content of event with id '%1$s'"</string>
+
     <string name="unignore">Unignore</string>
 
+    <string name="room_list_sharing_header_recent_rooms">Recent rooms</string>
+    <string name="room_list_sharing_header_other_rooms">Other rooms</string>
+
+    <string name="command_description_rainbow">Sends the given message colored as a rainbow</string>
+    <string name="command_description_rainbow_emote">Sends the given emote colored as a rainbow</string>
+
     <!-- Title for category in the settings which affect what is displayed in the timeline (ex: show read receipts, etc.) -->
     <string name="settings_category_timeline">Timeline</string>
 
     <!-- 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_warning">Once enabled, encryption cannot be disabled.</string>
+
+    <string name="room_settings_enable_encryption_dialog_title">Enable encryption?</string>
+    <string name="room_settings_enable_encryption_dialog_content">Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly.</string>
+    <string name="room_settings_enable_encryption_dialog_submit">Enable encryption</string>
 
 </resources>
diff --git a/vector/src/test/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt b/vector/src/test/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt
new file mode 100644
index 0000000000..5a9fdc0ab7
--- /dev/null
+++ b/vector/src/test/java/im/vector/riotx/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.features.home.room.detail.composer.rainbow
+
+import im.vector.riotx.test.trimIndentOneLine
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@Suppress("SpellCheckingInspection")
+class RainbowGeneratorTest {
+
+    private val rainbowGenerator = RainbowGenerator()
+
+    @Test
+    fun testEmpty() {
+        assertEquals("", rainbowGenerator.generate(""))
+    }
+
+    @Test
+    fun testAscii1() {
+        assertEquals("""<font color="#ff0000">a</font>""", rainbowGenerator.generate("a"))
+    }
+
+    @Test
+    fun testAscii2() {
+        val expected = """
+            <font color="#ff0000">a</font>
+            <font color="#00ffff">b</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("ab"))
+    }
+
+    @Test
+    fun testAscii3() {
+        val expected = """
+            <font color="#ff0000">T</font>
+            <font color="#ff5500">h</font>
+            <font color="#ffaa00">i</font>
+            <font color="#ffff00">s</font>
+             
+            <font color="#55ff00">i</font>
+            <font color="#00ff00">s</font>
+             
+            <font color="#00ffaa">a</font>
+             
+            <font color="#00aaff">r</font>
+            <font color="#0055ff">a</font>
+            <font color="#0000ff">i</font>
+            <font color="#5500ff">n</font>
+            <font color="#aa00ff">b</font>
+            <font color="#ff00ff">o</font>
+            <font color="#ff00aa">w</font>
+            <font color="#ff0055">!</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("This is a rainbow!"))
+    }
+
+    @Test
+    fun testEmoji1() {
+        assertEquals("""<font color="#ff0000">🀞</font>""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🀞
+    }
+
+    @Test
+    fun testEmoji2() {
+        assertEquals("""<font color="#ff0000">🀞</font>""", rainbowGenerator.generate("🀞"))
+    }
+
+    @Test
+    fun testEmoji3() {
+        val expected = """
+            <font color="#ff0000">🀞</font>
+            <font color="#00ffff">πŸ™‚</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("πŸ€žπŸ™‚"))
+    }
+
+    @Test
+    fun testEmojiMix1() {
+        val expected = """
+            <font color="#ff0000">H</font>
+            <font color="#ff6d00">e</font>
+            <font color="#ffdb00">l</font>
+            <font color="#b6ff00">l</font>
+            <font color="#49ff00">o</font>
+             
+            <font color="#00ff92">🀞</font>
+             
+            <font color="#0092ff">w</font>
+            <font color="#0024ff">o</font>
+            <font color="#4900ff">r</font>
+            <font color="#b600ff">l</font>
+            <font color="#ff00db">d</font>
+            <font color="#ff006d">!</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("Hello 🀞 world!"))
+    }
+
+    @Test
+    fun testEmojiMix2() {
+        val expected = """
+            <font color="#ff0000">a</font>
+            <font color="#00ffff">🀞</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("a🀞"))
+    }
+
+    @Test
+    fun testEmojiMix3() {
+        val expected = """
+            <font color="#ff0000">🀞</font>
+            <font color="#00ffff">a</font>
+        """.trimIndentOneLine()
+
+        assertEquals(expected, rainbowGenerator.generate("🀞a"))
+    }
+
+    @Test
+    fun testError1() {
+        assertEquals("<font color=\"#ff0000\">\uD83E</font>", rainbowGenerator.generate("\uD83E"))
+    }
+}
diff --git a/vector/src/test/java/im/vector/riotx/test/Extensions.kt b/vector/src/test/java/im/vector/riotx/test/Extensions.kt
new file mode 100644
index 0000000000..31781ce00e
--- /dev/null
+++ b/vector/src/test/java/im/vector/riotx/test/Extensions.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ *
+ * 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 im.vector.riotx.test
+
+fun String.trimIndentOneLine() = trimIndent().replace("\n", "")