diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml
index 90f03779a6..f478d2bd7b 100644
--- a/.github/workflows/triage-labelled.yml
+++ b/.github/workflows/triage-labelled.yml
@@ -98,7 +98,8 @@ jobs:
     # Skip in forks
     if: >
       github.repository == 'vector-im/element-android' &&
-      (contains(github.event.issue.labels.*.name, 'Team: Delight'))
+      (contains(github.event.issue.labels.*.name, 'Team: Delight') ||
+       contains(github.event.issue.labels.*.name, 'Z-AppLayout'))
     steps:
       - uses: octokit/graphql-action@v2.x
         with:
diff --git a/changelog.d/6506.wip b/changelog.d/6506.wip
new file mode 100644
index 0000000000..344c0bca2f
--- /dev/null
+++ b/changelog.d/6506.wip
@@ -0,0 +1 @@
+[App Layout] added dialog to configure app layout
diff --git a/changelog.d/6787.wip b/changelog.d/6787.wip
new file mode 100644
index 0000000000..ace2b04d9e
--- /dev/null
+++ b/changelog.d/6787.wip
@@ -0,0 +1 @@
+[App Layout] Dialpad moved from bottom navigation tab to a separate activity accessed via home screen context menu
diff --git a/changelog.d/6801.wip b/changelog.d/6801.wip
new file mode 100644
index 0000000000..bb3bc9d7a1
--- /dev/null
+++ b/changelog.d/6801.wip
@@ -0,0 +1 @@
+Adds new chat bottom sheet as the click action of the main FAB in the new app layout
diff --git a/changelog.d/6827.bugfix b/changelog.d/6827.bugfix
new file mode 100644
index 0000000000..2c3e130aa3
--- /dev/null
+++ b/changelog.d/6827.bugfix
@@ -0,0 +1 @@
+Fixing sign in/up for homeservers that rely on the SSO fallback url
diff --git a/changelog.d/6864.bugfix b/changelog.d/6864.bugfix
new file mode 100644
index 0000000000..6db3d7c074
--- /dev/null
+++ b/changelog.d/6864.bugfix
@@ -0,0 +1 @@
+Fixes server selection being unable to trust certificates
diff --git a/changelog.d/6884.bugfix b/changelog.d/6884.bugfix
new file mode 100644
index 0000000000..6c6b286688
--- /dev/null
+++ b/changelog.d/6884.bugfix
@@ -0,0 +1 @@
+Ensure SyncThread is started when the app is launched after a Push has been received.
diff --git a/changelog.d/6884.sdk b/changelog.d/6884.sdk
new file mode 100644
index 0000000000..0de43420f0
--- /dev/null
+++ b/changelog.d/6884.sdk
@@ -0,0 +1 @@
+Rename `DebugService.logDbUsageInfo` (resp. `Session.logDbUsageInfo`) to `DebugService.getDbUsageInfo` (resp. `Session.getDbUsageInfo`) and return a String instead of logging. The caller may want to log the String.
diff --git a/changelog.d/6891.bugfix b/changelog.d/6891.bugfix
new file mode 100644
index 0000000000..b6b46e1d93
--- /dev/null
+++ b/changelog.d/6891.bugfix
@@ -0,0 +1 @@
+Fixes missing firebase notifications after logging in when UnifiedPush distributor is installed
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index ed3e3cdd7b..aa8731da2a 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -199,7 +199,7 @@ dependencies {
     implementation libs.apache.commonsImaging
 
     // Phone number https://github.com/google/libphonenumber
-    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.53'
+    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.54'
 
     testImplementation libs.tests.junit
     // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
index d0cee08831..7f5e4f2ee7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
@@ -28,7 +28,7 @@ interface DebugService {
     fun getAllRealmConfigurations(): List<RealmConfiguration>
 
     /**
-     * Prints out info on DB size to logcat.
+     * Get info on DB size.
      */
-    fun logDbUsageInfo()
+    fun getDbUsageInfo(): String
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
index 68b931b33c..429d346a1b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
@@ -93,6 +93,8 @@ fun Throwable.isMissingEmailVerification() = this is Failure.ServerError &&
         error.code == MatrixError.M_UNAUTHORIZED &&
         error.message == "Unable to get validated threepid"
 
+fun Throwable.isUnrecognisedCertificate() = this is Failure.UnrecognizedCertificateFailure
+
 /**
  * Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
  */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
index 63c1c25130..13993149f4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
@@ -323,9 +323,9 @@ interface Session {
     fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
 
     /**
-     * Debug API, will print out info on DB size to logcat.
+     * Debug API, will return info about the DB.
      */
-    fun logDbUsageInfo()
+    fun getDbUsageInfo(): String
 
     /**
      * Debug API, return the list of all RealmConfiguration used by this session.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
index 71f7ab8494..6640b8a9af 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
@@ -53,6 +53,11 @@ interface SyncService {
      */
     fun getSyncState(): SyncState
 
+    /**
+     * This method returns true if the sync thread is alive, i.e. started.
+     */
+    fun isSyncThreadAlive(): Boolean
+
     /**
      * This method allows to listen the sync state.
      * @return a [LiveData] of [SyncState].
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
index f2f88e216b..020b42b3b8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
@@ -38,7 +38,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
         LiveEntityObserver, RealmChangeListener<RealmResults<T>> {
 
     private companion object {
-        val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND")
+        val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-LIVE_ENTITY_BACKGROUND")
     }
 
     protected val observerScope = CoroutineScope(SupervisorJob() + BACKGROUND_HANDLER.asCoroutineDispatcher())
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
index dc20549eb3..2e9c3303d4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
@@ -19,16 +19,15 @@ package org.matrix.android.sdk.internal.database.tools
 import io.realm.Realm
 import io.realm.RealmConfiguration
 import org.matrix.android.sdk.BuildConfig
-import timber.log.Timber
 
 internal class RealmDebugTools(
         private val realmConfiguration: RealmConfiguration
 ) {
     /**
-     * Log info about the DB.
+     * Get info about the DB.
      */
-    fun logInfo(baseName: String) {
-        buildString {
+    fun getInfo(baseName: String): String {
+        return buildString {
             append("\n$baseName Realm located at : ${realmConfiguration.realmDirectory}/${realmConfiguration.realmFileName}")
 
             if (BuildConfig.LOG_PRIVATE_DATA) {
@@ -54,7 +53,6 @@ internal class RealmDebugTools(
                 separator()
             }
         }
-                .let { Timber.i(it) }
     }
 
     private fun StringBuilder.separator() = append("\n==============================================")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
index 3f2e6fafc8..46479c3db6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
@@ -36,9 +36,9 @@ internal class DefaultDebugService @Inject constructor(
                 realmConfigurationGlobal
     }
 
-    override fun logDbUsageInfo() {
-        RealmDebugTools(realmConfigurationAuth).logInfo("Auth")
-        RealmDebugTools(realmConfigurationGlobal).logInfo("Global")
-        sessionManager.getLastSession()?.logDbUsageInfo()
+    override fun getDbUsageInfo() = buildString {
+        append(RealmDebugTools(realmConfigurationAuth).getInfo("Auth"))
+        append(RealmDebugTools(realmConfigurationGlobal).getInfo("Global"))
+        append(sessionManager.getLastSession()?.getDbUsageInfo())
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
index 49713a1d7f..f2f8a5dc04 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
@@ -40,7 +40,7 @@ internal object MatrixModule {
                 io = Dispatchers.IO,
                 computation = Dispatchers.Default,
                 main = Dispatchers.Main,
-                crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(),
+                crypto = createBackgroundHandler("Matrix-Crypto_Thread").asCoroutineDispatcher(),
                 dmVerif = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
         )
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
index 57db187bdc..679c5085ef 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
@@ -263,11 +263,11 @@ internal class DefaultSession @Inject constructor(
         }
     }
 
-    override fun logDbUsageInfo() {
-        RealmDebugTools(realmConfiguration).logInfo("Session")
-        RealmDebugTools(realmConfigurationCrypto).logInfo("Crypto")
-        RealmDebugTools(realmConfigurationIdentity).logInfo("Identity")
-        RealmDebugTools(realmConfigurationContentScanner).logInfo("ContentScanner")
+    override fun getDbUsageInfo() = buildString {
+        append(RealmDebugTools(realmConfiguration).getInfo("Session"))
+        append(RealmDebugTools(realmConfigurationCrypto).getInfo("Crypto"))
+        append(RealmDebugTools(realmConfigurationIdentity).getInfo("Identity"))
+        append(RealmDebugTools(realmConfigurationContentScanner).getInfo("ContentScanner"))
     }
 
     override fun getRealmConfigurations(): List<RealmConfiguration> {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
index 51107c9655..55363a7251 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
@@ -55,7 +55,7 @@ internal class EventSenderProcessorThread @Inject constructor(
         private val queuedTaskFactory: QueuedTaskFactory,
         private val taskExecutor: TaskExecutor,
         private val memento: QueueMemento
-) : Thread("SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
+) : Thread("Matrix-SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
 
     private fun markAsManaged(task: QueuedTask) {
         memento.track(task)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 4eaac67e5a..c380ccf14f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -76,7 +76,7 @@ internal class DefaultTimeline(
 ) : Timeline {
 
     companion object {
-        val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
+        val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-DefaultTimeline_Thread")
     }
 
     override val timelineID = UUID.randomUUID().toString()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
index 691dd7b20d..76c3c38abf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
@@ -73,6 +73,8 @@ internal class DefaultSyncService @Inject constructor(
 
     override fun getSyncState() = getSyncThread().currentState()
 
+    override fun isSyncThreadAlive() = getSyncThread().isAlive
+
     override fun getSyncRequestStateFlow() = syncRequestStateTracker.syncRequestState
 
     override fun hasAlreadySynced(): Boolean {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
index 24a60a80da..b47b215655 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
@@ -62,7 +62,7 @@ internal class SyncThread @Inject constructor(
         private val backgroundDetectionObserver: BackgroundDetectionObserver,
         private val activeCallHandler: ActiveCallHandler,
         private val lightweightSettingsStorage: DefaultLightweightSettingsStorage
-) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
+) : Thread("Matrix-SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
 
     private var state: SyncState = SyncState.Idle
     private var liveState = MutableLiveData(state)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
index 901d0eca8f..dea5f131b9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
@@ -49,13 +49,13 @@ internal class DefaultBackgroundDetectionObserver : BackgroundDetectionObserver
     }
 
     override fun onStart(owner: LifecycleOwner) {
-        Timber.v("App returning to foreground…")
+        Timber.d("App returning to foreground…")
         isInBackground = false
         listeners.forEach { it.onMoveToForeground() }
     }
 
     override fun onStop(owner: LifecycleOwner) {
-        Timber.v("App going to background…")
+        Timber.d("App going to background…")
         isInBackground = true
         listeners.forEach { it.onMoveToBackground() }
     }
diff --git a/vector/build.gradle b/vector/build.gradle
index 18e094523e..7296262019 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -413,7 +413,7 @@ dependencies {
     implementation 'com.facebook.stetho:stetho:1.6.0'
 
     // Phone number https://github.com/google/libphonenumber
-    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.53'
+    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.54'
 
     // FlowBinding
     implementation libs.github.flowBinding
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index beee800b4a..bed0b618d0 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -348,6 +348,7 @@
         <activity android:name=".features.location.LocationSharingActivity" />
         <activity android:name=".features.location.live.map.LiveLocationMapViewActivity" />
         <activity android:name=".features.settings.font.FontScaleSettingActivity"/>
+        <activity android:name=".features.call.dialpad.PstnDialActivity" />
 
         <!-- Services -->
 
diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt
index 4655de7377..46cb6ec79b 100644
--- a/vector/src/main/java/im/vector/app/VectorApplication.kt
+++ b/vector/src/main/java/im/vector/app/VectorApplication.kt
@@ -266,7 +266,7 @@ class VectorApplication :
     }
 
     private fun createFontThreadHandler(): Handler {
-        val handlerThread = HandlerThread("fonts")
+        val handlerThread = HandlerThread("Vector-fonts")
         handlerThread.start()
         return Handler(handlerThread.looper)
     }
diff --git a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt
index 7a1d613ab9..bb2ca97aad 100644
--- a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt
+++ b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt
@@ -20,6 +20,7 @@ import android.content.Context
 import arrow.core.Option
 import im.vector.app.ActiveSessionDataSource
 import im.vector.app.core.extensions.configureAndStart
+import im.vector.app.core.extensions.startSyncing
 import im.vector.app.core.pushers.UnifiedPushHelper
 import im.vector.app.core.services.GuardServiceStarter
 import im.vector.app.features.call.webrtc.WebRtcCallManager
@@ -100,10 +101,16 @@ class ActiveSessionHolder @Inject constructor(
     }
 
     suspend fun getOrInitializeSession(startSync: Boolean): Session? {
-        return activeSessionReference.get() ?: sessionInitializer.tryInitialize(readCurrentSession = { activeSessionReference.get() }) { session ->
-            setActiveSession(session)
-            session.configureAndStart(applicationContext, startSyncing = startSync)
-        }
+        return activeSessionReference.get()
+                ?.also {
+                    if (startSync && !it.syncService().isSyncThreadAlive()) {
+                        it.startSyncing(applicationContext)
+                    }
+                }
+                ?: sessionInitializer.tryInitialize(readCurrentSession = { activeSessionReference.get() }) { session ->
+                    setActiveSession(session)
+                    session.configureAndStart(applicationContext, startSyncing = startSync)
+                }
     }
 
     fun isWaitingForSessionInitialization() = activeSessionReference.get() == null && authenticationService.hasAuthenticatedSessions()
diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
index 6b8fe735b3..22e1c99ab2 100644
--- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
@@ -63,6 +63,7 @@ import im.vector.app.features.home.room.detail.TimelineFragment
 import im.vector.app.features.home.room.detail.search.SearchFragment
 import im.vector.app.features.home.room.list.RoomListFragment
 import im.vector.app.features.home.room.list.home.HomeRoomListFragment
+import im.vector.app.features.home.room.list.home.NewChatBottomSheet
 import im.vector.app.features.home.room.threads.list.views.ThreadListFragment
 import im.vector.app.features.location.LocationSharingFragment
 import im.vector.app.features.location.preview.LocationPreviewFragment
@@ -191,6 +192,11 @@ interface FragmentModule {
     @FragmentKey(RoomListFragment::class)
     fun bindRoomListFragment(fragment: RoomListFragment): Fragment
 
+    @Binds
+    @IntoMap
+    @FragmentKey(NewChatBottomSheet::class)
+    fun bindNewChatBottomSheetFragment(fragment: NewChatBottomSheet): Fragment
+
     @Binds
     @IntoMap
     @FragmentKey(LocalePickerFragment::class)
diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt
index caed413e2b..cb1d46efce 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt
@@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.session.sync.FilterService
 import timber.log.Timber
 
 fun Session.configureAndStart(context: Context, startSyncing: Boolean = true) {
-    Timber.i("Configure and start session for $myUserId")
+    Timber.i("Configure and start session for $myUserId. startSyncing: $startSyncing")
     open()
     filterService().setFilter(FilterService.FilterPreset.ElementFilter)
     if (startSyncing) {
diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt
index 1f44ab3686..0993485471 100644
--- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt
+++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt
@@ -92,8 +92,6 @@ class UnifiedPushHelper @Inject constructor(
                 return@launch
             }
 
-            // By default, use internal solution (fcm/background sync)
-            UnifiedPush.saveDistributor(context, context.packageName)
             val distributors = UnifiedPush.getDistributors(context)
 
             if (distributors.size == 1 && !force) {
@@ -101,7 +99,14 @@ class UnifiedPushHelper @Inject constructor(
                 UnifiedPush.registerApp(context)
                 onDoneRunnable?.run()
             } else {
-                openDistributorDialogInternal(activity, pushersManager, onDoneRunnable, distributors, !force, !force)
+                openDistributorDialogInternal(
+                        activity = activity,
+                        pushersManager = pushersManager,
+                        onDoneRunnable = onDoneRunnable,
+                        distributors = distributors,
+                        unregisterFirst = force,
+                        cancellable = !force
+                )
             }
         }
     }
@@ -165,6 +170,12 @@ class UnifiedPushHelper @Inject constructor(
                         onDoneRunnable?.run()
                     }
                 }
+                .setOnCancelListener {
+                    // By default, use internal solution (fcm/background sync)
+                    UnifiedPush.saveDistributor(context, context.packageName)
+                    UnifiedPush.registerApp(context)
+                    onDoneRunnable?.run()
+                }
                 .setCancelable(cancellable)
                 .show()
     }
diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt
new file mode 100644
index 0000000000..a0d6e29849
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2022 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.app.features.call.dialpad
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatDialog
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.error.ErrorFormatter
+import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.features.call.webrtc.WebRtcCallManager
+import im.vector.app.features.createdirect.DirectRoomHelper
+import im.vector.app.features.settings.VectorLocale
+import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.session.Session
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class PstnDialActivity : SimpleFragmentActivity() {
+
+    @Inject lateinit var callManager: WebRtcCallManager
+    @Inject lateinit var directRoomHelper: DirectRoomHelper
+    @Inject lateinit var session: Session
+    @Inject lateinit var errorFormatter: ErrorFormatter
+
+    private var progress: AppCompatDialog? = null
+
+    override fun getTitleRes(): Int = R.string.call
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (isFirstCreation()) {
+            addFragment(
+                    views.container,
+                    createDialPadFragment()
+            )
+        }
+    }
+
+    private fun handleStartCallWithPhoneNumber(rawNumber: String) {
+        lifecycleScope.launch {
+            try {
+                showLoadingDialog()
+                val result = DialPadLookup(session, callManager, directRoomHelper).lookupPhoneNumber(rawNumber)
+                callManager.startOutgoingCall(result.roomId, result.userId, isVideoCall = false)
+                dismissLoadingDialog()
+                finish()
+            } catch (failure: Throwable) {
+                dismissLoadingDialog()
+                displayErrorDialog(failure)
+            }
+        }
+    }
+
+    private fun createDialPadFragment(): Fragment {
+        val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, DialPadFragment::class.java.name)
+        return (fragment as DialPadFragment).apply {
+            arguments = Bundle().apply {
+                putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
+                putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
+                putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
+            }
+            callback = object : DialPadFragment.Callback {
+                override fun onOkClicked(formatted: String?, raw: String?) {
+                    if (raw.isNullOrEmpty()) return
+                    handleStartCallWithPhoneNumber(raw)
+                }
+            }
+        }
+    }
+
+    private fun showLoadingDialog() {
+        progress?.dismiss()
+        progress = MaterialProgressDialog(this)
+                .show(getString(R.string.please_wait))
+    }
+
+    private fun dismissLoadingDialog() {
+        progress?.dismiss()
+    }
+
+    private fun displayErrorDialog(throwable: Throwable) {
+        MaterialAlertDialogBuilder(this)
+                .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/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 4142c89e30..2a8390c93c 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -59,6 +59,7 @@ import im.vector.app.features.crypto.recover.SetupMode
 import im.vector.app.features.disclaimer.showDisclaimerDialog
 import im.vector.app.features.home.room.list.actions.RoomListSharedAction
 import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
+import im.vector.app.features.home.room.list.home.layout.HomeLayoutSettingBottomDialogFragment
 import im.vector.app.features.matrixto.MatrixToBottomSheet
 import im.vector.app.features.matrixto.OriginOfMatrixTo
 import im.vector.app.features.navigation.Navigator
@@ -292,6 +293,11 @@ class HomeActivity :
                 .show(supportFragmentManager, "SPACE_SETTINGS")
     }
 
+    private fun showLayoutSettings() {
+        HomeLayoutSettingBottomDialogFragment()
+                .show(supportFragmentManager, "LAYOUT_SETTINGS")
+    }
+
     private fun openSpaceInvite(spaceId: String) {
         SpaceInviteBottomSheet.newInstance(spaceId)
                 .show(supportFragmentManager, "SPACE_INVITE")
@@ -613,6 +619,10 @@ class HomeActivity :
                 navigator.openSettings(this)
                 true
             }
+            R.id.menu_home_layout_settings -> {
+                showLayoutSettings()
+                true
+            }
             R.id.menu_home_invite_friends -> {
                 launchInviteFriends()
                 true
diff --git a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
index 719384ecf2..74bca3336a 100644
--- a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.home
 
+import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.Menu
@@ -45,12 +46,11 @@ import im.vector.app.core.ui.views.KeysBackupBanner
 import im.vector.app.databinding.FragmentNewHomeDetailBinding
 import im.vector.app.features.call.SharedKnownCallsViewModel
 import im.vector.app.features.call.VectorCallActivity
-import im.vector.app.features.call.dialpad.DialPadFragment
+import im.vector.app.features.call.dialpad.PstnDialActivity
 import im.vector.app.features.call.webrtc.WebRtcCallManager
 import im.vector.app.features.home.room.list.home.HomeRoomListFragment
 import im.vector.app.features.popup.PopupAlertManager
 import im.vector.app.features.popup.VerificationVectorAlert
-import im.vector.app.features.settings.VectorLocale
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS
 import im.vector.app.features.themes.ThemeUtils
@@ -102,6 +102,10 @@ class NewHomeDetailFragment @Inject constructor(
                 viewModel.handle(HomeDetailAction.MarkAllRoomsRead)
                 true
             }
+            R.id.menu_home_dialpad -> {
+                startActivity(Intent(requireContext(), PstnDialActivity::class.java))
+                true
+            }
             else -> false
         }
     }
@@ -110,6 +114,7 @@ class NewHomeDetailFragment @Inject constructor(
         withState(viewModel) { state ->
             val isRoomList = state.currentTab is HomeTab.RoomList
             menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = isRoomList && hasUnreadRooms
+            menu.findItem(R.id.menu_home_dialpad).isVisible = state.showDialPadTab
         }
     }
 
@@ -142,14 +147,10 @@ class NewHomeDetailFragment @Inject constructor(
             updateUIForTab(currentTab)
         }
 
-        viewModel.onEach(HomeDetailViewState::showDialPadTab) { showDialPadTab ->
-            updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab)
-        }
-
         viewModel.observeViewEvents { viewEvent ->
             when (viewEvent) {
-                HomeDetailViewEvents.CallStarted -> handleCallStarted()
-                is HomeDetailViewEvents.FailToCall -> showFailure(viewEvent.failure)
+                HomeDetailViewEvents.CallStarted -> Unit
+                is HomeDetailViewEvents.FailToCall -> Unit
                 HomeDetailViewEvents.Loading -> showLoadingDialog()
             }
         }
@@ -179,6 +180,17 @@ class NewHomeDetailFragment @Inject constructor(
                 }
     }
 
+    private fun navigateBack() {
+        val previousSpaceId = spaceStateHandler.getSpaceBackstack().removeLastOrNull()
+        val parentSpaceId = spaceStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
+        setCurrentSpace(previousSpaceId ?: parentSpaceId)
+    }
+
+    private fun setCurrentSpace(spaceId: String?) {
+        spaceStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
+        sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
+    }
+
     private fun handleCallStarted() {
         dismissLoadingDialog()
         val fragmentTag = HomeTab.DialPad.toFragmentTag()
@@ -333,30 +345,15 @@ class NewHomeDetailFragment @Inject constructor(
                         add(R.id.roomListContainer, HomeRoomListFragment::class.java, null, fragmentTag)
                     }
                     is HomeTab.DialPad -> {
-                        add(R.id.roomListContainer, createDialPadFragment(), fragmentTag)
+                        throw NotImplementedError("this tab shouldn't exists when app layout is enabled")
                     }
                 }
             } else {
-                if (tab is HomeTab.DialPad) {
-                    (fragmentToShow as? DialPadFragment)?.applyCallback()
-                }
                 attach(fragmentToShow)
             }
         }
     }
 
-    private fun createDialPadFragment(): Fragment {
-        val fragment = childFragmentManager.fragmentFactory.instantiate(vectorBaseActivity.classLoader, DialPadFragment::class.java.name)
-        return (fragment as DialPadFragment).apply {
-            arguments = Bundle().apply {
-                putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
-                putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
-                putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
-            }
-            applyCallback()
-        }
-    }
-
     private fun updateTabVisibilitySafely(tabId: Int, isVisible: Boolean) {
         val wasVisible = views.bottomNavigationView.menu.findItem(tabId).isVisible
         views.bottomNavigationView.menu.findItem(tabId).isVisible = isVisible
@@ -458,9 +455,8 @@ class NewHomeDetailFragment @Inject constructor(
         return this
     }
 
-    override fun onBackPressed(toolbarButton: Boolean) = try {
-        val lastSpace = spaceStateHandler.popSpaceBackstack()
-        spaceStateHandler.setCurrentSpace(lastSpace, isForwardNavigation = false)
+    override fun onBackPressed(toolbarButton: Boolean) = if (spaceStateHandler.getCurrentSpace() != null) {
+        navigateBack()
         true
     } catch (e: NoSuchElementException) {
         false
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineAsyncHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineAsyncHelper.kt
index 655d46194d..87c6a1efda 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineAsyncHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineAsyncHelper.kt
@@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.helper
 import android.os.Handler
 import android.os.HandlerThread
 
-private const val THREAD_NAME = "Timeline_Building_Thread"
+private const val THREAD_NAME = "Vector-Timeline_Building_Thread"
 
 object TimelineAsyncHelper {
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeLayoutPreferencesStore.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeLayoutPreferencesStore.kt
new file mode 100644
index 0000000000..11cd892406
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeLayoutPreferencesStore.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2022 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.app.features.home.room.list.home
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.preferencesDataStore
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import org.matrix.android.sdk.api.extensions.orFalse
+import javax.inject.Inject
+
+private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "layout_preferences")
+
+class HomeLayoutPreferencesStore @Inject constructor(
+        private val context: Context
+) {
+
+    private val areRecentsEnbabled = booleanPreferencesKey("SETTINGS_PREFERENCES_HOME_RECENTS")
+    private val areFiltersEnabled = booleanPreferencesKey("SETTINGS_PREFERENCES_HOME_FILTERS")
+    private val isAZOrderingEnabled = booleanPreferencesKey("SETTINGS_PREFERENCES_USE_AZ_ORDER")
+
+    val areRecentsEnabledFlow: Flow<Boolean> = context.dataStore.data
+            .map { preferences -> preferences[areRecentsEnbabled].orFalse() }
+            .distinctUntilChanged()
+
+    val areFiltersEnabledFlow: Flow<Boolean> = context.dataStore.data
+            .map { preferences -> preferences[areFiltersEnabled].orFalse() }
+            .distinctUntilChanged()
+
+    val isAZOrderingEnabledFlow: Flow<Boolean> = context.dataStore.data
+            .map { preferences -> preferences[isAZOrderingEnabled].orFalse() }
+            .distinctUntilChanged()
+
+    suspend fun setRecentsEnabled(isEnabled: Boolean) {
+        context.dataStore.edit { settings ->
+            settings[areRecentsEnbabled] = isEnabled
+        }
+    }
+
+    suspend fun setFiltersEnabled(isEnabled: Boolean) {
+        context.dataStore.edit { settings ->
+            settings[areFiltersEnabled] = isEnabled
+        }
+    }
+
+    suspend fun setAZOrderingEnabled(isEnabled: Boolean) {
+        context.dataStore.edit { settings ->
+            settings[isAZOrderingEnabled] = isEnabled
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
index 8cbf760025..9464033896 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
@@ -74,6 +74,8 @@ class HomeRoomListFragment @Inject constructor(
 
     private lateinit var stateRestorer: LayoutManagerStateRestorer
 
+    private val newChatBottomSheet = NewChatBottomSheet()
+
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomListBinding {
         return FragmentRoomListBinding.inflate(inflater, container, false)
     }
@@ -160,13 +162,22 @@ class HomeRoomListFragment @Inject constructor(
         }.launchIn(lifecycleScope)
 
         views.roomListView.adapter = concatAdapter
+
+        // we need to force scroll when recents/filter tabs are added to make them visible
+        concatAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+                if (positionStart == 0) {
+                    layoutManager.scrollToPosition(0)
+                }
+            }
+        })
     }
 
     private fun setupFabs() {
         showFABs()
 
         views.newLayoutCreateChatButton.setOnClickListener {
-            // Click action for create chat modal goes here (Issue #6717)
+            newChatBottomSheet.show(requireActivity().supportFragmentManager, NewChatBottomSheet.TAG)
         }
 
         views.newLayoutOpenSpacesButton.setOnClickListener {
@@ -203,6 +214,9 @@ class HomeRoomListFragment @Inject constructor(
     }
 
     private fun setUpAdapters(sections: Set<HomeRoomSection>) {
+        concatAdapter.adapters.forEach {
+            concatAdapter.removeAdapter(it)
+        }
         sections.forEach {
             concatAdapter.addAdapter(getAdapterForData(it))
         }
@@ -232,12 +246,11 @@ class HomeRoomListFragment @Inject constructor(
             is HomeRoomSection.RoomSummaryData -> {
                 HomeFilteredRoomsController(
                         roomSummaryItemFactory,
-                        showFilters = section.showFilters,
                 ).also { controller ->
                     controller.listener = this
                     controller.onFilterChanged = ::onRoomFilterChanged
                     section.filtersData.onEach {
-                        controller.submitFiltersData(it)
+                        controller.submitFiltersData(it.getOrNull())
                     }.launchIn(lifecycleScope)
                     section.list.observe(viewLifecycleOwner) { list ->
                         controller.submitList(list)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
index 1fed9eba86..711ba0c10a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
@@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -53,12 +54,14 @@ import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
 import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import org.matrix.android.sdk.api.session.room.state.isPublic
+import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.flow.flow
 
 class HomeRoomListViewModel @AssistedInject constructor(
         @Assisted initialState: HomeRoomListViewState,
         private val session: Session,
         private val spaceStateHandler: SpaceStateHandler,
+        private val preferencesStore: HomeLayoutPreferencesStore,
 ) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
 
     @AssistedFactory
@@ -82,17 +85,30 @@ class HomeRoomListViewModel @AssistedInject constructor(
 
     init {
         configureSections()
+        observePreferences()
     }
 
-    private fun configureSections() {
+    private fun observePreferences() {
+        preferencesStore.areRecentsEnabledFlow.onEach {
+            configureSections()
+        }.launchIn(viewModelScope)
+
+        preferencesStore.isAZOrderingEnabledFlow.onEach {
+            configureSections()
+        }.launchIn(viewModelScope)
+    }
+
+    private fun configureSections() = viewModelScope.launch {
         val newSections = mutableSetOf<HomeRoomSection>()
 
-        newSections.add(getRecentRoomsSection())
+        val areSettingsEnabled = preferencesStore.areRecentsEnabledFlow.first()
+
+        if (areSettingsEnabled) {
+            newSections.add(getRecentRoomsSection())
+        }
         newSections.add(getFilteredRoomsSection())
 
-        viewModelScope.launch {
-            _sections.emit(newSections)
-        }
+        _sections.emit(newSections)
 
         setState {
             copy(state = StateView.State.Content)
@@ -111,13 +127,17 @@ class HomeRoomListViewModel @AssistedInject constructor(
         )
     }
 
-    private fun getFilteredRoomsSection(): HomeRoomSection.RoomSummaryData {
+    private suspend fun getFilteredRoomsSection(): HomeRoomSection.RoomSummaryData {
         val builder = RoomSummaryQueryParams.Builder().also {
             it.memberships = listOf(Membership.JOIN)
         }
 
         val params = getFilteredQueryParams(HomeRoomFilter.ALL, builder.build())
-        val sortOrder = RoomSortOrder.ACTIVITY // #6506
+        val sortOrder = if (preferencesStore.isAZOrderingEnabledFlow.first()) {
+            RoomSortOrder.NAME
+        } else {
+            RoomSortOrder.ACTIVITY
+        }
 
         val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
                 params,
@@ -135,19 +155,18 @@ class HomeRoomListViewModel @AssistedInject constructor(
                 .onEach { selectedSpaceOption ->
                     val selectedSpace = selectedSpaceOption.orNull()
                     liveResults.queryParams = liveResults.queryParams.copy(
-                            spaceFilter =  selectedSpace?.roomId.toActiveSpaceOrNoFilter()
+                            spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
                     )
                 }.launchIn(viewModelScope)
 
         return HomeRoomSection.RoomSummaryData(
                 list = liveResults.livePagedList,
-                showFilters = true, // #6506
                 filtersData = getFiltersDataFlow()
         )
     }
 
-    private fun getFiltersDataFlow(): SharedFlow<List<HomeRoomFilter>> {
-        val flow = MutableSharedFlow<List<HomeRoomFilter>>(replay = 1)
+    private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
+        val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
 
         val favouritesFlow = session.flow()
                 .liveRoomSummaries(
@@ -168,25 +187,28 @@ class HomeRoomListViewModel @AssistedInject constructor(
                 .map { it.isNotEmpty() }
                 .distinctUntilChanged()
 
-        favouritesFlow.combine(dmsFLow) { hasFavourite, hasDm ->
-            hasFavourite to hasDm
-        }.onEach { (hasFavourite, hasDm) ->
-            val filtersData = mutableListOf(
-                    HomeRoomFilter.ALL,
-                    HomeRoomFilter.UNREADS
-            )
-            if (hasFavourite) {
-                filtersData.add(
-                        HomeRoomFilter.FAVOURITES
+        combine(favouritesFlow, dmsFLow, preferencesStore.areFiltersEnabledFlow) { hasFavourite, hasDm, areFiltersEnabled ->
+            Triple(hasFavourite, hasDm, areFiltersEnabled)
+        }.onEach { (hasFavourite, hasDm, areFiltersEnabled) ->
+            if (areFiltersEnabled) {
+                val filtersData = mutableListOf(
+                        HomeRoomFilter.ALL,
+                        HomeRoomFilter.UNREADS
                 )
+                if (hasFavourite) {
+                    filtersData.add(
+                            HomeRoomFilter.FAVOURITES
+                    )
+                }
+                if (hasDm) {
+                    filtersData.add(
+                            HomeRoomFilter.PEOPlE
+                    )
+                }
+                flow.emit(Optional.from(filtersData))
+            } else {
+                flow.emit(Optional.empty())
             }
-            if (hasDm) {
-                filtersData.add(
-                        HomeRoomFilter.PEOPlE
-                )
-            }
-
-            flow.emit(filtersData)
         }.launchIn(viewModelScope)
 
         return flow
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt
index f51b479d37..74ec46d6b7 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt
@@ -21,12 +21,12 @@ import androidx.paging.PagedList
 import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
 import kotlinx.coroutines.flow.SharedFlow
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.util.Optional
 
 sealed class HomeRoomSection {
     data class RoomSummaryData(
             val list: LiveData<PagedList<RoomSummary>>,
-            val showFilters: Boolean,
-            val filtersData: SharedFlow<List<HomeRoomFilter>>
+            val filtersData: SharedFlow<Optional<List<HomeRoomFilter>>>,
     ) : HomeRoomSection()
 
     data class RecentRoomsData(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt
new file mode 100644
index 0000000000..05b86f7393
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 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.app.features.home.room.list.home
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.databinding.FragmentNewChatBottomSheetBinding
+import im.vector.app.features.navigation.Navigator
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class NewChatBottomSheet @Inject constructor() : BottomSheetDialogFragment() {
+
+    @Inject lateinit var navigator: Navigator
+
+    private lateinit var binding: FragmentNewChatBottomSheetBinding
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        binding = FragmentNewChatBottomSheetBinding.inflate(inflater, container, false)
+        initFABs()
+        return binding.root
+    }
+
+    private fun initFABs() {
+        binding.startChat.setOnClickListener {
+            navigator.openCreateDirectRoom(requireActivity())
+        }
+
+        binding.createRoom.setOnClickListener {
+            navigator.openCreateRoom(requireActivity())
+        }
+
+        binding.exploreRooms.setOnClickListener {
+            navigator.openRoomDirectory(requireContext())
+        }
+    }
+
+    companion object {
+        const val TAG = "NewChatBottomSheet"
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt
index 7c1f154d52..2d673bc089 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt
@@ -28,7 +28,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
 
 class HomeFilteredRoomsController(
         private val roomSummaryItemFactory: RoomSummaryItemFactory,
-        private val showFilters: Boolean,
 ) : PagedListEpoxyController<RoomSummary>(
         // Important it must match the PageList builder notify Looper
         modelBuildingHandler = createUIHandler()
@@ -48,7 +47,7 @@ class HomeFilteredRoomsController(
 
     override fun addModels(models: List<EpoxyModel<*>>) {
         val host = this
-        if (showFilters) {
+        if (host.filtersData != null) {
             roomFilterHeaderItem {
                 id("filter_header")
                 filtersData(host.filtersData)
@@ -58,7 +57,7 @@ class HomeFilteredRoomsController(
         super.addModels(models)
     }
 
-    fun submitFiltersData(data: List<HomeRoomFilter>) {
+    fun submitFiltersData(data: List<HomeRoomFilter>?) {
         this.filtersData = data
         requestForcedModelBuild()
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt
new file mode 100644
index 0000000000..0c4d64a1cc
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2022 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.app.features.home.room.list.home.layout
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.lifecycleScope
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
+import im.vector.app.databinding.BottomSheetHomeLayoutSettingsBinding
+import im.vector.app.features.home.room.list.home.HomeLayoutPreferencesStore
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class HomeLayoutSettingBottomDialogFragment : VectorBaseBottomSheetDialogFragment<BottomSheetHomeLayoutSettingsBinding>() {
+
+    @Inject lateinit var preferencesStore: HomeLayoutPreferencesStore
+
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetHomeLayoutSettingsBinding {
+        return BottomSheetHomeLayoutSettingsBinding.inflate(inflater, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        viewLifecycleOwner.lifecycleScope.launch {
+            views.homeLayoutSettingsRecents.isChecked = preferencesStore.areRecentsEnabledFlow.first()
+            views.homeLayoutSettingsFilters.isChecked = preferencesStore.areFiltersEnabledFlow.first()
+
+            if (preferencesStore.isAZOrderingEnabledFlow.first()) {
+                views.homeLayoutSettingsSortName.isChecked = true
+            } else {
+                views.homeLayoutSettingsSortActivity.isChecked = true
+            }
+        }
+
+        views.homeLayoutSettingsRecents.setOnCheckedChangeListener { _, isChecked ->
+            setRecentsEnabled(isChecked)
+        }
+        views.homeLayoutSettingsFilters.setOnCheckedChangeListener { _, isChecked ->
+            setFiltersEnabled(isChecked)
+        }
+        views.homeLayoutSettingsSortGroup.setOnCheckedChangeListener { _, checkedId ->
+            setAzOrderingEnabled(checkedId == R.id.home_layout_settings_sort_name)
+        }
+    }
+
+    private fun setRecentsEnabled(isEnabled: Boolean) = lifecycleScope.launch {
+        preferencesStore.setRecentsEnabled(isEnabled)
+    }
+
+    private fun setFiltersEnabled(isEnabled: Boolean) = lifecycleScope.launch {
+        preferencesStore.setFiltersEnabled(isEnabled)
+    }
+
+    private fun setAzOrderingEnabled(isEnabled: Boolean) = lifecycleScope.launch {
+        preferencesStore.setAZOrderingEnabled(isEnabled)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt
index 53832bbc74..ebec912779 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt
@@ -59,7 +59,13 @@ class RecentRoomCarouselController @Inject constructor(
         data?.let { data ->
             carousel {
                 id("recents_carousel")
-                padding(Carousel.Padding(host.hPadding, host.itemSpacing))
+                padding(Carousel.Padding(
+                        host.hPadding,
+                        0,
+                        host.hPadding,
+                        0,
+                        host.itemSpacing)
+                )
                 withModelsFrom(data) { roomSummary ->
                     val onClick = host.listener?.let { it::onRoomClicked }
                     val onLongClick = host.listener?.let { it::onRoomLongClicked }
diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
index b18df6c9cf..ddab65d981 100644
--- a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
@@ -85,7 +85,7 @@ abstract class AbstractSSOLoginFragment<VB : ViewBinding> : AbstractLoginFragmen
 
     private fun prefetchIfNeeded() {
         withState(loginViewModel) { state ->
-            if (state.loginMode.hasSso() && state.loginMode.ssoIdentityProviders().isNullOrEmpty()) {
+            if (state.loginMode.hasSso() && state.loginMode.ssoState().isFallback()) {
                 // in this case we can prefetch (not other cases for privacy concerns)
                 loginViewModel.getSsoUrl(
                         redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
diff --git a/vector/src/main/java/im/vector/app/features/login/HomeServerConnectionConfigFactory.kt b/vector/src/main/java/im/vector/app/features/login/HomeServerConnectionConfigFactory.kt
index 955c3f7290..253c514e5a 100644
--- a/vector/src/main/java/im/vector/app/features/login/HomeServerConnectionConfigFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/login/HomeServerConnectionConfigFactory.kt
@@ -17,12 +17,13 @@
 package im.vector.app.features.login
 
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.network.ssl.Fingerprint
 import timber.log.Timber
 import javax.inject.Inject
 
 class HomeServerConnectionConfigFactory @Inject constructor() {
 
-    fun create(url: String?): HomeServerConnectionConfig? {
+    fun create(url: String?, fingerprint: Fingerprint? = null): HomeServerConnectionConfig? {
         if (url == null) {
             return null
         }
@@ -30,6 +31,13 @@ class HomeServerConnectionConfigFactory @Inject constructor() {
         return try {
             HomeServerConnectionConfig.Builder()
                     .withHomeServerUri(url)
+                    .run {
+                        if (fingerprint == null) {
+                            this
+                        } else {
+                            withAllowedFingerPrints(listOf(fingerprint))
+                        }
+                    }
                     .build()
         } catch (t: Throwable) {
             Timber.e(t)
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
index 9c598c400b..03010e0a75 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
@@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.MatrixError
 import org.matrix.android.sdk.api.failure.isInvalidPassword
@@ -100,13 +99,11 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
         }
     }
 
-    private fun setupSocialLoginButtons(state: LoginViewState) {
-        views.loginSocialLoginButtons.mode = when (state.signMode) {
-            SignMode.Unknown -> error("developer error")
-            SignMode.SignUp -> SocialLoginButtonsView.Mode.MODE_SIGN_UP
-            SignMode.SignIn,
-            SignMode.SignInWithMatrixId -> SocialLoginButtonsView.Mode.MODE_SIGN_IN
-        }
+    private fun ssoMode(state: LoginViewState) = when (state.signMode) {
+        SignMode.Unknown -> error("developer error")
+        SignMode.SignUp -> SocialLoginButtonsView.Mode.MODE_SIGN_UP
+        SignMode.SignIn,
+        SignMode.SignInWithMatrixId -> SocialLoginButtonsView.Mode.MODE_SIGN_IN
     }
 
     private fun submit() {
@@ -201,16 +198,13 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
 
             if (state.loginMode is LoginMode.SsoAndPassword) {
                 views.loginSocialLoginContainer.isVisible = true
-                views.loginSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders?.sorted()
-                views.loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
-                    override fun onProviderSelected(provider: SsoIdentityProvider?) {
-                        loginViewModel.getSsoUrl(
-                                redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
-                                deviceId = state.deviceId,
-                                providerId = provider?.id
-                        )
-                                ?.let { openInCustomTab(it) }
-                    }
+                views.loginSocialLoginButtons.render(state.loginMode.ssoState, ssoMode(state)) { provider ->
+                    loginViewModel.getSsoUrl(
+                            redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+                            deviceId = state.deviceId,
+                            providerId = provider?.id
+                    )
+                            ?.let { openInCustomTab(it) }
                 }
             } else {
                 views.loginSocialLoginContainer.isVisible = false
@@ -272,7 +266,6 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
 
         setupUi(state)
         setupAutoFill(state)
-        setupSocialLoginButtons(state)
         setupButtons(state)
 
         when (state.asyncLoginAction) {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginMode.kt b/vector/src/main/java/im/vector/app/features/login/LoginMode.kt
index dd479c89c5..944b159441 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginMode.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginMode.kt
@@ -18,22 +18,21 @@ package im.vector.app.features.login
 
 import android.os.Parcelable
 import kotlinx.parcelize.Parcelize
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 
 sealed class LoginMode : Parcelable { // Parcelable because persist state
 
     @Parcelize object Unknown : LoginMode()
     @Parcelize object Password : LoginMode()
-    @Parcelize data class Sso(val ssoIdentityProviders: List<SsoIdentityProvider>?) : LoginMode()
-    @Parcelize data class SsoAndPassword(val ssoIdentityProviders: List<SsoIdentityProvider>?) : LoginMode()
+    @Parcelize data class Sso(val ssoState: SsoState) : LoginMode()
+    @Parcelize data class SsoAndPassword(val ssoState: SsoState) : LoginMode()
     @Parcelize object Unsupported : LoginMode()
 }
 
-fun LoginMode.ssoIdentityProviders(): List<SsoIdentityProvider>? {
+fun LoginMode.ssoState(): SsoState {
     return when (this) {
-        is LoginMode.Sso -> ssoIdentityProviders
-        is LoginMode.SsoAndPassword -> ssoIdentityProviders
-        else -> null
+        is LoginMode.Sso -> ssoState
+        is LoginMode.SsoAndPassword -> ssoState
+        else -> SsoState.Fallback
     }
 }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
index 1325ea37af..aafd426335 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
@@ -25,7 +25,7 @@ import com.airbnb.mvrx.withState
 import im.vector.app.R
 import im.vector.app.core.extensions.toReducedUrl
 import im.vector.app.databinding.FragmentLoginSignupSigninSelectionBinding
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
+import im.vector.app.features.login.SocialLoginButtonsView.Mode
 import javax.inject.Inject
 
 /**
@@ -73,16 +73,13 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
         when (state.loginMode) {
             is LoginMode.SsoAndPassword -> {
                 views.loginSignupSigninSignInSocialLoginContainer.isVisible = true
-                views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders()?.sorted()
-                views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
-                    override fun onProviderSelected(provider: SsoIdentityProvider?) {
-                        loginViewModel.getSsoUrl(
-                                redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
-                                deviceId = state.deviceId,
-                                providerId = provider?.id
-                        )
-                                ?.let { openInCustomTab(it) }
-                    }
+                views.loginSignupSigninSocialLoginButtons.render(state.loginMode.ssoState(), Mode.MODE_CONTINUE) { provider ->
+                    loginViewModel.getSsoUrl(
+                            redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+                            deviceId = state.deviceId,
+                            providerId = provider?.id
+                    )
+                            ?.let { openInCustomTab(it) }
                 }
             }
             else -> {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index 40f72ccc99..79d06a0864 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -223,7 +223,7 @@ class LoginViewModel @AssistedInject constructor(
         setState {
             copy(
                     signMode = SignMode.SignIn,
-                    loginMode = LoginMode.Sso(action.ssoIdentityProviders),
+                    loginMode = LoginMode.Sso(action.ssoIdentityProviders.toSsoState()),
                     homeServerUrlFromUser = action.homeServerUrl,
                     homeServerUrl = action.homeServerUrl,
                     deviceId = action.deviceId
@@ -816,8 +816,8 @@ class LoginViewModel @AssistedInject constructor(
             val loginMode = when {
                 // SSO login is taken first
                 data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
-                        data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
-                data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
+                        data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders.toSsoState())
+                data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState())
                 data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
                 else -> LoginMode.Unsupported
             }
diff --git a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
index 4f3b1237f2..816050420e 100644
--- a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
@@ -160,8 +160,11 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
     }
 }
 
-fun SocialLoginButtonsView.render(ssoProviders: List<SsoIdentityProvider>?, mode: SocialLoginButtonsView.Mode, listener: (SsoIdentityProvider?) -> Unit) {
+fun SocialLoginButtonsView.render(state: SsoState, mode: SocialLoginButtonsView.Mode, listener: (SsoIdentityProvider?) -> Unit) {
     this.mode = mode
-    this.ssoIdentityProviders = ssoProviders?.sorted()
+    this.ssoIdentityProviders = when (state) {
+        SsoState.Fallback -> null
+        is SsoState.IdentityProviders -> state.providers.sorted()
+    }
     this.listener = SocialLoginButtonsView.InteractionListener { listener(it) }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/SsoState.kt b/vector/src/main/java/im/vector/app/features/login/SsoState.kt
new file mode 100644
index 0000000000..5f57780bd7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/SsoState.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 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.app.features.login
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
+
+sealed interface SsoState : Parcelable {
+    @Parcelize
+    data class IdentityProviders(val providers: List<SsoIdentityProvider>) : SsoState
+
+    @Parcelize
+    object Fallback : SsoState
+
+    fun isFallback() = this == Fallback
+
+    fun providersOrNull() = when (this) {
+        Fallback -> null
+        is IdentityProviders -> providers.takeIf { it.isNotEmpty() }
+    }
+}
+
+fun List<SsoIdentityProvider>?.toSsoState() = this
+        ?.takeIf { it.isNotEmpty() }
+        ?.let { SsoState.IdentityProviders(it) }
+        ?: SsoState.Fallback
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
index d07ac46b85..f1617b660b 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
@@ -82,7 +82,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
 
     data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction
 
-    data class UserAcceptCertificate(val fingerprint: Fingerprint) : OnboardingAction
+    data class UserAcceptCertificate(val fingerprint: Fingerprint, val retryAction: OnboardingAction) : OnboardingAction
 
     object PersonalizeProfile : OnboardingAction
     data class UpdateDisplayName(val displayName: String) : OnboardingAction
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
index bbbf13fba9..1441152128 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
@@ -21,6 +21,7 @@ import im.vector.app.core.platform.VectorViewEvents
 import im.vector.app.features.login.ServerType
 import im.vector.app.features.login.SignMode
 import org.matrix.android.sdk.api.auth.registration.Stage
+import org.matrix.android.sdk.api.failure.Failure as SdkFailure
 
 /**
  * Transient events for Login.
@@ -29,6 +30,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {
     data class Loading(val message: CharSequence? = null) : OnboardingViewEvents()
     data class Failure(val throwable: Throwable) : OnboardingViewEvents()
     data class DeeplinkAuthenticationFailure(val retryAction: OnboardingAction) : OnboardingViewEvents()
+    data class UnrecognisedCertificateFailure(val retryAction: OnboardingAction, val cause: SdkFailure.UnrecognizedCertificateFailure) : OnboardingViewEvents()
 
     object DisplayRegistrationFallback : OnboardingViewEvents()
     data class DisplayRegistrationStage(val stage: Stage) : OnboardingViewEvents()
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 73288bd6d5..6228b95398 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -60,7 +60,10 @@ import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.auth.login.LoginWizard
 import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
 import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
+import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
+import org.matrix.android.sdk.api.failure.isUnrecognisedCertificate
+import org.matrix.android.sdk.api.network.ssl.Fingerprint
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
 import timber.log.Timber
@@ -113,10 +116,6 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    // Store the last action, to redo it after user has trusted the untrusted certificate
-    private var lastAction: OnboardingAction? = null
-    private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
-
     private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
     private val defaultHomeserverUrl = matrixOrgUrl
 
@@ -146,9 +145,9 @@ class OnboardingViewModel @AssistedInject constructor(
             is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
             is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
             is OnboardingAction.InitWith -> handleInitWith(action)
-            is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
+            is OnboardingAction.HomeServerChange -> handleHomeserverChange(action)
             is OnboardingAction.UserNameEnteredAction -> handleUserNameEntered(action)
-            is AuthenticateAction -> withAction(action) { handleAuthenticateAction(action) }
+            is AuthenticateAction -> handleAuthenticateAction(action)
             is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
             is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
             is OnboardingAction.ResetPassword -> handleResetPassword(action)
@@ -221,11 +220,6 @@ class OnboardingViewModel @AssistedInject constructor(
         )
     }
 
-    private fun withAction(action: OnboardingAction, block: (OnboardingAction) -> Unit) {
-        lastAction = action
-        block(action)
-    }
-
     private fun handleAuthenticateAction(action: AuthenticateAction) {
         when (action) {
             is AuthenticateAction.Register -> handleRegisterWith(action.username, action.password, action.initialDeviceName)
@@ -276,20 +270,13 @@ class OnboardingViewModel @AssistedInject constructor(
     private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
         // It happens when we get the login flow, or during direct authentication.
         // So alter the homeserver config and retrieve again the login flow
-        when (val finalLastAction = lastAction) {
-            is OnboardingAction.HomeServerChange.SelectHomeServer -> {
-                currentHomeServerConnectionConfig
-                        ?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
-                        ?.let { startAuthenticationFlow(finalLastAction, it, serverTypeOverride = null) }
-            }
+        when (action.retryAction) {
+            is OnboardingAction.HomeServerChange -> handleHomeserverChange(action.retryAction, fingerprint = action.fingerprint)
             is AuthenticateAction.LoginDirect ->
                 handleDirectLogin(
-                        finalLastAction,
-                        HomeServerConnectionConfig.Builder()
-                                // Will be replaced by the task
-                                .withHomeServerUri("https://dummy.org")
-                                .withAllowedFingerPrints(listOf(action.fingerprint))
-                                .build()
+                        action.retryAction,
+                        // Will be replaced by the task
+                        homeServerConnectionConfigFactory.create("https://dummy.org", action.fingerprint)
                 )
             else -> Unit
         }
@@ -589,9 +576,19 @@ class OnboardingViewModel @AssistedInject constructor(
         currentJob = viewModelScope.launch {
             directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
                     onSuccess = { onSessionCreated(it, authenticationDescription = AuthenticationDescription.Login) },
-                    onFailure = {
+                    onFailure = { error ->
                         setState { copy(isLoading = false) }
-                        _viewEvents.post(OnboardingViewEvents.Failure(it))
+                        when {
+                            error.isUnrecognisedCertificate() -> {
+                                _viewEvents.post(
+                                        OnboardingViewEvents.UnrecognisedCertificateFailure(
+                                                retryAction = action,
+                                                cause = error as Failure.UnrecognizedCertificateFailure
+                                        )
+                                )
+                            }
+                            else -> _viewEvents.post(OnboardingViewEvents.Failure(error))
+                        }
                     }
             )
         }
@@ -682,8 +679,13 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    private fun handleHomeserverChange(action: OnboardingAction.HomeServerChange, serverTypeOverride: ServerType? = null, postAction: suspend () -> Unit = {}) {
-        val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl)
+    private fun handleHomeserverChange(
+            action: OnboardingAction.HomeServerChange,
+            serverTypeOverride: ServerType? = null,
+            fingerprint: Fingerprint? = null,
+            postAction: suspend () -> Unit = {},
+    ) {
+        val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl, fingerprint)
         if (homeServerConnectionConfig == null) {
             // This is invalid
             _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
@@ -698,8 +700,6 @@ class OnboardingViewModel @AssistedInject constructor(
             serverTypeOverride: ServerType?,
             postAction: suspend () -> Unit = {},
     ) {
-        currentHomeServerConnectionConfig = homeServerConnectionConfig
-
         currentJob = viewModelScope.launch {
             setState { copy(isLoading = true) }
             runCatching { startAuthenticationFlowUseCase.execute(homeServerConnectionConfig) }.fold(
@@ -723,9 +723,10 @@ class OnboardingViewModel @AssistedInject constructor(
                             retryAction = (trigger as OnboardingAction.HomeServerChange.SelectHomeServer).resetToDefaultUrl()
                     )
             )
-            else -> _viewEvents.post(
-                    OnboardingViewEvents.Failure(error)
-            )
+            error.isUnrecognisedCertificate() -> {
+                _viewEvents.post(OnboardingViewEvents.UnrecognisedCertificateFailure(trigger, error as Failure.UnrecognizedCertificateFailure))
+            }
+            else -> _viewEvents.post(OnboardingViewEvents.Failure(error))
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCase.kt
index 2dc9a05154..db21a53854 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCase.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.onboarding
 
 import im.vector.app.core.extensions.containsAllItems
 import im.vector.app.features.login.LoginMode
+import im.vector.app.features.login.toSsoState
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.data.LoginFlowResult
@@ -50,8 +51,8 @@ class StartAuthenticationFlowUseCase @Inject constructor(
     )
 
     private fun LoginFlowResult.findPreferredLoginMode() = when {
-        supportedLoginTypes.containsAllItems(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(ssoIdentityProviders)
-        supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(ssoIdentityProviders)
+        supportedLoginTypes.containsAllItems(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(ssoIdentityProviders.toSsoState())
+        supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(ssoIdentityProviders.toSsoState())
         supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
         else -> LoginMode.Unsupported
     }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
index 072e94bc30..f3cb326221 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
@@ -33,7 +33,6 @@ import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewModel
 import im.vector.app.features.onboarding.OnboardingViewState
 import kotlinx.coroutines.CancellationException
-import org.matrix.android.sdk.api.failure.Failure
 
 /**
  * Parent Fragment for all the login/registration screens.
@@ -68,6 +67,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
     private fun handleOnboardingViewEvents(viewEvents: OnboardingViewEvents) {
         when (viewEvents) {
             is OnboardingViewEvents.Failure -> showFailure(viewEvents.throwable)
+            is OnboardingViewEvents.UnrecognisedCertificateFailure -> showUnrecognizedCertificateFailure(viewEvents)
             else ->
                 // This is handled by the Activity
                 Unit
@@ -84,20 +84,20 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
             is CancellationException ->
                 /* Ignore this error, user has cancelled the action */
                 Unit
-            is Failure.UnrecognizedCertificateFailure -> showUnrecognizedCertificateFailure(throwable)
             else -> onError(throwable)
         }
     }
 
-    private fun showUnrecognizedCertificateFailure(failure: Failure.UnrecognizedCertificateFailure) {
+    private fun showUnrecognizedCertificateFailure(event: OnboardingViewEvents.UnrecognisedCertificateFailure) {
         // Ask the user to accept the certificate
+        val cause = event.cause
         unrecognizedCertificateDialog.show(requireActivity(),
-                failure.fingerprint,
-                failure.url,
+                cause.fingerprint,
+                cause.url,
                 object : UnrecognizedCertificateDialog.Callback {
                     override fun onAccept() {
                         // User accept the certificate
-                        viewModel.handle(OnboardingAction.UserAcceptCertificate(failure.fingerprint))
+                        viewModel.handle(OnboardingAction.UserAcceptCertificate(cause.fingerprint, event.retryAction))
                     }
 
                     override fun onIgnore() {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractSSOFtueAuthFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractSSOFtueAuthFragment.kt
index 1b764f4ce6..b1352db0cc 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractSSOFtueAuthFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractSSOFtueAuthFragment.kt
@@ -26,7 +26,7 @@ import com.airbnb.mvrx.withState
 import im.vector.app.core.utils.openUrlInChromeCustomTab
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.hasSso
-import im.vector.app.features.login.ssoIdentityProviders
+import im.vector.app.features.login.ssoState
 
 abstract class AbstractSSOFtueAuthFragment<VB : ViewBinding> : AbstractFtueAuthFragment<VB>() {
 
@@ -88,7 +88,7 @@ abstract class AbstractSSOFtueAuthFragment<VB : ViewBinding> : AbstractFtueAuthF
 
     private fun prefetchIfNeeded() {
         withState(viewModel) { state ->
-            if (state.selectedHomeserver.preferredLoginMode.hasSso() && state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders().isNullOrEmpty()) {
+            if (state.selectedHomeserver.preferredLoginMode.hasSso() && state.selectedHomeserver.preferredLoginMode.ssoState().isFallback()) {
                 // in this case we can prefetch (not other cases for privacy concerns)
                 viewModel.fetchSsoUrl(
                         redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index c2d2346765..b24511b4bc 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -38,13 +38,13 @@ import im.vector.app.databinding.FragmentFtueCombinedLoginBinding
 import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.SocialLoginButtonsView
+import im.vector.app.features.login.SsoState
 import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewState
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import reactivecircus.flowbinding.android.widget.textChanges
 import javax.inject.Inject
 
@@ -125,11 +125,11 @@ class FtueAuthCombinedLoginFragment @Inject constructor(
         when (state.selectedHomeserver.preferredLoginMode) {
             is LoginMode.SsoAndPassword -> {
                 showUsernamePassword()
-                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
+                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
             }
             is LoginMode.Sso -> {
                 hideUsernamePassword()
-                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
+                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
             }
             else -> {
                 showUsernamePassword()
@@ -138,10 +138,10 @@ class FtueAuthCombinedLoginFragment @Inject constructor(
         }
     }
 
-    private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
-        views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
-        views.ssoButtonsHeader.isVisible = views.ssoGroup.isVisible && views.loginEntryGroup.isVisible
-        views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
+    private fun renderSsoProviders(deviceId: String?, ssoState: SsoState) {
+        views.ssoGroup.isVisible = true
+        views.ssoButtonsHeader.isVisible = isUsernameAndPasswordVisible()
+        views.ssoButtons.render(ssoState, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
             viewModel.fetchSsoUrl(
                     redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
                     deviceId = deviceId,
@@ -163,6 +163,8 @@ class FtueAuthCombinedLoginFragment @Inject constructor(
         views.loginEntryGroup.isVisible = true
     }
 
+    private fun isUsernameAndPasswordVisible() = views.loginEntryGroup.isVisible
+
     private fun setupAutoFill() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             views.loginInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
index 8340fb903a..d06d1e8051 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
@@ -44,6 +44,7 @@ import im.vector.app.databinding.FragmentFtueCombinedRegisterBinding
 import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.SocialLoginButtonsView
+import im.vector.app.features.login.SsoState
 import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
@@ -51,7 +52,6 @@ import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewState
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
 import org.matrix.android.sdk.api.failure.isInvalidPassword
 import org.matrix.android.sdk.api.failure.isInvalidUsername
@@ -205,14 +205,14 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
         }
 
         when (state.selectedHomeserver.preferredLoginMode) {
-            is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
+            is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
             else -> hideSsoProviders()
         }
     }
 
-    private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
-        views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
-        views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { provider ->
+    private fun renderSsoProviders(deviceId: String?, ssoState: SsoState) {
+        views.ssoGroup.isVisible = true
+        views.ssoButtons.render(ssoState, SocialLoginButtonsView.Mode.MODE_CONTINUE) { provider ->
             viewModel.fetchSsoUrl(
                     redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
                     deviceId = deviceId,
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
index 17ceb5314c..ad159943b1 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
@@ -37,7 +37,8 @@ import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.ServerType
 import im.vector.app.features.login.SignMode
-import im.vector.app.features.login.SocialLoginButtonsView
+import im.vector.app.features.login.SocialLoginButtonsView.Mode
+import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewState
@@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.failure.isInvalidPassword
 import org.matrix.android.sdk.api.failure.isInvalidUsername
 import org.matrix.android.sdk.api.failure.isLoginEmailUnknown
@@ -111,13 +111,11 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
         }
     }
 
-    private fun setupSocialLoginButtons(state: OnboardingViewState) {
-        views.loginSocialLoginButtons.mode = when (state.signMode) {
-            SignMode.Unknown -> error("developer error")
-            SignMode.SignUp -> SocialLoginButtonsView.Mode.MODE_SIGN_UP
-            SignMode.SignIn,
-            SignMode.SignInWithMatrixId -> SocialLoginButtonsView.Mode.MODE_SIGN_IN
-        }
+    private fun ssoMode(state: OnboardingViewState) = when (state.signMode) {
+        SignMode.Unknown -> error("developer error")
+        SignMode.SignUp -> Mode.MODE_SIGN_UP
+        SignMode.SignIn,
+        SignMode.SignInWithMatrixId -> Mode.MODE_SIGN_IN
     }
 
     private fun submit() {
@@ -215,16 +213,13 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
 
             if (state.selectedHomeserver.preferredLoginMode is LoginMode.SsoAndPassword) {
                 views.loginSocialLoginContainer.isVisible = true
-                views.loginSocialLoginButtons.ssoIdentityProviders = state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders?.sorted()
-                views.loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
-                    override fun onProviderSelected(provider: SsoIdentityProvider?) {
-                        viewModel.fetchSsoUrl(
-                                redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
-                                deviceId = state.deviceId,
-                                provider = provider
-                        )
-                                ?.let { openInCustomTab(it) }
-                    }
+                views.loginSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode.ssoState, ssoMode(state)) { provider ->
+                    viewModel.fetchSsoUrl(
+                            redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+                            deviceId = state.deviceId,
+                            provider = provider
+                    )
+                            ?.let { openInCustomTab(it) }
                 }
             } else {
                 views.loginSocialLoginContainer.isVisible = false
@@ -305,7 +300,6 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
 
         setupUi(state)
         setupAutoFill(state)
-        setupSocialLoginButtons(state)
         setupButtons(state)
 
         if (state.isLoading) {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSignUpSignInSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSignUpSignInSelectionFragment.kt
index 6723e48bcc..dc4c6d8a34 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSignUpSignInSelectionFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSignUpSignInSelectionFragment.kt
@@ -30,11 +30,10 @@ import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.ServerType
 import im.vector.app.features.login.SignMode
-import im.vector.app.features.login.SocialLoginButtonsView
-import im.vector.app.features.login.ssoIdentityProviders
+import im.vector.app.features.login.SocialLoginButtonsView.Mode
+import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingViewState
-import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import javax.inject.Inject
 
 /**
@@ -80,16 +79,13 @@ class FtueAuthSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOF
         when (state.selectedHomeserver.preferredLoginMode) {
             is LoginMode.SsoAndPassword -> {
                 views.loginSignupSigninSignInSocialLoginContainer.isVisible = true
-                views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders()?.sorted()
-                views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
-                    override fun onProviderSelected(provider: SsoIdentityProvider?) {
-                        viewModel.fetchSsoUrl(
-                                redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
-                                deviceId = state.deviceId,
-                                provider = provider
-                        )
-                                ?.let { openInCustomTab(it) }
-                    }
+                views.loginSignupSigninSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode.ssoState, Mode.MODE_CONTINUE) { provider ->
+                    viewModel.fetchSsoUrl(
+                            redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+                            deviceId = state.deviceId,
+                            provider = provider
+                    )
+                            ?.let { openInCustomTab(it) }
                 }
             }
             else -> {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
index 150ab74ec2..e568b3d92b 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
@@ -202,6 +202,7 @@ class FtueAuthVariant(
                 openMsisdnConfirmation(viewEvents.msisdn)
             }
             is OnboardingViewEvents.Failure,
+            is OnboardingViewEvents.UnrecognisedCertificateFailure,
             is OnboardingViewEvents.Loading ->
                 // This is handled by the Fragments
                 Unit
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
index ad09593ebd..eefbf63a12 100755
--- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt
@@ -78,6 +78,7 @@ class BugReporter @Inject constructor(
         private val systemLocaleProvider: SystemLocaleProvider,
         private val matrix: Matrix,
         private val buildMeta: BuildMeta,
+        private val processInfo: ProcessInfo,
         private val sdkIntProvider: BuildVersionSdkIntProvider,
 ) {
     var inMultiWindowMode = false
@@ -499,10 +500,26 @@ class BugReporter @Inject constructor(
      */
     fun openBugReportScreen(activity: FragmentActivity, reportType: ReportType = ReportType.BUG_REPORT) {
         screenshot = takeScreenshot(activity)
-        matrix.debugService().logDbUsageInfo()
+        logDbInfo()
+        logProcessInfo()
+        logOtherInfo()
         activity.startActivity(BugReportActivity.intent(activity, reportType))
     }
 
+    private fun logOtherInfo() {
+        Timber.i("SyncThread state: " + activeSessionHolder.getSafeActiveSession()?.syncService()?.getSyncState())
+    }
+
+    private fun logDbInfo() {
+        val dbInfo = matrix.debugService().getDbUsageInfo()
+        Timber.i(dbInfo)
+    }
+
+    private fun logProcessInfo() {
+        val pInfo = processInfo.getInfo()
+        Timber.i(pInfo)
+    }
+
     private fun rageShakeAppNameForReport(reportType: ReportType): String {
         // As per https://github.com/matrix-org/rageshake
         // app: Identifier for the application (eg 'riot-web').
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/ProcessInfo.kt b/vector/src/main/java/im/vector/app/features/rageshake/ProcessInfo.kt
new file mode 100644
index 0000000000..78e49a2e65
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/rageshake/ProcessInfo.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 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.app.features.rageshake
+
+import android.annotation.SuppressLint
+import android.app.Application
+import android.os.Build
+import android.os.Process
+import java.lang.reflect.Method
+import javax.inject.Inject
+
+class ProcessInfo @Inject constructor() {
+    fun getInfo() = buildString {
+        append("===========================================\n")
+        append("*              PROCESS INFO               *\n")
+        append("===========================================\n")
+        val processId = Process.myPid()
+        append("ProcessId: $processId\n")
+        append("ProcessName: ${getProcessName()}\n")
+        append(getThreadInfo())
+        append("===========================================\n")
+    }
+
+    @SuppressLint("PrivateApi")
+    private fun getProcessName(): String? {
+        return if (Build.VERSION.SDK_INT >= 28) {
+            Application.getProcessName()
+        } else {
+            try {
+                val activityThread = Class.forName("android.app.ActivityThread")
+                val getProcessName: Method = activityThread.getDeclaredMethod("currentProcessName")
+                getProcessName.invoke(null) as? String
+            } catch (t: Throwable) {
+                null
+            }
+        }
+    }
+
+    private fun getThreadInfo() = buildString {
+        append("Thread activeCount: ${Thread.activeCount()}\n")
+        Thread.getAllStackTraces().keys
+                .sortedBy { it.name }
+                .forEach { thread -> append(thread.getInfo()) }
+    }
+}
+
+private fun Thread.getInfo() = buildString {
+    append("Thread '$name':")
+    append(" id: $id")
+    append(" priority: $priority")
+    append(" group name: ${threadGroup?.name ?: "null"}")
+    append(" state: $state")
+    append(" isAlive: $isAlive")
+    append(" isDaemon: $isDaemon")
+    append(" isInterrupted: $isInterrupted")
+    append("\n")
+}
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt
index 5b369d4b49..483e3a140f 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt
@@ -63,7 +63,7 @@ class SoftLogoutFragment @Inject constructor(
                             LoginAction.SetupSsoForSessionRecovery(
                                     softLogoutViewState.homeServerUrl,
                                     softLogoutViewState.deviceId,
-                                    mode.ssoIdentityProviders
+                                    mode.ssoState.providersOrNull()
                             )
                     )
                 }
@@ -72,7 +72,7 @@ class SoftLogoutFragment @Inject constructor(
                             LoginAction.SetupSsoForSessionRecovery(
                                     softLogoutViewState.homeServerUrl,
                                     softLogoutViewState.deviceId,
-                                    mode.ssoIdentityProviders
+                                    mode.ssoState.providersOrNull()
                             )
                     )
                 }
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
index 9d0580638b..f3e2f82edc 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
@@ -33,6 +33,7 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.extensions.hasUnsavedKeys
 import im.vector.app.core.platform.VectorViewModel
 import im.vector.app.features.login.LoginMode
+import im.vector.app.features.login.toSsoState
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.LoginType
@@ -115,8 +116,8 @@ class SoftLogoutViewModel @AssistedInject constructor(
             val loginMode = when {
                 // SSO login is taken first
                 data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
-                        data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
-                data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
+                        data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders.toSsoState())
+                data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState())
                 data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
                 else -> LoginMode.Unsupported
             }
diff --git a/vector/src/main/res/drawable/ic_chat.xml b/vector/src/main/res/drawable/ic_chat.xml
new file mode 100644
index 0000000000..fb10eae9c9
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_chat.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12.283,21.44C17.649,21.44 22,17.088 22,11.719C22,6.351 17.649,1.999 12.283,1.999C6.916,1.999 2.566,6.351 2.566,11.719C2.566,13.223 2.907,14.648 3.517,15.918L2.045,20.705C1.808,21.474 2.531,22.194 3.299,21.953L8.046,20.47C9.326,21.091 10.764,21.44 12.283,21.44Z"
+      android:fillColor="#737D8C"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_room_add.xml b/vector/src/main/res/drawable/ic_room_add.xml
new file mode 100644
index 0000000000..8404ff2181
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_room_add.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M17.048,4.105C17.107,3.556 16.708,3.064 16.159,3.006C15.61,2.948 15.118,3.346 15.059,3.895L14.668,7.602H9.613L9.983,4.105C10.041,3.556 9.642,3.064 9.093,3.006C8.544,2.948 8.052,3.346 7.994,3.895L7.602,7.602H4.834C4.281,7.602 3.834,8.05 3.834,8.602C3.834,9.154 4.281,9.602 4.834,9.602H7.39L6.872,14.505H4C3.448,14.505 3,14.953 3,15.505C3,16.058 3.448,16.506 4,16.506H6.661L6.331,19.622C6.273,20.171 6.671,20.663 7.221,20.721C7.77,20.779 8.262,20.381 8.32,19.832L8.589,17.288C8.319,16.818 8.165,16.274 8.165,15.693C8.165,14.871 8.474,14.122 8.984,13.555L9.401,9.602H12.959C13.538,8.776 14.497,8.235 15.583,8.235C16.669,8.235 17.628,8.776 18.207,9.602H19.379C19.931,9.602 20.379,9.154 20.379,8.602C20.379,8.05 19.931,7.602 19.379,7.602H16.679L17.048,4.105ZM15.583,10.382C16.135,10.382 16.583,10.83 16.583,11.382V14.693H19.852C20.404,14.693 20.852,15.141 20.852,15.693C20.852,16.246 20.404,16.694 19.852,16.694H16.583V20.004C16.583,20.557 16.135,21.004 15.583,21.004C15.031,21.004 14.583,20.557 14.583,20.004V16.694H11.313C10.76,16.694 10.313,16.246 10.313,15.693C10.313,15.141 10.76,14.693 11.313,14.693H14.583V11.382C14.583,10.83 15.031,10.382 15.583,10.382Z"
+      android:fillColor="#737D8C"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_room_explore.xml b/vector/src/main/res/drawable/ic_room_explore.xml
new file mode 100644
index 0000000000..9811a09054
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_room_explore.xml
@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16.647,3.006C17.196,3.065 17.593,3.558 17.533,4.108L17.147,7.684H19.998C20.55,7.684 20.998,8.132 20.998,8.684C20.998,9.236 20.55,9.684 19.998,9.684H16.931L16.8,10.891C16.058,10.967 15.356,11.181 14.722,11.508L14.919,9.684H9.582L9.039,14.708H11.898C11.67,15.332 11.545,16.006 11.544,16.708H8.822L8.455,20.111C8.395,20.66 7.902,21.057 7.353,20.997C6.804,20.938 6.407,20.445 6.466,19.896L6.811,16.708H3.999C3.447,16.708 2.999,16.26 2.999,15.708C2.999,15.156 3.447,14.708 3.999,14.708H7.027L7.57,9.684H4.864C4.312,9.684 3.864,9.236 3.864,8.684C3.864,8.132 4.312,7.684 4.864,7.684H7.786L8.196,3.893C8.255,3.344 8.748,2.947 9.298,3.006C9.847,3.065 10.244,3.558 10.184,4.108L9.798,7.684H15.135L15.545,3.893C15.604,3.344 16.097,2.947 16.647,3.006Z"
+      android:fillColor="#737D8C"
+      android:fillType="evenOdd"/>
+  <path
+      android:pathData="M19.003,16.765C19.003,17.817 18.151,18.669 17.1,18.669C16.048,18.669 15.196,17.817 15.196,16.765C15.196,15.714 16.048,14.862 17.1,14.862C18.151,14.862 19.003,15.714 19.003,16.765ZM20.332,18.698C20.67,18.133 20.865,17.472 20.865,16.765C20.865,14.686 19.179,13 17.1,13C15.02,13 13.334,14.686 13.334,16.765C13.334,18.845 15.02,20.531 17.1,20.531C17.806,20.531 18.467,20.336 19.032,19.998C19.062,20.038 19.094,20.077 19.131,20.114L20.745,21.727C21.108,22.091 21.698,22.091 22.061,21.727C22.425,21.364 22.425,20.774 22.061,20.411L20.448,18.797C20.411,18.76 20.372,18.728 20.332,18.698Z"
+      android:fillColor="#737D8C"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/layout/bottom_sheet_home_layout_settings.xml b/vector/src/main/res/layout/bottom_sheet_home_layout_settings.xml
new file mode 100644
index 0000000000..1766695354
--- /dev/null
+++ b/vector/src/main/res/layout/bottom_sheet_home_layout_settings.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?colorSurface"
+    android:orientation="vertical">
+
+    <TextView
+        style="@style/Widget.Vector.TextView.Subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="8dp"
+        android:text="@string/home_layout_preferences"
+        android:textAllCaps="true" />
+
+    <com.google.android.material.switchmaterial.SwitchMaterial
+        android:id="@+id/home_layout_settings_recents"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_marginHorizontal="16dp"
+        android:checked="true"
+        android:text="@string/home_layout_preferences_recents" />
+
+    <com.google.android.material.switchmaterial.SwitchMaterial
+        android:id="@+id/home_layout_settings_filters"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_marginHorizontal="16dp"
+        android:checked="true"
+        android:text="@string/home_layout_preferences_filters" />
+
+    <TextView
+        style="@style/Widget.Vector.TextView.Subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp"
+        android:layout_marginVertical="8dp"
+        android:text="@string/home_layout_preferences_sort_by"
+        android:textAllCaps="true" />
+
+    <RadioGroup
+        android:id="@+id/home_layout_settings_sort_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp"
+        android:orientation="vertical">
+
+        <RadioButton
+            android:id="@+id/home_layout_settings_sort_activity"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:checked="true"
+            android:text="@string/home_layout_preferences_sort_activity"
+            android:textColor="?vctr_content_primary" />
+
+        <RadioButton
+            android:id="@+id/home_layout_settings_sort_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/home_layout_preferences_sort_name"
+            android:textColor="?vctr_content_primary" />
+    </RadioGroup>
+
+</LinearLayout>
diff --git a/vector/src/main/res/layout/fragment_new_chat_bottom_sheet.xml b/vector/src/main/res/layout/fragment_new_chat_bottom_sheet.xml
new file mode 100644
index 0000000000..07c19b43eb
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_new_chat_bottom_sheet.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/start_chat"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?selectableItemBackground"
+        android:drawablePadding="16dp"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="16dp"
+        android:text="@string/start_chat"
+        android:textColor="?vctr_content_primary"
+        android:textSize="16sp"
+        app:drawableStartCompat="@drawable/ic_chat" />
+
+    <TextView
+        android:id="@+id/create_room"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?selectableItemBackground"
+        android:drawablePadding="16dp"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="16dp"
+        android:text="@string/create_room"
+        android:textColor="?vctr_content_primary"
+        android:textSize="16sp"
+        app:drawableStartCompat="@drawable/ic_room_add" />
+
+    <TextView
+        android:id="@+id/explore_rooms"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?selectableItemBackground"
+        android:drawablePadding="16dp"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="16dp"
+        android:text="@string/explore_rooms"
+        android:textColor="?vctr_content_primary"
+        android:textSize="16sp"
+        app:drawableStartCompat="@drawable/ic_room_explore" />
+
+</LinearLayout>
diff --git a/vector/src/main/res/menu/menu_new_home.xml b/vector/src/main/res/menu/menu_new_home.xml
index e893624d54..6cd52e5608 100644
--- a/vector/src/main/res/menu/menu_new_home.xml
+++ b/vector/src/main/res/menu/menu_new_home.xml
@@ -3,6 +3,10 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <item
+        android:id="@+id/menu_home_layout_settings"
+        android:title="@string/home_layout_preferences"/>
+
     <item
         android:id="@+id/menu_home_invite_friends"
         android:title="@string/invite_friends"
@@ -37,6 +41,6 @@
         android:icon="@drawable/ic_home_search"
         android:title="@string/home_filter_placeholder_home"
         app:iconTint="?vctr_content_secondary"
-        app:showAsAction="always" />
+        app:showAsAction="ifRoom" />
 
 </menu>
diff --git a/vector/src/main/res/menu/room_list.xml b/vector/src/main/res/menu/room_list.xml
index 60ffdcd87b..ad375d241b 100644
--- a/vector/src/main/res/menu/room_list.xml
+++ b/vector/src/main/res/menu/room_list.xml
@@ -1,9 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu 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">
 
     <item
         android:id="@+id/menu_home_mark_all_as_read"
         android:icon="@drawable/ic_material_done"
         android:title="@string/action_mark_all_as_read" />
 
-</menu>
\ No newline at end of file
+    <item
+        android:id="@+id/menu_home_dialpad"
+        android:title="@string/call_dial_pad_title"
+        android:visible="false"
+        app:showAsAction="never"
+        tools:visible="true" />
+</menu>
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 483ceb5d2a..0b62c16f92 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -137,7 +137,10 @@
 
     <!-- Home Screen -->
     <string name="all_chats">All Chats</string>
+    <string name="start_chat">Start Chat</string>
+    <string name="create_room">Create Room</string>
     <string name="change_space">Change Space</string>
+    <string name="explore_rooms">Explore Rooms</string>
 
     <!-- Last seen time -->
 
@@ -424,6 +427,15 @@
 
     <!-- Home screen -->
     <string name="home_filter_placeholder_home">Filter room names</string>
+    <string name="home_layout_preferences">Layout preferences</string>
+
+
+    <!-- Home screen layout settings -->
+    <string name="home_layout_preferences_filters">Show filters</string>
+    <string name="home_layout_preferences_recents">Show recents</string>
+    <string name="home_layout_preferences_sort_by">Sort by</string>
+    <string name="home_layout_preferences_sort_activity">Activity</string>
+    <string name="home_layout_preferences_sort_name">A - Z</string>
 
     <!-- Home fragment -->
     <string name="invitations_header">Invites</string>
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
index b505d05944..216cb76084 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
@@ -48,6 +48,7 @@ import im.vector.app.test.fakes.FakeVectorOverrides
 import im.vector.app.test.fakes.toTestString
 import im.vector.app.test.fixtures.a401ServerError
 import im.vector.app.test.fixtures.aHomeServerCapabilities
+import im.vector.app.test.fixtures.anUnrecognisedCertificateError
 import im.vector.app.test.test
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBeEqualTo
@@ -58,6 +59,7 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.auth.registration.Stage
 import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.network.ssl.Fingerprint
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 
@@ -65,10 +67,12 @@ private const val A_DISPLAY_NAME = "a display name"
 private const val A_PICTURE_FILENAME = "a-picture.png"
 private val A_SERVER_ERROR = a401ServerError()
 private val AN_ERROR = RuntimeException("an error!")
+private val AN_UNRECOGNISED_CERTIFICATE_ERROR = anUnrecognisedCertificateError()
 private val A_LOADABLE_REGISTER_ACTION = RegisterAction.StartRegistration
 private val A_NON_LOADABLE_REGISTER_ACTION = RegisterAction.CheckIfEmailHasBeenValidated(delayMillis = -1L)
 private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.SendAgainThreePid
 private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
+private val A_FINGERPRINT = Fingerprint(ByteArray(1), Fingerprint.HashType.SHA1)
 private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationActionHandler.Result.NextStage(Stage.Dummy(mandatory = true))
 private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
 private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
@@ -320,6 +324,25 @@ class OnboardingViewModelTest {
                 .finish()
     }
 
+    @Test
+    fun `given has sign in with matrix id sign mode, when handling login or register action fails with certificate error, then emits error`() = runTest {
+        viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
+        fakeDirectLoginUseCase.givenFailureResult(A_DIRECT_LOGIN, config = null, cause = AN_UNRECOGNISED_CERTIFICATE_ERROR)
+        givenInitialisesSession(fakeSession)
+        val test = viewModel.test()
+
+        viewModel.handle(A_DIRECT_LOGIN)
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(isLoading = false) }
+                )
+                .assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(A_DIRECT_LOGIN, AN_UNRECOGNISED_CERTIFICATE_ERROR))
+                .finish()
+    }
+
     @Test
     fun `when handling SignUp then sets sign mode to sign up and starts registration`() = runTest {
         givenRegistrationResultFor(RegisterAction.StartRegistration, ANY_CONTINUING_REGISTRATION_RESULT)
@@ -406,7 +429,7 @@ class OnboardingViewModelTest {
     @Test
     fun `given unavailable deeplink, when selecting homeserver, then emits failure with default homeserver as retry action`() = runTest {
         fakeContext.givenHasConnection()
-        fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, A_HOMESERVER_CONFIG)
+        fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
         fakeStartAuthenticationFlowUseCase.givenHomeserverUnavailable(A_HOMESERVER_CONFIG)
         val test = viewModel.test()
 
@@ -548,6 +571,44 @@ class OnboardingViewModelTest {
                 .finish()
     }
 
+    @Test
+    fun `when editing homeserver errors with certificate error, then emits error`() = runTest {
+        fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
+        fakeStartAuthenticationFlowUseCase.givenErrors(A_HOMESERVER_CONFIG, AN_UNRECOGNISED_CERTIFICATE_ERROR)
+        val editAction = OnboardingAction.HomeServerChange.EditHomeServer(A_HOMESERVER_URL)
+        val test = viewModel.test()
+
+        viewModel.handle(editAction)
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(isLoading = false) }
+                )
+                .assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(editAction, AN_UNRECOGNISED_CERTIFICATE_ERROR))
+                .finish()
+    }
+
+    @Test
+    fun `when selecting homeserver errors with certificate error, then emits error`() = runTest {
+        fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
+        fakeStartAuthenticationFlowUseCase.givenErrors(A_HOMESERVER_CONFIG, AN_UNRECOGNISED_CERTIFICATE_ERROR)
+        val selectAction = OnboardingAction.HomeServerChange.SelectHomeServer(A_HOMESERVER_URL)
+        val test = viewModel.test()
+
+        viewModel.handle(selectAction)
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(isLoading = false) }
+                )
+                .assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(selectAction, AN_UNRECOGNISED_CERTIFICATE_ERROR))
+                .finish()
+    }
+
     @Test
     fun `given unavailable full matrix id, when a register username is entered, then emits availability error`() = runTest {
         viewModelWith(initialRegistrationState("ignored-url"))
@@ -724,6 +785,76 @@ class OnboardingViewModelTest {
                 .finish()
     }
 
+    @Test
+    fun `given in sign in mode, when accepting user certificate with SelectHomeserver retry action, then emits OnHomeserverEdited`() = runTest {
+        viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignIn))
+        val test = viewModel.test()
+        fakeVectorFeatures.givenCombinedLoginEnabled()
+        givenCanSuccessfullyUpdateHomeserver(
+                A_HOMESERVER_URL,
+                SELECTED_HOMESERVER_STATE,
+                config = A_HOMESERVER_CONFIG.copy(allowedFingerprints = listOf(A_FINGERPRINT)),
+                fingerprint = A_FINGERPRINT,
+        )
+
+        viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, OnboardingAction.HomeServerChange.SelectHomeServer(A_HOMESERVER_URL)))
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(selectedHomeserver = SELECTED_HOMESERVER_STATE) },
+                        { copy(signMode = SignMode.SignIn) },
+                        { copy(isLoading = false) }
+                )
+                .assertEvents(OnboardingViewEvents.OpenCombinedLogin)
+                .finish()
+    }
+
+    @Test
+    fun `given in sign up mode, when accepting user certificate with EditHomeserver retry action, then emits OnHomeserverEdited`() = runTest {
+        viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignUp))
+        givenCanSuccessfullyUpdateHomeserver(
+                A_HOMESERVER_URL,
+                SELECTED_HOMESERVER_STATE,
+                config = A_HOMESERVER_CONFIG.copy(allowedFingerprints = listOf(A_FINGERPRINT)),
+                fingerprint = A_FINGERPRINT,
+        )
+        val test = viewModel.test()
+
+        viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, OnboardingAction.HomeServerChange.EditHomeServer(A_HOMESERVER_URL)))
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(selectedHomeserver = SELECTED_HOMESERVER_STATE) },
+                        { copy(isLoading = false) }
+
+                )
+                .assertEvents(OnboardingViewEvents.OnHomeserverEdited)
+                .finish()
+    }
+
+    @Test
+    fun `given DirectLogin retry action, when accepting user certificate, then logs in directly`() = runTest {
+        fakeHomeServerConnectionConfigFactory.givenConfigFor("https://dummy.org", A_FINGERPRINT, A_HOMESERVER_CONFIG)
+        fakeDirectLoginUseCase.givenSuccessResult(A_DIRECT_LOGIN, config = A_HOMESERVER_CONFIG, result = fakeSession)
+        givenInitialisesSession(fakeSession)
+        val test = viewModel.test()
+
+        viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, A_DIRECT_LOGIN))
+
+        test
+                .assertStatesChanges(
+                        initialState,
+                        { copy(isLoading = true) },
+                        { copy(isLoading = false) }
+                )
+                .assertEvents(OnboardingViewEvents.OnAccountSignedIn)
+                .finish()
+    }
+
     @Test
     fun `given can successfully start password reset, when resetting password, then emits confirmation email sent`() = runTest {
         viewModelWith(initialState.copy(selectedHomeserver = SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES))
@@ -991,15 +1122,20 @@ class OnboardingViewModelTest {
         fakeRegistrationActionHandler.givenResultsFor(results)
     }
 
-    private fun givenCanSuccessfullyUpdateHomeserver(homeserverUrl: String, resultingState: SelectedHomeserverState) {
-        fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, A_HOMESERVER_CONFIG)
-        fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
+    private fun givenCanSuccessfullyUpdateHomeserver(
+            homeserverUrl: String,
+            resultingState: SelectedHomeserverState,
+            config: HomeServerConnectionConfig = A_HOMESERVER_CONFIG,
+            fingerprint: Fingerprint? = null,
+    ) {
+        fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, fingerprint, config)
+        fakeStartAuthenticationFlowUseCase.givenResult(config, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
         givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.StartRegistration)
-        fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
+        fakeHomeServerHistoryService.expectUrlToBeAdded(config.homeServerUri.toString())
     }
 
     private fun givenUpdatingHomeserverErrors(homeserverUrl: String, resultingState: SelectedHomeserverState, error: Throwable) {
-        fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, A_HOMESERVER_CONFIG)
+        fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, fingerprint = null, A_HOMESERVER_CONFIG)
         fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
         givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.Error(error))
         fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCaseTest.kt
index 48cd32c84d..d15a6cf042 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/StartAuthenticationFlowUseCaseTest.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.onboarding
 
 import im.vector.app.features.login.LoginMode
+import im.vector.app.features.login.SsoState
 import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
 import im.vector.app.test.fakes.FakeAuthenticationService
 import im.vector.app.test.fakes.FakeUri
@@ -32,7 +33,11 @@ import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 
 private const val A_DECLARED_HOMESERVER_URL = "https://foo.bar"
 private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(homeServerUri = FakeUri().instance)
-private val SSO_IDENTITY_PROVIDERS = emptyList<SsoIdentityProvider>()
+private val FALLBACK_SSO_IDENTITY_PROVIDERS = emptyList<SsoIdentityProvider>()
+private val SSO_IDENTITY_PROVIDERS = listOf(SsoIdentityProvider(id = "id", "name", null, "sso-brand"))
+private val SSO_LOGIN_TYPE = listOf(LoginFlowTypes.SSO)
+private val SSO_AND_PASSWORD_LOGIN_TYPES = listOf(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD)
+private val PASSWORD_LOGIN_TYPE = listOf(LoginFlowTypes.PASSWORD)
 
 class StartAuthenticationFlowUseCaseTest {
 
@@ -47,7 +52,7 @@ class StartAuthenticationFlowUseCaseTest {
 
     @Test
     fun `given empty login result when starting authentication flow then returns empty result`() = runTest {
-        val loginResult = aLoginResult()
+        val loginResult = aLoginResult(supportedLoginTypes = emptyList())
         fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
 
         val result = useCase.execute(A_HOMESERVER_CONFIG)
@@ -57,55 +62,81 @@ class StartAuthenticationFlowUseCaseTest {
     }
 
     @Test
-    fun `given login supports SSO and Password when starting authentication flow then prefers SsoAndPassword`() = runTest {
-        val supportedLoginTypes = listOf(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD)
-        val loginResult = aLoginResult(supportedLoginTypes = supportedLoginTypes)
+    fun `given empty sso providers and login supports SSO and Password when starting authentication flow then prefers fallback SsoAndPassword`() = runTest {
+        val loginResult = aLoginResult(supportedLoginTypes = SSO_AND_PASSWORD_LOGIN_TYPES, ssoProviders = emptyList())
         fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
 
         val result = useCase.execute(A_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo expectedResult(
-                supportedLoginTypes = supportedLoginTypes,
-                preferredLoginMode = LoginMode.SsoAndPassword(SSO_IDENTITY_PROVIDERS),
+                supportedLoginTypes = SSO_AND_PASSWORD_LOGIN_TYPES,
+                preferredLoginMode = LoginMode.SsoAndPassword(SsoState.Fallback),
         )
         verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
     }
 
     @Test
-    fun `given login supports SSO when starting authentication flow then prefers Sso`() = runTest {
-        val supportedLoginTypes = listOf(LoginFlowTypes.SSO)
-        val loginResult = aLoginResult(supportedLoginTypes = supportedLoginTypes)
+    fun `given sso providers and login supports SSO and Password when starting authentication flow then prefers SsoAndPassword`() = runTest {
+        val loginResult = aLoginResult(supportedLoginTypes = SSO_AND_PASSWORD_LOGIN_TYPES, ssoProviders = SSO_IDENTITY_PROVIDERS)
         fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
 
         val result = useCase.execute(A_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo expectedResult(
-                supportedLoginTypes = supportedLoginTypes,
-                preferredLoginMode = LoginMode.Sso(SSO_IDENTITY_PROVIDERS),
+                supportedLoginTypes = SSO_AND_PASSWORD_LOGIN_TYPES,
+                preferredLoginMode = LoginMode.SsoAndPassword(SsoState.IdentityProviders(SSO_IDENTITY_PROVIDERS)),
+        )
+        verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
+    }
+
+    @Test
+    fun `given empty sso providers and login supports SSO when starting authentication flow then prefers fallback Sso`() = runTest {
+        val loginResult = aLoginResult(supportedLoginTypes = SSO_LOGIN_TYPE, ssoProviders = emptyList())
+        fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
+
+        val result = useCase.execute(A_HOMESERVER_CONFIG)
+
+        result shouldBeEqualTo expectedResult(
+                supportedLoginTypes = SSO_LOGIN_TYPE,
+                preferredLoginMode = LoginMode.Sso(SsoState.Fallback),
+        )
+        verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
+    }
+
+    @Test
+    fun `given identity providers and login supports SSO when starting authentication flow then prefers Sso`() = runTest {
+        val loginResult = aLoginResult(supportedLoginTypes = SSO_LOGIN_TYPE, ssoProviders = SSO_IDENTITY_PROVIDERS)
+        fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
+
+        val result = useCase.execute(A_HOMESERVER_CONFIG)
+
+        result shouldBeEqualTo expectedResult(
+                supportedLoginTypes = SSO_LOGIN_TYPE,
+                preferredLoginMode = LoginMode.Sso(SsoState.IdentityProviders(SSO_IDENTITY_PROVIDERS)),
         )
         verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
     }
 
     @Test
     fun `given login supports Password when starting authentication flow then prefers Password`() = runTest {
-        val supportedLoginTypes = listOf(LoginFlowTypes.PASSWORD)
-        val loginResult = aLoginResult(supportedLoginTypes = supportedLoginTypes)
+        val loginResult = aLoginResult(supportedLoginTypes = PASSWORD_LOGIN_TYPE)
         fakeAuthenticationService.givenLoginFlow(A_HOMESERVER_CONFIG, loginResult)
 
         val result = useCase.execute(A_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo expectedResult(
-                supportedLoginTypes = supportedLoginTypes,
+                supportedLoginTypes = PASSWORD_LOGIN_TYPE,
                 preferredLoginMode = LoginMode.Password,
         )
         verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
     }
 
     private fun aLoginResult(
-            supportedLoginTypes: List<String> = emptyList()
+            supportedLoginTypes: List<String>,
+            ssoProviders: List<SsoIdentityProvider> = FALLBACK_SSO_IDENTITY_PROVIDERS
     ) = LoginFlowResult(
             supportedLoginTypes = supportedLoginTypes,
-            ssoIdentityProviders = SSO_IDENTITY_PROVIDERS,
+            ssoIdentityProviders = ssoProviders,
             isLoginAndRegistrationSupported = true,
             homeServerUrl = A_DECLARED_HOMESERVER_URL,
             isOutdatedHomeserver = false,
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeHomeServerConnectionConfigFactory.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeHomeServerConnectionConfigFactory.kt
index 553a35ad8c..c0cfe5375b 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeHomeServerConnectionConfigFactory.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeHomeServerConnectionConfigFactory.kt
@@ -20,11 +20,12 @@ import im.vector.app.features.login.HomeServerConnectionConfigFactory
 import io.mockk.every
 import io.mockk.mockk
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.network.ssl.Fingerprint
 
 class FakeHomeServerConnectionConfigFactory {
     val instance: HomeServerConnectionConfigFactory = mockk()
 
-    fun givenConfigFor(url: String, config: HomeServerConnectionConfig) {
-        every { instance.create(url) } returns config
+    fun givenConfigFor(url: String, fingerprint: Fingerprint? = null, config: HomeServerConnectionConfig) {
+        every { instance.create(url, fingerprint) } returns config
     }
 }
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeStartAuthenticationFlowUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeStartAuthenticationFlowUseCase.kt
index bfbef9e565..4b2709facc 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeStartAuthenticationFlowUseCase.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeStartAuthenticationFlowUseCase.kt
@@ -31,6 +31,10 @@ class FakeStartAuthenticationFlowUseCase {
         coEvery { instance.execute(config) } returns result
     }
 
+    fun givenErrors(config: HomeServerConnectionConfig, error: Throwable) {
+        coEvery { instance.execute(config) } throws  error
+    }
+
     fun givenHomeserverUnavailable(config: HomeServerConnectionConfig) {
         coEvery { instance.execute(config) } throws aHomeserverUnavailableError()
     }
diff --git a/vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt b/vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt
index 0f44976ab3..8437401294 100644
--- a/vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt
+++ b/vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt
@@ -18,6 +18,7 @@ package im.vector.app.test.fixtures
 
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.MatrixError
+import org.matrix.android.sdk.api.network.ssl.Fingerprint
 import java.net.UnknownHostException
 import javax.net.ssl.HttpsURLConnection
 
@@ -38,3 +39,5 @@ fun aLoginEmailUnknownError() = Failure.ServerError(
 )
 
 fun aHomeserverUnavailableError() = Failure.NetworkConnection(UnknownHostException())
+
+fun anUnrecognisedCertificateError() = Failure.UnrecognizedCertificateFailure("a-url", Fingerprint(ByteArray(1), Fingerprint.HashType.SHA1))