From 9b332f7a32f0d81126c94c481b5997da24f46724 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 14 Dec 2020 17:32:54 +0300
Subject: [PATCH 01/41] Test message decryption in a room with 3 members.

---
 .../timeline/TimelineWithManyMembersTest.kt   | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
new file mode 100644
index 0000000000..a0271cb5b9
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.session.room.timeline
+
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.runners.MethodSorters
+import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.message.MessageContent
+import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
+import org.matrix.android.sdk.common.CommonTestHelper
+import org.matrix.android.sdk.common.CryptoTestHelper
+import java.util.concurrent.CountDownLatch
+
+@RunWith(JUnit4::class)
+@FixMethodOrder(MethodSorters.JVM)
+class TimelineWithManyMembersTest : InstrumentedTest {
+
+    private val commonTestHelper = CommonTestHelper(context())
+    private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
+
+    /**
+     * Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
+     */
+    @Test
+    fun everyoneShouldDecryptMessage3Members() {
+        val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobAndSamInARoom()
+
+        val aliceSession = cryptoTestData.firstSession
+        val bobSession = cryptoTestData.secondSession!!
+        val samSession = cryptoTestData.thirdSession!!
+
+        val aliceRoomId = cryptoTestData.roomId
+
+        aliceSession.cryptoService().setWarnOnUnknownDevices(false)
+        bobSession.cryptoService().setWarnOnUnknownDevices(false)
+        samSession.cryptoService().setWarnOnUnknownDevices(false)
+
+        val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
+        val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
+        val roomFromSamPOV = samSession.getRoom(aliceRoomId)!!
+
+        val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
+        val samTimeline = roomFromSamPOV.createTimeline(null, TimelineSettings(30))
+        bobTimeline.start()
+        samTimeline.start()
+
+        val firstMessage = "First messages from Alice"
+        commonTestHelper.sendTextMessage(
+                roomFromAlicePOV,
+                firstMessage,
+                1)
+
+        bobSession.startSync(true)
+        run {
+            val lock = CountDownLatch(1)
+            val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
+                snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse()
+            }
+            bobTimeline.addListener(eventsListener)
+            commonTestHelper.await(lock)
+        }
+        bobSession.stopSync()
+
+        samSession.startSync(true)
+        run {
+            val lock = CountDownLatch(1)
+            val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
+                snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse()
+            }
+            samTimeline.addListener(eventsListener)
+            commonTestHelper.await(lock)
+        }
+        samSession.stopSync()
+    }
+}

From 7e4725c091246c3bb97fb7e45863d31a3b637f1c Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 14 Dec 2020 18:07:20 +0300
Subject: [PATCH 02/41] Update CryptoTestData to handle more than 3 sessions.

---
 .../android/sdk/common/CryptoTestData.kt      | 21 ++++++++++++-------
 .../android/sdk/common/CryptoTestHelper.kt    |  6 +++---
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
index 76e59d9a90..e01aaef639 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
@@ -18,14 +18,21 @@ package org.matrix.android.sdk.common
 
 import org.matrix.android.sdk.api.session.Session
 
-data class CryptoTestData(val firstSession: Session,
-                          val roomId: String,
-                          val secondSession: Session? = null,
-                          val thirdSession: Session? = null) {
+data class CryptoTestData(val roomId: String,
+                          val sessions: List<Session> = emptyList()) {
+
+    val firstSession: Session
+        get() = sessions.first()
+
+    val secondSession: Session?
+        get() = sessions.getOrNull(1)
+
+    val thirdSession: Session?
+        get() = sessions.getOrNull(2)
 
     fun cleanUp(testHelper: CommonTestHelper) {
-        testHelper.signOutAndClose(firstSession)
-        secondSession?.let { testHelper.signOutAndClose(it) }
-        thirdSession?.let { testHelper.signOutAndClose(it) }
+        sessions.forEach {
+            testHelper.signOutAndClose(it)
+        }
     }
 }
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index cbb22daf0f..f108cbb105 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -73,7 +73,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
 
-        return CryptoTestData(aliceSession, roomId)
+        return CryptoTestData(roomId, listOf(aliceSession))
     }
 
     /**
@@ -139,7 +139,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
 //        assertNotNull(roomFromBobPOV.powerLevels)
 //        assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
 
-        return CryptoTestData(aliceSession, aliceRoomId, bobSession)
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
     }
 
     /**
@@ -157,7 +157,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
         // wait the initial sync
         SystemClock.sleep(1000)
 
-        return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
     }
 
     /**

From 427dc784fe2d1f131be8e29f930efb70fb74a4e6 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 15 Dec 2020 16:50:49 +0300
Subject: [PATCH 03/41] Support testing a room with many members.

---
 .../android/sdk/common/CryptoTestData.kt      |  2 +-
 .../android/sdk/common/CryptoTestHelper.kt    | 33 ++++++++++++--
 .../timeline/TimelineWithManyMembersTest.kt   | 43 +++++++++++++++++++
 3 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
index e01aaef639..693ab28da1 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.common
 import org.matrix.android.sdk.api.session.Session
 
 data class CryptoTestData(val roomId: String,
-                          val sessions: List<Session> = emptyList()) {
+                          val sessions: MutableList<Session> = mutableListOf()) {
 
     val firstSession: Session
         get() = sessions.first()
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index f108cbb105..66fc1348d5 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -73,7 +73,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
 
-        return CryptoTestData(roomId, listOf(aliceSession))
+        return CryptoTestData(roomId, mutableListOf(aliceSession))
     }
 
     /**
@@ -139,7 +139,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
 //        assertNotNull(roomFromBobPOV.powerLevels)
 //        assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
 
-        return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
+        return CryptoTestData(aliceRoomId, mutableListOf(aliceSession, bobSession))
     }
 
     /**
@@ -157,7 +157,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
         // wait the initial sync
         SystemClock.sleep(1000)
 
-        return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
+        return CryptoTestData(aliceRoomId, mutableListOf(aliceSession, cryptoTestData.secondSession!!, samSession))
     }
 
     /**
@@ -381,4 +381,31 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
     }
+
+    fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
+        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
+        aliceSession.cryptoService().setWarnOnUnknownDevices(false)
+
+        val roomId = mTestHelper.doSync<String> {
+            aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
+        }
+        val room = aliceSession.getRoom(roomId)!!
+
+        mTestHelper.runBlockingTest {
+            room.enableEncryption()
+        }
+
+        val cryptoTestData = CryptoTestData(roomId, mutableListOf(aliceSession))
+        for (index in 1 until numberOfMembers) {
+            mTestHelper
+                    .createAccount("User_$index", defaultSessionParams)
+                    .also { session -> mTestHelper.doSync<Unit> { room.invite(session.myUserId, null, it) } }
+                    .also { println("TEST -> " + it.myUserId + " invited") }
+                    .also { session -> mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) } }
+                    .also { println("TEST -> " + it.myUserId + " joined") }
+                    .also { session -> cryptoTestData.sessions.add(session) }
+        }
+
+        return cryptoTestData
+    }
 }
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
index a0271cb5b9..1a397c1d1c 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -16,6 +16,8 @@
 
 package org.matrix.android.sdk.session.room.timeline
 
+import android.os.SystemClock
+import android.util.Log
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -29,11 +31,14 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import org.matrix.android.sdk.common.CommonTestHelper
 import org.matrix.android.sdk.common.CryptoTestHelper
 import java.util.concurrent.CountDownLatch
+import kotlin.test.assertEquals
 
 @RunWith(JUnit4::class)
 @FixMethodOrder(MethodSorters.JVM)
 class TimelineWithManyMembersTest : InstrumentedTest {
 
+    private val NUMBER_OF_MEMBERS = 5
+
     private val commonTestHelper = CommonTestHelper(context())
     private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
 
@@ -91,4 +96,42 @@ class TimelineWithManyMembersTest : InstrumentedTest {
         }
         samSession.stopSync()
     }
+
+    /**
+     * Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
+     */
+    @Test
+    fun everyone_should_decrypt_message_in_a_crowded_room() {
+        val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
+
+        val sessionForFirstMember = cryptoTestData.firstSession
+        val roomForFirstMember = sessionForFirstMember.getRoom(cryptoTestData.roomId)!!
+
+        val firstMessage = "First messages from Alice"
+        commonTestHelper.sendTextMessage(
+                roomForFirstMember,
+                firstMessage,
+                1)
+
+        for (index in 1 until cryptoTestData.sessions.size) {
+            val session = cryptoTestData.sessions[index]
+            val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
+            val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30))
+            timelineForCurrentMember.start()
+
+            session.startSync(true)
+
+            run {
+                val lock = CountDownLatch(1)
+                val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
+                    val decryptedMessage = snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body
+                    println("Decrypted Message: $decryptedMessage")
+                    return@createEventListener decryptedMessage?.startsWith(firstMessage).orFalse()
+                }
+                timelineForCurrentMember.addListener(eventsListener)
+                commonTestHelper.await(lock)
+            }
+            session.stopSync()
+        }
+    }
 }

From b263273c878806ba3f474380f438349c7f8b70ac Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 16 Dec 2020 13:45:26 +0300
Subject: [PATCH 04/41] Improve test with detailed CryptoError message.

---
 .../android/sdk/common/CommonTestHelper.kt    |  4 ++--
 .../android/sdk/common/CryptoTestHelper.kt    |  2 +-
 .../timeline/TimelineWithManyMembersTest.kt   | 23 ++++++++++++-------
 3 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
index 0e7088a6a5..7dcf43ba0f 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
@@ -119,7 +119,7 @@ class CommonTestHelper(context: Context) {
      * @param message      the message to send
      * @param nbOfMessages the number of time the message will be sent
      */
-    fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
+    fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
         val timeline = room.createTimeline(null, TimelineSettings(10))
         val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
         val latch = CountDownLatch(1)
@@ -151,7 +151,7 @@ class CommonTestHelper(context: Context) {
             room.sendTextMessage(message + " #" + (i + 1))
         }
         // Wait 3 second more per message
-        await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
+        await(latch, timeout = timeout + 3_000L * nbOfMessages)
         timeline.dispose()
 
         // Check that all events has been created
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index 66fc1348d5..5d3407dde1 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -399,7 +399,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
         for (index in 1 until numberOfMembers) {
             mTestHelper
                     .createAccount("User_$index", defaultSessionParams)
-                    .also { session -> mTestHelper.doSync<Unit> { room.invite(session.myUserId, null, it) } }
+                    .also { session -> mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) } }
                     .also { println("TEST -> " + it.myUserId + " invited") }
                     .also { session -> mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) } }
                     .also { println("TEST -> " + it.myUserId + " joined") }
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
index 1a397c1d1c..dacde121b3 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -16,8 +16,6 @@
 
 package org.matrix.android.sdk.session.room.timeline
 
-import android.os.SystemClock
-import android.util.Log
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -31,7 +29,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import org.matrix.android.sdk.common.CommonTestHelper
 import org.matrix.android.sdk.common.CryptoTestHelper
 import java.util.concurrent.CountDownLatch
-import kotlin.test.assertEquals
+import kotlin.test.fail
 
 @RunWith(JUnit4::class)
 @FixMethodOrder(MethodSorters.JVM)
@@ -111,7 +109,9 @@ class TimelineWithManyMembersTest : InstrumentedTest {
         commonTestHelper.sendTextMessage(
                 roomForFirstMember,
                 firstMessage,
-                1)
+                1,
+                600_000
+        )
 
         for (index in 1 until cryptoTestData.sessions.size) {
             val session = cryptoTestData.sessions[index]
@@ -124,12 +124,19 @@ class TimelineWithManyMembersTest : InstrumentedTest {
             run {
                 val lock = CountDownLatch(1)
                 val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
-                    val decryptedMessage = snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body
-                    println("Decrypted Message: $decryptedMessage")
-                    return@createEventListener decryptedMessage?.startsWith(firstMessage).orFalse()
+                    snapshot
+                            .find { it.isEncrypted() }
+                            ?.let {
+                                val body = it.root.getClearContent()?.toModel<MessageContent>()?.body
+                                if (body?.startsWith(firstMessage).orFalse()) {
+                                    return@createEventListener true
+                                } else {
+                                    fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
+                                }
+                            } ?: return@createEventListener false
                 }
                 timelineForCurrentMember.addListener(eventsListener)
-                commonTestHelper.await(lock)
+                commonTestHelper.await(lock, 600_000)
             }
             session.stopSync()
         }

From 7b97981bb57ad7b651700ecacc878f4c1324967f Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 16 Dec 2020 15:45:50 +0300
Subject: [PATCH 05/41] Make sure to load all members in the room before
 sending the event.

---
 .../android/sdk/common/CommonTestHelper.kt    | 10 ++--
 .../timeline/TimelineWithManyMembersTest.kt   | 58 +------------------
 .../internal/crypto/tasks/SendEventTask.kt    |  9 +++
 3 files changed, 16 insertions(+), 61 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
index 7dcf43ba0f..cb49ee8818 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
@@ -86,7 +86,7 @@ class CommonTestHelper(context: Context) {
      *
      * @param session    the session to sync
      */
-    fun syncSession(session: Session) {
+    fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
         val lock = CountDownLatch(1)
 
         val job = GlobalScope.launch(Dispatchers.Main) {
@@ -109,7 +109,7 @@ class CommonTestHelper(context: Context) {
         }
         GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
 
-        await(lock)
+        await(lock, timeout)
     }
 
     /**
@@ -215,14 +215,14 @@ class CommonTestHelper(context: Context) {
                     .getLoginFlow(hs, it)
         }
 
-        doSync<RegistrationResult> {
+        doSync<RegistrationResult>(timeout = 60_000) {
             matrix.authenticationService
                     .getRegistrationWizard()
                     .createAccount(userName, password, null, it)
         }
 
         // Perform dummy step
-        val registrationResult = doSync<RegistrationResult> {
+        val registrationResult = doSync<RegistrationResult>(timeout = 60_000) {
             matrix.authenticationService
                     .getRegistrationWizard()
                     .dummy(it)
@@ -231,7 +231,7 @@ class CommonTestHelper(context: Context) {
         assertTrue(registrationResult is RegistrationResult.Success)
         val session = (registrationResult as RegistrationResult.Success).session
         if (sessionTestParams.withInitialSync) {
-            syncSession(session)
+            syncSession(session, 60_000)
         }
 
         return session
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
index dacde121b3..ca74acdee7 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -35,66 +35,11 @@ import kotlin.test.fail
 @FixMethodOrder(MethodSorters.JVM)
 class TimelineWithManyMembersTest : InstrumentedTest {
 
-    private val NUMBER_OF_MEMBERS = 5
+    private val NUMBER_OF_MEMBERS = 6
 
     private val commonTestHelper = CommonTestHelper(context())
     private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
 
-    /**
-     * Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
-     */
-    @Test
-    fun everyoneShouldDecryptMessage3Members() {
-        val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobAndSamInARoom()
-
-        val aliceSession = cryptoTestData.firstSession
-        val bobSession = cryptoTestData.secondSession!!
-        val samSession = cryptoTestData.thirdSession!!
-
-        val aliceRoomId = cryptoTestData.roomId
-
-        aliceSession.cryptoService().setWarnOnUnknownDevices(false)
-        bobSession.cryptoService().setWarnOnUnknownDevices(false)
-        samSession.cryptoService().setWarnOnUnknownDevices(false)
-
-        val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
-        val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
-        val roomFromSamPOV = samSession.getRoom(aliceRoomId)!!
-
-        val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
-        val samTimeline = roomFromSamPOV.createTimeline(null, TimelineSettings(30))
-        bobTimeline.start()
-        samTimeline.start()
-
-        val firstMessage = "First messages from Alice"
-        commonTestHelper.sendTextMessage(
-                roomFromAlicePOV,
-                firstMessage,
-                1)
-
-        bobSession.startSync(true)
-        run {
-            val lock = CountDownLatch(1)
-            val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
-                snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse()
-            }
-            bobTimeline.addListener(eventsListener)
-            commonTestHelper.await(lock)
-        }
-        bobSession.stopSync()
-
-        samSession.startSync(true)
-        run {
-            val lock = CountDownLatch(1)
-            val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
-                snapshot.firstOrNull()?.root?.getClearContent()?.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse()
-            }
-            samTimeline.addListener(eventsListener)
-            commonTestHelper.await(lock)
-        }
-        samSession.stopSync()
-    }
-
     /**
      * Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
      */
@@ -129,6 +74,7 @@ class TimelineWithManyMembersTest : InstrumentedTest {
                             ?.let {
                                 val body = it.root.getClearContent()?.toModel<MessageContent>()?.body
                                 if (body?.startsWith(firstMessage).orFalse()) {
+                                    println("User " + session.myUserId + " decrypted as " + body)
                                     return@createEventListener true
                                 } else {
                                     fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
index 8b739c4b64..5c8c7dfb25 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
@@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
 import org.matrix.android.sdk.internal.session.room.send.SendResponse
 import org.matrix.android.sdk.internal.task.Task
@@ -35,11 +36,19 @@ internal interface SendEventTask : Task<SendEventTask.Params, String> {
 internal class DefaultSendEventTask @Inject constructor(
         private val localEchoRepository: LocalEchoRepository,
         private val encryptEventTask: DefaultEncryptEventTask,
+        private val loadRoomMembersTask: LoadRoomMembersTask,
         private val roomAPI: RoomAPI,
         private val eventBus: EventBus) : SendEventTask {
 
     override suspend fun execute(params: SendEventTask.Params): String {
         try {
+            // Make sure to load all members in the room before sending the event.
+            params.event.roomId
+                    ?.takeIf { params.encrypt }
+                    ?.let { roomId ->
+                        loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
+                    }
+
             val event = handleEncryption(params)
             val localId = event.eventId!!
 

From 80396fcd391d195be206dedd914115fd6bc8722c Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 16 Dec 2020 15:46:14 +0300
Subject: [PATCH 06/41] Changelog added.

---
 CHANGES.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGES.md b/CHANGES.md
index 0fb7d72c13..9ab5276ff3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -57,6 +57,7 @@ Bugfix 🐛:
  - No known servers error is given when joining rooms on new Gitter bridge (#2516)
  - Show preview when sending attachment from the keyboard (#2440)
  - Do not compress GIFs (#1616, #1254)
+ - Wait for all room members to be known before sending a message to a e2e room (#2518)
 
 SDK API changes ⚠️:
  - StateService now exposes suspendable function instead of using MatrixCallback.

From 938cd32ddd97a3608a1497d0cf83b97150a33a3a Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 17 Dec 2020 18:25:40 +0300
Subject: [PATCH 07/41] Do not load room members if there is an ongoing
 request.

---
 .../database/RealmSessionStoreMigration.kt    | 18 ++++++++++++++-
 .../sdk/internal/database/model/RoomEntity.kt | 10 +++++++-
 .../model/RoomMembersLoadStatusType.java      | 23 +++++++++++++++++++
 .../room/membership/LoadRoomMembersTask.kt    |  5 ++--
 4 files changed, 52 insertions(+), 4 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index b970ec60e2..fd922ef0e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -21,6 +21,7 @@ import io.realm.RealmMigration
 import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
 import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
 import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
 import timber.log.Timber
 import javax.inject.Inject
@@ -28,7 +29,7 @@ import javax.inject.Inject
 class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
 
     companion object {
-        const val SESSION_STORE_SCHEMA_VERSION = 6L
+        const val SESSION_STORE_SCHEMA_VERSION = 7L
     }
 
     override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@@ -40,6 +41,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
         if (oldVersion <= 3) migrateTo4(realm)
         if (oldVersion <= 4) migrateTo5(realm)
         if (oldVersion <= 5) migrateTo6(realm)
+        if (oldVersion <= 6) migrateTo7(realm)
     }
 
     private fun migrateTo1(realm: DynamicRealm) {
@@ -105,4 +107,18 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
                 .addField(PreviewUrlCacheEntityFields.MXC_URL, String::class.java)
                 .addField(PreviewUrlCacheEntityFields.LAST_UPDATED_TIMESTAMP, Long::class.java)
     }
+
+    private fun migrateTo7(realm: DynamicRealm) {
+        Timber.d("Step 6 -> 7")
+        realm.schema.get("RoomEntity")
+                ?.addField("membersLoadStatusStr", String::class.java)
+                ?.transform { obj ->
+                    if (obj.getBoolean("areAllMembersLoaded")) {
+                        obj.setString("membersLoadStatusStr", RoomMembersLoadStatusType.LOADED.name)
+                    } else {
+                        obj.setString("membersLoadStatusStr", RoomMembersLoadStatusType.NONE.name)
+                    }
+                }
+                ?.removeField("areAllMembersLoaded")
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
index 9af1646a4c..a0a6060d36 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
@@ -24,7 +24,6 @@ import io.realm.annotations.PrimaryKey
 internal open class RoomEntity(@PrimaryKey var roomId: String = "",
                                var chunks: RealmList<ChunkEntity> = RealmList(),
                                var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList(),
-                               var areAllMembersLoaded: Boolean = false
 ) : RealmObject() {
 
     private var membershipStr: String = Membership.NONE.name
@@ -36,5 +35,14 @@ internal open class RoomEntity(@PrimaryKey var roomId: String = "",
             membershipStr = value.name
         }
 
+    private var membersLoadStatusStr: String = RoomMembersLoadStatusType.NONE.name
+    var membersLoadStatus: RoomMembersLoadStatusType
+        get() {
+            return RoomMembersLoadStatusType.valueOf(membersLoadStatusStr)
+        }
+        set(value) {
+            membersLoadStatusStr = value.name
+        }
+
     companion object
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java
new file mode 100644
index 0000000000..d7063082bb
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.database.model;
+
+public enum RoomMembersLoadStatusType {
+    NONE,
+    LOADING,
+    LOADED
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
index 627f927ad8..69530c5c0e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
@@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.util.awaitTransaction
 import io.realm.Realm
 import io.realm.kotlin.createObject
 import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import javax.inject.Inject
 
 internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit> {
@@ -84,14 +85,14 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
                 }
                 roomMemberEventHandler.handle(realm, roomId, roomMemberEvent)
             }
-            roomEntity.areAllMembersLoaded = true
+            roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADED
             roomSummaryUpdater.update(realm, roomId, updateMembers = true)
         }
     }
 
     private fun areAllMembersAlreadyLoaded(roomId: String): Boolean {
         return Realm.getInstance(monarchy.realmConfiguration).use {
-            RoomEntity.where(it, roomId).findFirst()?.areAllMembersLoaded ?: false
+            RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus == RoomMembersLoadStatusType.LOADED
         }
     }
 }

From 5d8f365520a0f48d94346d3e13b5fc431738e06c Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 17 Dec 2020 18:55:31 +0300
Subject: [PATCH 08/41] Load room members seamlessly when timeline is starting.

---
 .../sdk/internal/database/model/RoomEntity.kt        |  2 +-
 .../session/room/timeline/DefaultTimeline.kt         | 12 +++++++++++-
 .../session/room/timeline/DefaultTimelineService.kt  |  7 +++++--
 .../features/home/room/detail/RoomDetailViewModel.kt |  2 --
 4 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
index a0a6060d36..3ff2532604 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt
@@ -23,7 +23,7 @@ import io.realm.annotations.PrimaryKey
 
 internal open class RoomEntity(@PrimaryKey var roomId: String = "",
                                var chunks: RealmList<ChunkEntity> = RealmList(),
-                               var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList(),
+                               var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList()
 ) : RealmObject() {
 
     private var membershipStr: String = Membership.NONE.name
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 86b0497bd0..dd58529412 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -27,6 +27,7 @@ import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.NoOpMatrixCallback
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.events.model.EventType
@@ -53,6 +54,7 @@ import org.matrix.android.sdk.internal.database.query.filterEvents
 import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.database.query.whereRoomId
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.task.configureWith
 import org.matrix.android.sdk.internal.util.Debouncer
@@ -81,7 +83,8 @@ internal class DefaultTimeline(
         private val hiddenReadReceipts: TimelineHiddenReadReceipts,
         private val eventBus: EventBus,
         private val eventDecryptor: TimelineEventDecryptor,
-        private val realmSessionProvider: RealmSessionProvider
+        private val realmSessionProvider: RealmSessionProvider,
+        private val loadRoomMembersTask: LoadRoomMembersTask
 ) : Timeline, TimelineHiddenReadReceipts.Delegate {
 
     data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
@@ -184,6 +187,13 @@ internal class DefaultTimeline(
                 if (settings.shouldHandleHiddenReadReceipts()) {
                     hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this)
                 }
+
+                loadRoomMembersTask
+                        .configureWith(LoadRoomMembersTask.Params(roomId)) {
+                            this.callback = NoOpMatrixCallback()
+                        }
+                        .executeBy(taskExecutor)
+
                 isReady.set(true)
             }
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index 783aa53ddf..d02e906d00 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
 import org.matrix.android.sdk.internal.task.TaskExecutor
 
 internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
@@ -51,7 +52,8 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                                                                   private val paginationTask: PaginationTask,
                                                                   private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
                                                                   private val timelineEventMapper: TimelineEventMapper,
-                                                                  private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper
+                                                                  private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
+                                                                  private val loadRoomMembersTask: LoadRoomMembersTask
 ) : TimelineService {
 
     @AssistedInject.Factory
@@ -73,7 +75,8 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                 eventBus = eventBus,
                 eventDecryptor = eventDecryptor,
                 fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
-                realmSessionProvider = realmSessionProvider
+                realmSessionProvider = realmSessionProvider,
+                loadRoomMembersTask = loadRoomMembersTask
         )
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index e4e7177e4f..1e6e7c9d14 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -31,7 +31,6 @@ import im.vector.app.R
 import im.vector.app.core.extensions.exhaustive
 import im.vector.app.core.platform.VectorViewModel
 import im.vector.app.core.resources.StringProvider
-import im.vector.app.core.utils.subscribeLogError
 import im.vector.app.features.call.WebRtcPeerConnectionManager
 import im.vector.app.features.command.CommandParser
 import im.vector.app.features.command.ParsedCommand
@@ -168,7 +167,6 @@ class RoomDetailViewModel @AssistedInject constructor(
         observePowerLevel()
         room.getRoomSummaryLive()
         room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT, NoOpMatrixCallback())
-        room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
         // Inform the SDK that the room is displayed
         session.onRoomDisplayed(initialState.roomId)
         chatEffectManager.delegate = this

From 42a5680374ef8fd8f71ee85b5a9c7c198be970ea Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 18 Dec 2020 12:27:51 +0300
Subject: [PATCH 09/41] Fix copyright.

---
 .../sdk/session/room/timeline/TimelineWithManyMembersTest.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
index ca74acdee7..6dd139390c 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.

From 7732bd47cebbcaadb53c84615a491ce271cf25db Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:01:39 +0100
Subject: [PATCH 10/41] Update Changelog after release

---
 CHANGES.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 9ab5276ff3..38868ed822 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,7 +8,7 @@ Improvements 🙌:
  -
 
 Bugfix 🐛:
- -
+ - Wait for all room members to be known before sending a message to a e2e room (#2518)
 
 Translations 🗣:
  -
@@ -57,7 +57,6 @@ Bugfix 🐛:
  - No known servers error is given when joining rooms on new Gitter bridge (#2516)
  - Show preview when sending attachment from the keyboard (#2440)
  - Do not compress GIFs (#1616, #1254)
- - Wait for all room members to be known before sending a message to a e2e room (#2518)
 
 SDK API changes ⚠️:
  - StateService now exposes suspendable function instead of using MatrixCallback.

From ff8a20801253969911f7fde8faba3265cca58cb2 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:04:46 +0100
Subject: [PATCH 11/41] Change to immutable list

---
 .../org/matrix/android/sdk/common/CryptoTestData.kt  |  2 +-
 .../matrix/android/sdk/common/CryptoTestHelper.kt    | 12 ++++++------
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
index 693ab28da1..b6bedbd719 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.common
 import org.matrix.android.sdk.api.session.Session
 
 data class CryptoTestData(val roomId: String,
-                          val sessions: MutableList<Session> = mutableListOf()) {
+                          val sessions: List<Session>) {
 
     val firstSession: Session
         get() = sessions.first()
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index 5d3407dde1..f219986c49 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -73,7 +73,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             }
         }
 
-        return CryptoTestData(roomId, mutableListOf(aliceSession))
+        return CryptoTestData(roomId, listOf(aliceSession))
     }
 
     /**
@@ -139,7 +139,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
 //        assertNotNull(roomFromBobPOV.powerLevels)
 //        assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
 
-        return CryptoTestData(aliceRoomId, mutableListOf(aliceSession, bobSession))
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
     }
 
     /**
@@ -157,7 +157,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
         // wait the initial sync
         SystemClock.sleep(1000)
 
-        return CryptoTestData(aliceRoomId, mutableListOf(aliceSession, cryptoTestData.secondSession!!, samSession))
+        return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
     }
 
     /**
@@ -395,7 +395,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
             room.enableEncryption()
         }
 
-        val cryptoTestData = CryptoTestData(roomId, mutableListOf(aliceSession))
+        val sessions = mutableListOf(aliceSession)
         for (index in 1 until numberOfMembers) {
             mTestHelper
                     .createAccount("User_$index", defaultSessionParams)
@@ -403,9 +403,9 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
                     .also { println("TEST -> " + it.myUserId + " invited") }
                     .also { session -> mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) } }
                     .also { println("TEST -> " + it.myUserId + " joined") }
-                    .also { session -> cryptoTestData.sessions.add(session) }
+                    .also { session -> sessions.add(session) }
         }
 
-        return cryptoTestData
+        return CryptoTestData(roomId, sessions)
     }
 }

From 00b16db7cc823e8ef127fbc3e43d250cb428d03f Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:06:30 +0100
Subject: [PATCH 12/41] Simplification of code

---
 .../matrix/android/sdk/common/CryptoTestHelper.kt   | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index f219986c49..3d5856fc64 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -397,13 +397,12 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
 
         val sessions = mutableListOf(aliceSession)
         for (index in 1 until numberOfMembers) {
-            mTestHelper
-                    .createAccount("User_$index", defaultSessionParams)
-                    .also { session -> mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) } }
-                    .also { println("TEST -> " + it.myUserId + " invited") }
-                    .also { session -> mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) } }
-                    .also { println("TEST -> " + it.myUserId + " joined") }
-                    .also { session -> sessions.add(session) }
+            val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
+            mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) }
+            println("TEST -> " + session.myUserId + " invited")
+            mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) }
+            println("TEST -> " + session.myUserId + " joined")
+            sessions.add(session)
         }
 
         return CryptoTestData(roomId, sessions)

From 15597eb041899df40c582f2d9793fb9bf07d8845 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:10:36 +0100
Subject: [PATCH 13/41] Rename .java to .kt

---
 ...oomMembersLoadStatusType.java => RoomMembersLoadStatusType.kt} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/{RoomMembersLoadStatusType.java => RoomMembersLoadStatusType.kt} (100%)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.java
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt

From abf763f454ac7e89f1d6fe314c112394749c7ac2 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:10:36 +0100
Subject: [PATCH 14/41] Convert to internal Kotlin class

---
 .../sdk/internal/database/model/RoomMembersLoadStatusType.kt  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
index d7063082bb..79fe17253b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMembersLoadStatusType.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.database.model;
+package org.matrix.android.sdk.internal.database.model
 
-public enum RoomMembersLoadStatusType {
+internal enum class RoomMembersLoadStatusType {
     NONE,
     LOADING,
     LOADED

From b0ba62aa312c9847277f47489b7e892aa34cba3f Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:12:01 +0100
Subject: [PATCH 15/41] Use const

---
 .../sdk/internal/database/RealmSessionStoreMigration.kt        | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index fd922ef0e5..57002b5a60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -21,6 +21,7 @@ import io.realm.RealmMigration
 import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
 import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
 import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomEntityFields
 import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
 import timber.log.Timber
@@ -111,7 +112,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
     private fun migrateTo7(realm: DynamicRealm) {
         Timber.d("Step 6 -> 7")
         realm.schema.get("RoomEntity")
-                ?.addField("membersLoadStatusStr", String::class.java)
+                ?.addField(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, String::class.java)
                 ?.transform { obj ->
                     if (obj.getBoolean("areAllMembersLoaded")) {
                         obj.setString("membersLoadStatusStr", RoomMembersLoadStatusType.LOADED.name)

From ca4b91a98f3086a4ce167d9942023233e039aa0c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:38:27 +0100
Subject: [PATCH 16/41] Use the new RoomMembersLoadStatusType.LOADING value

---
 .../room/membership/LoadRoomMembersTask.kt    | 48 +++++++++++++++----
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
index 69530c5c0e..55dd747fdb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
@@ -17,12 +17,19 @@
 package org.matrix.android.sdk.internal.session.room.membership
 
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import io.realm.kotlin.createObject
+import kotlinx.coroutines.TimeoutCancellationException
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
 import org.matrix.android.sdk.internal.database.model.EventInsertType
 import org.matrix.android.sdk.internal.database.model.RoomEntity
+import org.matrix.android.sdk.internal.database.model.RoomEntityFields
+import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
 import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.database.query.where
@@ -33,10 +40,7 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
 import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import io.realm.Realm
-import io.realm.kotlin.createObject
-import org.greenrobot.eventbus.EventBus
-import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
 internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit> {
@@ -57,9 +61,33 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
 ) : LoadRoomMembersTask {
 
     override suspend fun execute(params: LoadRoomMembersTask.Params) {
-        if (areAllMembersAlreadyLoaded(params.roomId)) {
-            return
+        when (getRoomMembersLoadStatus(params.roomId)) {
+            RoomMembersLoadStatusType.NONE    -> doRequest(params)
+            RoomMembersLoadStatusType.LOADING -> waitPreviousRequestToFinish(params)
+            RoomMembersLoadStatusType.LOADED  -> Unit
         }
+    }
+
+    private suspend fun waitPreviousRequestToFinish(params: LoadRoomMembersTask.Params) {
+        try {
+            awaitNotEmptyResult(monarchy.realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
+                realm.where(RoomEntity::class.java)
+                        .equalTo(RoomEntityFields.ROOM_ID, params.roomId)
+                        .equalTo(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, RoomMembersLoadStatusType.LOADED.name)
+            }
+        } catch (exception: TimeoutCancellationException) {
+            // Timeout, do the request anyway (?)
+            doRequest(params)
+        }
+    }
+
+    private suspend fun doRequest(params: LoadRoomMembersTask.Params) {
+        monarchy.awaitTransaction { realm ->
+            val roomEntity = RoomEntity.where(realm, params.roomId).findFirst()
+                    ?: realm.createObject(params.roomId)
+            roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADING
+        }
+
         val lastToken = syncTokenStore.getLastToken()
         val response = executeRequest<RoomMembersResponse>(eventBus) {
             apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
@@ -90,9 +118,11 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
         }
     }
 
-    private fun areAllMembersAlreadyLoaded(roomId: String): Boolean {
-        return Realm.getInstance(monarchy.realmConfiguration).use {
-            RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus == RoomMembersLoadStatusType.LOADED
+    private fun getRoomMembersLoadStatus(roomId: String): RoomMembersLoadStatusType {
+        var result: RoomMembersLoadStatusType?
+        Realm.getInstance(monarchy.realmConfiguration).use {
+            result = RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus
         }
+        return result ?: RoomMembersLoadStatusType.NONE
     }
 }

From 3d291c04c981e6b70336dfe35ea1836190e5bf7e Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 18 Dec 2020 16:53:26 +0100
Subject: [PATCH 17/41] const -> companion

---
 .../sdk/session/room/timeline/TimelineWithManyMembersTest.kt  | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
index 6dd139390c..ff07cf1d1d 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineWithManyMembersTest.kt
@@ -35,7 +35,9 @@ import kotlin.test.fail
 @FixMethodOrder(MethodSorters.JVM)
 class TimelineWithManyMembersTest : InstrumentedTest {
 
-    private val NUMBER_OF_MEMBERS = 6
+    companion object {
+        private const val NUMBER_OF_MEMBERS = 6
+    }
 
     private val commonTestHelper = CommonTestHelper(context())
     private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)

From 13938f2ab3eb02e55e51ee9f924c7a52d047fd4e Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 21 Dec 2020 11:18:25 +0100
Subject: [PATCH 18/41] Let the Matrix SDK compute the SSO url

---
 .../sdk/api/auth/AuthenticationService.kt     |  5 +++
 .../matrix/android/sdk/api/auth/Constants.kt  |  6 +--
 .../android/sdk/api/util/UrlExtensions.kt     | 37 +++++++++++++++++++
 .../auth/DefaultAuthenticationService.kt      | 26 +++++++++++++
 .../app/core/extensions/UrlExtensions.kt      | 20 ----------
 .../login/AbstractSSOLoginFragment.kt         |  7 +++-
 .../app/features/login/LoginActivity.kt       |  3 ++
 .../app/features/login/LoginFragment.kt       |  7 +++-
 .../LoginSignUpSignInSelectionFragment.kt     | 15 ++++++--
 .../app/features/login/LoginViewModel.kt      |  4 ++
 .../app/features/login/LoginViewState.kt      | 27 --------------
 .../app/features/login/LoginWebFragment.kt    |  2 +-
 12 files changed, 103 insertions(+), 56 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 360b955869..3314b47ce9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -41,6 +41,11 @@ interface AuthenticationService {
      */
     fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
 
+    /**
+     * Get a SSO url
+     */
+    fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
+
     /**
      * Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
      */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
index 7d18aba627..d832caefde 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
@@ -32,7 +32,7 @@ const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
  * Path to use when the client want to connect using SSO
  * Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login
  */
-const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
-const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
+internal const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
+internal const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
 
-const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
+internal const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
new file mode 100644
index 0000000000..beaff2bdda
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.util
+
+import java.net.URLEncoder
+
+/**
+ * Append param and value to a Url, using "?" or "&". Value parameter will be encoded
+ * Return this for chaining purpose
+ */
+fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
+    if (contains("?")) {
+        append("&")
+    } else {
+        append("?")
+    }
+
+    append(param)
+    append("=")
+    append(URLEncoder.encode(value, "utf-8"))
+
+    return this
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 55f053de8d..51f6b6c155 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -23,6 +23,9 @@ import kotlinx.coroutines.withContext
 import okhttp3.OkHttpClient
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
+import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
+import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.data.LoginFlowResult
@@ -34,6 +37,7 @@ import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.util.Cancelable
 import org.matrix.android.sdk.api.util.NoOpCancellable
+import org.matrix.android.sdk.api.util.appendParamToUrl
 import org.matrix.android.sdk.internal.SessionManager
 import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
 import org.matrix.android.sdk.internal.auth.data.RiotConfig
@@ -99,6 +103,28 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
+    override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
+        return pendingSessionData?.let { safePendingSessionData ->
+            val homeServerUrl = safePendingSessionData.homeServerConnectionConfig.homeServerUri.toString()
+
+            buildString {
+                append(homeServerUrl.trim { it == '/' })
+                if (providerId != null) {
+                    append(MSC2858_SSO_REDIRECT_PATH)
+                    append("/$providerId")
+                } else {
+                    append(SSO_REDIRECT_PATH)
+                }
+                // Set a redirect url we will intercept later
+                appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
+                deviceId?.takeIf { it.isNotBlank() }?.let {
+                    // But https://github.com/matrix-org/synapse/issues/5755
+                    appendParamToUrl("device_id", it)
+                }
+            }
+        }
+    }
+
     override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
         pendingSessionData = null
 
diff --git a/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
index 38977d33ba..5037f78445 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/UrlExtensions.kt
@@ -16,26 +16,6 @@
 
 package im.vector.app.core.extensions
 
-import java.net.URLEncoder
-
-/**
- * Append param and value to a Url, using "?" or "&". Value parameter will be encoded
- * Return this for chaining purpose
- */
-fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
-    if (contains("?")) {
-        append("&")
-    } else {
-        append("?")
-    }
-
-    append(param)
-    append("=")
-    append(URLEncoder.encode(value, "utf-8"))
-
-    return this
-}
-
 /**
  * Ex: "https://matrix.org/" -> "matrix.org"
  */
diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
index c20f4ddd23..3fc5037ae7 100644
--- a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt
@@ -87,7 +87,12 @@ abstract class AbstractSSOLoginFragment<VB: ViewBinding> : AbstractLoginFragment
         withState(loginViewModel) { state ->
             if (state.loginMode.hasSso() && state.loginMode.ssoIdentityProviders().isNullOrEmpty()) {
                 // in this case we can prefetch (not other cases for privacy concerns)
-                prefetchUrl(state.getSsoUrl(null))
+                loginViewModel.getSsoUrl(
+                        redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                        deviceId = state.deviceId,
+                        providerId = null
+                )
+                        ?.let { prefetchUrl(it) }
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 503b6d74a6..803fd38983 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -360,6 +360,9 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarCo
 
         private const val EXTRA_CONFIG = "EXTRA_CONFIG"
 
+        // Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
+        const val VECTOR_REDIRECT_URL = "element://connect"
+
         fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
             return Intent(context, LoginActivity::class.java).apply {
                 putExtra(EXTRA_CONFIG, loginConfig)
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
index c396e61b1a..3b22e0f206 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
@@ -193,7 +193,12 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
                 views.loginSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders
                 views.loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
                     override fun onProviderSelected(id: String?) {
-                        openInCustomTab(state.getSsoUrl(id))
+                        loginViewModel.getSsoUrl(
+                                redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                                deviceId = state.deviceId,
+                                providerId = id
+                        )
+                                ?.let { openInCustomTab(it) }
                     }
                 }
             } else {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
index cdf6c64c9e..b25cea6fce 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginSignUpSignInSelectionFragment.kt
@@ -76,8 +76,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
                 views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders()
                 views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
                     override fun onProviderSelected(id: String?) {
-                        val url = withState(loginViewModel) { it.getSsoUrl(id) }
-                        openInCustomTab(url)
+                        loginViewModel.getSsoUrl(
+                                redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                                deviceId = state.deviceId,
+                                providerId = id
+                        )
+                                ?.let { openInCustomTab(it) }
                     }
                 }
             }
@@ -105,7 +109,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
 
     private fun submit() = withState(loginViewModel) { state ->
         if (state.loginMode is LoginMode.Sso) {
-            openInCustomTab(state.getSsoUrl(null))
+            loginViewModel.getSsoUrl(
+                    redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
+                    deviceId = state.deviceId,
+                    providerId = null
+            )
+                    ?.let { openInCustomTab(it) }
         } else {
             loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
         }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index 0a6dbcaae2..ab79c6ae48 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -818,4 +818,8 @@ class LoginViewModel @AssistedInject constructor(
     fun getInitialHomeServerUrl(): String? {
         return loginConfig?.homeServerUrl
     }
+
+    fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
+        return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
index 5254abf1d9..37ac89794f 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt
@@ -22,10 +22,6 @@ import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.PersistState
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.Uninitialized
-import im.vector.app.core.extensions.appendParamToUrl
-import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
 
 data class LoginViewState(
         val asyncLoginAction: Async<Unit> = Uninitialized,
@@ -69,27 +65,4 @@ data class LoginViewState(
     fun isUserLogged(): Boolean {
         return asyncLoginAction is Success
     }
-
-    fun getSsoUrl(providerId: String?): String {
-        return buildString {
-            append(homeServerUrl?.trim { it == '/' })
-            if (providerId != null) {
-                append(MSC2858_SSO_REDIRECT_PATH)
-                append("/$providerId")
-            } else {
-                append(SSO_REDIRECT_PATH)
-            }
-            // Set a redirect url we will intercept later
-            appendParamToUrl(SSO_REDIRECT_URL_PARAM, VECTOR_REDIRECT_URL)
-            deviceId?.takeIf { it.isNotBlank() }?.let {
-                // But https://github.com/matrix-org/synapse/issues/5755
-                appendParamToUrl("device_id", it)
-            }
-        }
-    }
-
-    companion object {
-        // Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
-        private const val VECTOR_REDIRECT_URL = "element://connect"
-    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
index acf4f706c5..7ba6626604 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
@@ -33,7 +33,6 @@ import android.webkit.WebViewClient
 import androidx.appcompat.app.AlertDialog
 import com.airbnb.mvrx.activityViewModel
 import im.vector.app.R
-import im.vector.app.core.extensions.appendParamToUrl
 import im.vector.app.core.utils.AssetReader
 import im.vector.app.databinding.FragmentLoginWebBinding
 import im.vector.app.features.signout.soft.SoftLogoutAction
@@ -42,6 +41,7 @@ import im.vector.app.features.signout.soft.SoftLogoutViewModel
 import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.data.Credentials
+import org.matrix.android.sdk.api.util.appendParamToUrl
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import timber.log.Timber
 import java.net.URLDecoder

From 36a553a8868c57459994849ad25b9d8c8230b94e Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 21 Dec 2020 12:08:49 +0100
Subject: [PATCH 19/41] Let the Matrix SDK compute the Fallback urls

---
 .../sdk/api/auth/AuthenticationService.kt     |  5 ++
 .../matrix/android/sdk/api/auth/Constants.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 50 ++++++++++++++-----
 .../app/features/login/LoginViewModel.kt      |  4 ++
 .../app/features/login/LoginWebFragment.kt    | 17 +------
 5 files changed, 50 insertions(+), 30 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 3314b47ce9..bf21941e0c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -46,6 +46,11 @@ interface AuthenticationService {
      */
     fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
 
+    /**
+     * Get the sign in or sign up fallback URL
+     */
+    fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String?
+
     /**
      * Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
      */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
index d832caefde..35b4a0a8d6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
@@ -20,13 +20,13 @@ package org.matrix.android.sdk.api.auth
  * Path to use when the client does not supported any or all login flows
  * Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback
  */
-const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
+internal const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
 
 /**
  * Path to use when the client does not supported any or all registration flows
  * Not documented
  */
-const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
+internal const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
 
 /**
  * Path to use when the client want to connect using SSO
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 51f6b6c155..0996011ed0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -23,7 +23,9 @@ import kotlinx.coroutines.withContext
 import okhttp3.OkHttpClient
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
+import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
 import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
 import org.matrix.android.sdk.api.auth.data.Credentials
@@ -104,27 +106,51 @@ internal class DefaultAuthenticationService @Inject constructor(
     }
 
     override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
-        return pendingSessionData?.let { safePendingSessionData ->
-            val homeServerUrl = safePendingSessionData.homeServerConnectionConfig.homeServerUri.toString()
+        val homeServerUrlBase = getHomeServerUrlBase() ?: return null
 
-            buildString {
-                append(homeServerUrl.trim { it == '/' })
-                if (providerId != null) {
-                    append(MSC2858_SSO_REDIRECT_PATH)
-                    append("/$providerId")
-                } else {
-                    append(SSO_REDIRECT_PATH)
-                }
-                // Set a redirect url we will intercept later
-                appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
+        return buildString {
+            append(homeServerUrlBase)
+            if (providerId != null) {
+                append(MSC2858_SSO_REDIRECT_PATH)
+                append("/$providerId")
+            } else {
+                append(SSO_REDIRECT_PATH)
+            }
+            // Set the redirect url
+            appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
+            deviceId?.takeIf { it.isNotBlank() }?.let {
+                // But https://github.com/matrix-org/synapse/issues/5755
+                appendParamToUrl("device_id", it)
+            }
+        }
+    }
+
+    override fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
+        val homeServerUrlBase = getHomeServerUrlBase() ?: return null
+
+        return buildString {
+            append(homeServerUrlBase)
+            if (forSignIn) {
+                append(LOGIN_FALLBACK_PATH)
                 deviceId?.takeIf { it.isNotBlank() }?.let {
                     // But https://github.com/matrix-org/synapse/issues/5755
                     appendParamToUrl("device_id", it)
                 }
+            } else {
+                // For sign up
+                append(REGISTER_FALLBACK_PATH)
             }
         }
     }
 
+    private fun getHomeServerUrlBase(): String? {
+        return pendingSessionData
+                ?.homeServerConnectionConfig
+                ?.homeServerUri
+                ?.toString()
+                ?.trim { it == '/' }
+    }
+
     override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
         pendingSessionData = null
 
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index ab79c6ae48..666bd21add 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -822,4 +822,8 @@ class LoginViewModel @AssistedInject constructor(
     fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
         return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
     }
+
+    fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
+        return authenticationService.getFallbackUrl(forSignIn, deviceId)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
index 7ba6626604..4b03c93321 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt
@@ -38,10 +38,7 @@ import im.vector.app.databinding.FragmentLoginWebBinding
 import im.vector.app.features.signout.soft.SoftLogoutAction
 import im.vector.app.features.signout.soft.SoftLogoutViewModel
 
-import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
-import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
 import org.matrix.android.sdk.api.auth.data.Credentials
-import org.matrix.android.sdk.api.util.appendParamToUrl
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import timber.log.Timber
 import java.net.URLDecoder
@@ -119,19 +116,7 @@ class LoginWebFragment @Inject constructor(
     }
 
     private fun launchWebView(state: LoginViewState) {
-        val url = buildString {
-            append(state.homeServerUrl?.trim { it == '/' })
-            if (state.signMode == SignMode.SignIn) {
-                append(LOGIN_FALLBACK_PATH)
-                state.deviceId?.takeIf { it.isNotBlank() }?.let {
-                    // But https://github.com/matrix-org/synapse/issues/5755
-                    appendParamToUrl("device_id", it)
-                }
-            } else {
-                // MODE_REGISTER
-                append(REGISTER_FALLBACK_PATH)
-            }
-        }
+        val url = loginViewModel.getFallbackUrl(state.signMode == SignMode.SignIn, state.deviceId) ?: return
 
         views.loginWebWebView.loadUrl(url)
 

From 6c4836e27e8ee05cac09a85e9e81f3b5db4e2b1f Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 21 Dec 2020 12:11:19 +0100
Subject: [PATCH 20/41] Move file to internal

---
 .../matrix/android/sdk/{api => internal}/auth/Constants.kt   | 2 +-
 .../sdk/internal/auth/DefaultAuthenticationService.kt        | 5 -----
 2 files changed, 1 insertion(+), 6 deletions(-)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{api => internal}/auth/Constants.kt (96%)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
similarity index 96%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
index 35b4a0a8d6..642279cc27 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/Constants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/Constants.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.api.auth
+package org.matrix.android.sdk.internal.auth
 
 /**
  * Path to use when the client does not supported any or all login flows
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 0996011ed0..c99e9bd81c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -23,11 +23,6 @@ import kotlinx.coroutines.withContext
 import okhttp3.OkHttpClient
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.auth.AuthenticationService
-import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
-import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
-import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.data.LoginFlowResult

From 073e6227d62975130a22be864a48f1c979ea49dd Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 28 Dec 2020 10:59:50 +0100
Subject: [PATCH 21/41] Correct Copyright mention

---
 .../main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
index beaff2bdda..17f27b2514 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/UrlExtensions.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.

From d1bec21759f2a534bd955972b1adabf825bccbcb Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 28 Dec 2020 12:23:06 +0100
Subject: [PATCH 22/41] Fix crash after migration to view bindings Also rename
 some layouts to follow convention

---
 .../core/ui/views/BottomSheetActionButton.kt  |  8 +-
 .../app/features/call/CallControlsView.kt     |  4 +-
 .../timeline/item/PollResultLineView.kt       |  8 +-
 .../signout/SignOutBottomSheetActionButton.kt | 12 +--
 .../main/res/layout/item_signout_action.xml   | 36 --------
 .../res/layout/item_verification_action.xml   | 10 +--
 .../view_bottom_sheet_action_button.xml       | 84 +++++++++++++++++++
 .../main/res/layout/view_call_controls.xml    | 35 ++++----
 ...ult_item.xml => view_poll_result_line.xml} |  0
 ...ew_sign_out_bottom_sheet_action_button.xml | 43 ++++++++++
 10 files changed, 166 insertions(+), 74 deletions(-)
 delete mode 100644 vector/src/main/res/layout/item_signout_action.xml
 create mode 100644 vector/src/main/res/layout/view_bottom_sheet_action_button.xml
 rename vector/src/main/res/layout/{item_timeline_event_poll_result_item.xml => view_poll_result_line.xml} (100%)
 create mode 100644 vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml

diff --git a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
index 6f261ad717..f86825750a 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt
@@ -28,7 +28,7 @@ import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemVerificationActionBinding
+import im.vector.app.databinding.ViewBottomSheetActionButtonBinding
 import im.vector.app.features.themes.ThemeUtils
 
 class BottomSheetActionButton @JvmOverloads constructor(
@@ -36,7 +36,7 @@ class BottomSheetActionButton @JvmOverloads constructor(
         attrs: AttributeSet? = null,
         defStyleAttr: Int = 0
 ) : FrameLayout(context, attrs, defStyleAttr) {
-    val views : ItemVerificationActionBinding
+    val views: ViewBottomSheetActionButtonBinding
 
     var title: String? = null
         set(value) {
@@ -97,8 +97,8 @@ class BottomSheetActionButton @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_verification_action, this)
-        views = ItemVerificationActionBinding.bind(this)
+        inflate(context, R.layout.view_bottom_sheet_action_button, this)
+        views = ViewBottomSheetActionButtonBinding.bind(this)
 
         context.withStyledAttributes(attrs, R.styleable.BottomSheetActionButton) {
             title = getString(R.styleable.BottomSheetActionButton_actionTitle) ?: ""
diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
index 5fdc70c539..9aa6ccd298 100644
--- a/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
+++ b/vector/src/main/java/im/vector/app/features/call/CallControlsView.kt
@@ -18,7 +18,7 @@ package im.vector.app.features.call
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.LinearLayout
+import android.widget.FrameLayout
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.databinding.ViewCallControlsBinding
@@ -28,7 +28,7 @@ import org.webrtc.PeerConnection
 
 class CallControlsView @JvmOverloads constructor(
         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
+) : FrameLayout(context, attrs, defStyleAttr) {
 
     private val views: ViewCallControlsBinding
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
index d5996a65ba..aa864851cd 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollResultLineView.kt
@@ -23,7 +23,7 @@ import android.widget.LinearLayout
 import androidx.core.content.withStyledAttributes
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemTimelineEventPollResultItemBinding
+import im.vector.app.databinding.ViewPollResultLineBinding
 
 class PollResultLineView @JvmOverloads constructor(
         context: Context,
@@ -31,7 +31,7 @@ class PollResultLineView @JvmOverloads constructor(
         defStyleAttr: Int = 0
 ) : LinearLayout(context, attrs, defStyleAttr) {
 
-    private val views: ItemTimelineEventPollResultItemBinding
+    private val views: ViewPollResultLineBinding
 
     var label: String? = null
         set(value) {
@@ -60,8 +60,8 @@ class PollResultLineView @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_timeline_event_poll_result_item, this)
-        views = ItemTimelineEventPollResultItemBinding.bind(this)
+        inflate(context, R.layout.view_poll_result_line, this)
+        views = ViewPollResultLineBinding.bind(this)
         orientation = HORIZONTAL
 
         context.withStyledAttributes(attrs, R.styleable.PollResultLineView) {
diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
index 00df261095..61bef29d54 100644
--- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
+++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt
@@ -20,19 +20,19 @@ import android.content.Context
 import android.content.res.ColorStateList
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
-import android.widget.LinearLayout
+import android.widget.FrameLayout
 import androidx.core.content.withStyledAttributes
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.ItemSignoutActionBinding
+import im.vector.app.databinding.ViewSignOutBottomSheetActionButtonBinding
 import im.vector.app.features.themes.ThemeUtils
 
 class SignOutBottomSheetActionButton @JvmOverloads constructor(
         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
+) : FrameLayout(context, attrs, defStyleAttr) {
 
-    private val views: ItemSignoutActionBinding
+    private val views: ViewSignOutBottomSheetActionButtonBinding
 
     var action: (() -> Unit)? = null
 
@@ -67,8 +67,8 @@ class SignOutBottomSheetActionButton @JvmOverloads constructor(
         }
 
     init {
-        inflate(context, R.layout.item_signout_action, this)
-        views = ItemSignoutActionBinding.bind(this)
+        inflate(context, R.layout.view_sign_out_bottom_sheet_action_button, this)
+        views = ViewSignOutBottomSheetActionButtonBinding.bind(this)
 
         context.withStyledAttributes(attrs, R.styleable.SignOutBottomSheetActionButton) {
             title = getString(R.styleable.SignOutBottomSheetActionButton_actionTitle) ?: ""
diff --git a/vector/src/main/res/layout/item_signout_action.xml b/vector/src/main/res/layout/item_signout_action.xml
deleted file mode 100644
index b1eb8c1f62..0000000000
--- a/vector/src/main/res/layout/item_signout_action.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/signedOutActionClickable"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:foreground="?attr/selectableItemBackground"
-    android:minHeight="50dp"
-    android:orientation="horizontal"
-    android:paddingStart="@dimen/layout_horizontal_margin"
-    android:paddingTop="8dp"
-    android:paddingEnd="@dimen/layout_horizontal_margin"
-    android:paddingBottom="8dp">
-
-    <ImageView
-        android:id="@+id/actionIconImageView"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_gravity="center_vertical"
-        android:layout_marginEnd="16dp"
-        android:scaleType="fitCenter"
-        android:src="@drawable/ic_secure_backup"
-        app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
-
-    <TextView
-        android:id="@+id/actionTitleText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:text="@string/secure_backup_setup"
-        android:textColor="?riotx_text_secondary"
-        android:textSize="17sp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_verification_action.xml b/vector/src/main/res/layout/item_verification_action.xml
index ae49893792..68ee392cff 100644
--- a/vector/src/main/res/layout/item_verification_action.xml
+++ b/vector/src/main/res/layout/item_verification_action.xml
@@ -24,10 +24,10 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@drawable/ic_share"
-        tools:visibility="visible"
         app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
+        tools:ignore="MissingPrefix"
+        tools:src="@drawable/ic_share"
+        tools:visibility="visible" />
 
 
     <TextView
@@ -70,8 +70,8 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@drawable/ic_arrow_right"
         app:tint="?riotx_text_primary"
-        tools:ignore="MissingPrefix" />
+        tools:ignore="MissingPrefix"
+        tools:src="@drawable/ic_arrow_right" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/view_bottom_sheet_action_button.xml b/vector/src/main/res/layout/view_bottom_sheet_action_button.xml
new file mode 100644
index 0000000000..c0f55df9e6
--- /dev/null
+++ b/vector/src/main/res/layout/view_bottom_sheet_action_button.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:parentTag="android.widget.FrameLayout">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/itemVerificationClickableZone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?riotx_bottom_sheet_background"
+        android:clickable="true"
+        android:focusable="true"
+        android:foreground="?attr/selectableItemBackground"
+        android:minHeight="64dp"
+        android:paddingStart="@dimen/layout_horizontal_margin"
+        android:paddingTop="8dp"
+        android:paddingEnd="@dimen/layout_horizontal_margin"
+        android:paddingBottom="8dp">
+
+        <ImageView
+            android:id="@+id/itemVerificationLeftIcon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="center"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix"
+            tools:src="@drawable/ic_share"
+            tools:visibility="visible" />
+
+
+        <TextView
+            android:id="@+id/itemVerificationActionTitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:textColor="@color/riotx_accent"
+            android:textSize="16sp"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintBottom_toTopOf="@+id/itemVerificationActionSubTitle"
+            app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
+            app:layout_constraintStart_toEndOf="@+id/itemVerificationLeftIcon"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_goneMarginStart="0dp"
+            tools:text="@string/start_verification" />
+
+        <TextView
+            android:id="@+id/itemVerificationActionSubTitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:textColor="?riotx_text_secondary"
+            android:textSize="12sp"
+            android:visibility="gone"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/itemVerificationActionIcon"
+            app:layout_constraintStart_toStartOf="@+id/itemVerificationActionTitle"
+            app:layout_constraintTop_toBottomOf="@+id/itemVerificationActionTitle"
+            tools:text="For maximum security, do this in person"
+            tools:visibility="visible" />
+
+        <ImageView
+            android:id="@+id/itemVerificationActionIcon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix"
+            tools:src="@drawable/ic_arrow_right" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</merge>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/view_call_controls.xml b/vector/src/main/res/layout/view_call_controls.xml
index 435520b9ef..2487f131e3 100644
--- a/vector/src/main/res/layout/view_call_controls.xml
+++ b/vector/src/main/res/layout/view_call_controls.xml
@@ -1,9 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    tools:parentTag="android.widget.FrameLayout">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/ringingControls"
@@ -23,8 +24,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/ringingControlDecline"
@@ -36,8 +37,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call_end"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <androidx.constraintlayout.helper.widget.Flow
             android:layout_width="match_parent"
@@ -69,8 +70,8 @@
             android:padding="10dp"
             android:src="@drawable/ic_home_bottom_chat"
             app:backgroundTint="?attr/riotx_background"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            app:tint="?attr/riotx_text_primary"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/muteIcon"
@@ -82,10 +83,10 @@
             android:padding="16dp"
             android:src="@drawable/ic_microphone_off"
             app:backgroundTint="?attr/riotx_background"
+            app:tint="?attr/riotx_text_primary"
             tools:contentDescription="@string/a11y_mute_microphone"
             tools:ignore="MissingConstraints,MissingPrefix"
-            tools:src="@drawable/ic_microphone_on"
-            app:tint="?attr/riotx_text_primary" />
+            tools:src="@drawable/ic_microphone_on" />
 
         <ImageView
             android:id="@+id/iv_end_call"
@@ -97,8 +98,8 @@
             android:focusable="true"
             android:padding="16dp"
             android:src="@drawable/ic_call_end"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="@color/white" />
+            app:tint="@color/white"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/videoToggleIcon"
@@ -110,9 +111,9 @@
             android:padding="16dp"
             android:src="@drawable/ic_call_videocam_off_default"
             app:backgroundTint="?attr/riotx_background"
+            app:tint="?attr/riotx_text_primary"
             tools:contentDescription="@string/a11y_stop_camera"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <ImageView
             android:id="@+id/iv_more"
@@ -125,8 +126,8 @@
             android:padding="8dp"
             android:src="@drawable/ic_more_vertical"
             app:backgroundTint="?attr/riotx_background"
-            tools:ignore="MissingConstraints,MissingPrefix"
-            app:tint="?attr/riotx_text_primary" />
+            app:tint="?attr/riotx_text_primary"
+            tools:ignore="MissingConstraints,MissingPrefix" />
 
         <androidx.constraintlayout.helper.widget.Flow
             android:layout_width="match_parent"
@@ -202,4 +203,4 @@
     <!--        app:layout_constraintEnd_toEndOf="parent"-->
     <!--        app:layout_constraintTop_toTopOf="parent" />-->
 
-</FrameLayout>
\ No newline at end of file
+</merge>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_timeline_event_poll_result_item.xml b/vector/src/main/res/layout/view_poll_result_line.xml
similarity index 100%
rename from vector/src/main/res/layout/item_timeline_event_poll_result_item.xml
rename to vector/src/main/res/layout/view_poll_result_line.xml
diff --git a/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml b/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml
new file mode 100644
index 0000000000..6809cfd119
--- /dev/null
+++ b/vector/src/main/res/layout/view_sign_out_bottom_sheet_action_button.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:parentTag="android.widget.FrameLayout">
+
+    <LinearLayout
+        android:id="@+id/signedOutActionClickable"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:foreground="?attr/selectableItemBackground"
+        android:minHeight="50dp"
+        android:orientation="horizontal"
+        android:paddingStart="@dimen/layout_horizontal_margin"
+        android:paddingTop="8dp"
+        android:paddingEnd="@dimen/layout_horizontal_margin"
+        android:paddingBottom="8dp">
+
+        <ImageView
+            android:id="@+id/actionIconImageView"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="16dp"
+            android:scaleType="fitCenter"
+            android:src="@drawable/ic_secure_backup"
+            app:tint="?riotx_text_primary"
+            tools:ignore="MissingPrefix" />
+
+        <TextView
+            android:id="@+id/actionTitleText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:text="@string/secure_backup_setup"
+            android:textColor="?riotx_text_secondary"
+            android:textSize="17sp" />
+
+    </LinearLayout>
+
+</merge>

From 9f3176c49cd3a8b45e4a895e758212a0f4d3e5be Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 28 Dec 2020 14:41:23 +0100
Subject: [PATCH 23/41] Fix code quality

---
 tools/check/forbidden_strings_in_code.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index b9929dfebe..2306eaed8b 100644
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1
 # android\.text\.TextUtils
 
 ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
-enum class===84
+enum class===85
 
 ### Do not import temporary legacy classes
 import org.matrix.android.sdk.internal.legacy.riot===3

From cc01f25d8f00e6c3cdee9b1cc98155e76413ac24 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 28 Dec 2020 14:52:49 +0100
Subject: [PATCH 24/41] Revert status to RoomMembersLoadStatusType.NONE) in
 case of failure

---
 .../room/membership/LoadRoomMembersTask.kt    | 24 +++++++++++++------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
index 55dd747fdb..53fa73c624 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
@@ -82,16 +82,19 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
     }
 
     private suspend fun doRequest(params: LoadRoomMembersTask.Params) {
-        monarchy.awaitTransaction { realm ->
-            val roomEntity = RoomEntity.where(realm, params.roomId).findFirst()
-                    ?: realm.createObject(params.roomId)
-            roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADING
-        }
+        setRoomMembersLoadStatus(params.roomId, RoomMembersLoadStatusType.LOADING)
 
         val lastToken = syncTokenStore.getLastToken()
-        val response = executeRequest<RoomMembersResponse>(eventBus) {
-            apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
+        val response = try {
+            executeRequest<RoomMembersResponse>(eventBus) {
+                apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
+            }
+        } catch (throwable: Throwable) {
+            // Revert status to NONE
+            setRoomMembersLoadStatus(params.roomId, RoomMembersLoadStatusType.NONE)
+            throw throwable
         }
+        // This will also set the status to LOADED
         insertInDb(response, params.roomId)
     }
 
@@ -125,4 +128,11 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
         }
         return result ?: RoomMembersLoadStatusType.NONE
     }
+
+    private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) {
+        monarchy.awaitTransaction { realm ->
+            val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
+            roomEntity.membersLoadStatus = status
+        }
+    }
 }

From cbdacc199ab08ff93c3d6ee950a278e8ef067ced Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 28 Dec 2020 17:17:01 +0300
Subject: [PATCH 25/41] Disable scroll effect when click to a link in the
 topic.

---
 CHANGES.md                                    |  1 +
 .../app/core/epoxy/ExpandableTextItem.kt      |  7 +++--
 .../app/core/ui/views/NonScrollingTextView.kt | 31 +++++++++++++++++++
 .../roomprofile/RoomProfileController.kt      | 13 ++++++++
 .../roomprofile/RoomProfileFragment.kt        |  4 +++
 .../res/layout/item_expandable_textview.xml   |  2 +-
 6 files changed, 55 insertions(+), 3 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/core/ui/views/NonScrollingTextView.kt

diff --git a/CHANGES.md b/CHANGES.md
index a028ef6f1e..27229361a7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,7 @@ Improvements 🙌:
 
 Bugfix 🐛:
  - Url previews sometimes attached to wrong message (#2561)
+ - Room Topic not displayed correctly after visiting a link (#2551)
 
 Translations 🗣:
  -
diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
index 3dceec48ef..eb8d4a41c4 100644
--- a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
+++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
@@ -18,6 +18,7 @@ package im.vector.app.core.epoxy
 
 import android.animation.ObjectAnimator
 import android.text.TextUtils
+import android.text.method.MovementMethod
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.core.view.doOnPreDraw
@@ -25,7 +26,6 @@ import androidx.core.view.isVisible
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
-import im.vector.app.core.extensions.copyOnLongClick
 
 @EpoxyModelClass(layout = R.layout.item_expandable_textview)
 abstract class ExpandableTextItem : VectorEpoxyModel<ExpandableTextItem.Holder>() {
@@ -36,13 +36,16 @@ abstract class ExpandableTextItem : VectorEpoxyModel<ExpandableTextItem.Holder>(
     @EpoxyAttribute
     var maxLines: Int = 3
 
+    @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+    var movementMethod: MovementMethod? = null
+
     private var isExpanded = false
     private var expandedLines = 0
 
     override fun bind(holder: Holder) {
         super.bind(holder)
         holder.content.text = content
-        holder.content.copyOnLongClick()
+        holder.content.movementMethod = movementMethod
 
         holder.content.doOnPreDraw {
             if (holder.content.lineCount > maxLines) {
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NonScrollingTextView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NonScrollingTextView.kt
new file mode 100644
index 0000000000..712432abfd
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/ui/views/NonScrollingTextView.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.core.ui.views
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.appcompat.widget.AppCompatTextView
+
+class NonScrollingTextView : AppCompatTextView {
+    constructor(context: Context) : super(context)
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+
+    override fun scrollTo(x: Int, y: Int) {
+        // NOOP
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
index 891d15d04f..6b988df94d 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
@@ -26,6 +26,8 @@ import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.core.ui.list.genericFooterItem
 import im.vector.app.features.home.ShortcutCreator
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod
 import im.vector.app.features.settings.VectorPreferences
 import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -53,6 +55,7 @@ class RoomProfileController @Inject constructor(
         fun onSettingsClicked()
         fun onLeaveRoomClicked()
         fun onRoomIdClicked()
+        fun onUrlInTopicLongClicked(url: String)
     }
 
     override fun buildModels(data: RoomProfileViewState?) {
@@ -71,6 +74,16 @@ class RoomProfileController @Inject constructor(
                         id("topic")
                         content(it)
                         maxLines(2)
+                        movementMethod(createLinkMovementMethod(object : TimelineEventController.UrlClickCallback {
+                            override fun onUrlClicked(url: String, title: String): Boolean {
+                                return false
+                            }
+
+                            override fun onUrlLongClicked(url: String): Boolean {
+                                callback?.onUrlInTopicLongClicked(url)
+                                return true
+                            }
+                        }))
                     }
                 }
 
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 473c1d4324..44e17a1681 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -261,6 +261,10 @@ class RoomProfileFragment @Inject constructor(
         copyToClipboard(requireContext(), roomProfileArgs.roomId)
     }
 
+    override fun onUrlInTopicLongClicked(url: String) {
+        copyToClipboard(requireContext(), url, true)
+    }
+
     private fun onShareRoomProfile(permalink: String) {
         startSharePlainTextIntent(
                 fragment = this,
diff --git a/vector/src/main/res/layout/item_expandable_textview.xml b/vector/src/main/res/layout/item_expandable_textview.xml
index 9f61a3c4d4..4dc7085737 100644
--- a/vector/src/main/res/layout/item_expandable_textview.xml
+++ b/vector/src/main/res/layout/item_expandable_textview.xml
@@ -6,7 +6,7 @@
     android:layout_height="wrap_content"
     android:padding="16dp">
 
-    <TextView
+    <im.vector.app.core.ui.views.NonScrollingTextView
         android:id="@+id/expandableContent"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"

From 3e78098c43368bc26b9ad7a735f0f735e6dfeb0e Mon Sep 17 00:00:00 2001
From: Tigermouthbear <tigermouthbear@tigr.dev>
Date: Mon, 28 Dec 2020 23:32:06 -0500
Subject: [PATCH 26/41] Add System theme option and set as default; closes
 #904, closes #2387

---
 CHANGES.md                                    |  2 +-
 .../vector/app/features/themes/ThemeUtils.kt  | 26 +++++++++++++------
 vector/src/main/res/values/array.xml          |  2 ++
 vector/src/main/res/values/strings.xml        |  1 +
 .../res/xml/vector_settings_preferences.xml   |  2 +-
 5 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index a028ef6f1e..5ab9ff733d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,7 +5,7 @@ Features ✨:
  - Enable url previews for notices (#2562)
 
 Improvements 🙌:
- -
+  - Add System theme option and set as default (#904) (#2387)
 
 Bugfix 🐛:
  - Url previews sometimes attached to wrong message (#2561)
diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
index bba6b9c253..172303cff7 100644
--- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
@@ -18,6 +18,8 @@ package im.vector.app.features.themes
 
 import android.app.Activity
 import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
 import android.graphics.drawable.Drawable
 import android.util.TypedValue
 import android.view.Menu
@@ -39,6 +41,7 @@ object ThemeUtils {
     const val APPLICATION_THEME_KEY = "APPLICATION_THEME_KEY"
 
     // the theme possible values
+    private const val SYSTEM_THEME_VALUE = "system"
     private const val THEME_DARK_VALUE = "dark"
     private const val THEME_LIGHT_VALUE = "light"
     private const val THEME_BLACK_VALUE = "black"
@@ -54,13 +57,11 @@ object ThemeUtils {
     }
 
     /**
-     * @return true if current theme is Light or Status
+     * @return true if current theme is Light/Status or current theme is System and system theme is light
      */
     fun isLightTheme(context: Context): Boolean {
-        return when (getApplicationTheme(context)) {
-            THEME_LIGHT_VALUE -> true
-            else              -> false
-        }
+        val theme = getApplicationTheme(context)
+        return theme == THEME_LIGHT_VALUE || (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources))
     }
 
     /**
@@ -73,11 +74,11 @@ object ThemeUtils {
         val currentTheme = this.currentTheme.get()
         return if (currentTheme == null) {
             val prefs = DefaultSharedPreferences.getInstance(context)
-            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
+            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, SYSTEM_THEME_VALUE) ?: SYSTEM_THEME_VALUE
             if (themeFromPref == "status") {
                 // Migrate to light theme, which is the closest theme
-                themeFromPref = THEME_LIGHT_VALUE
-                prefs.edit { putString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) }
+                themeFromPref = SYSTEM_THEME_VALUE
+                prefs.edit { putString(APPLICATION_THEME_KEY, SYSTEM_THEME_VALUE) }
             }
             this.currentTheme.set(themeFromPref)
             themeFromPref
@@ -86,6 +87,13 @@ object ThemeUtils {
         }
     }
 
+    /**
+     * @return true if system theme is dark
+     */
+    private fun isSystemDarkTheme(resources: Resources): Boolean {
+        return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+    }
+
     /**
      * Update the application theme
      *
@@ -94,6 +102,7 @@ object ThemeUtils {
     fun setApplicationTheme(context: Context, aTheme: String) {
         currentTheme.set(aTheme)
         when (aTheme) {
+            SYSTEM_THEME_VALUE -> context.setTheme(if(isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light)
             THEME_DARK_VALUE   -> context.setTheme(R.style.AppTheme_Dark)
             THEME_BLACK_VALUE  -> context.setTheme(R.style.AppTheme_Black)
             else               -> context.setTheme(R.style.AppTheme_Light)
@@ -110,6 +119,7 @@ object ThemeUtils {
      */
     fun setActivityTheme(activity: Activity, otherThemes: ActivityOtherThemes) {
         when (getApplicationTheme(activity)) {
+            SYSTEM_THEME_VALUE -> if(isSystemDarkTheme(activity.resources)) activity.setTheme(otherThemes.dark)
             THEME_DARK_VALUE   -> activity.setTheme(otherThemes.dark)
             THEME_BLACK_VALUE  -> activity.setTheme(otherThemes.black)
         }
diff --git a/vector/src/main/res/values/array.xml b/vector/src/main/res/values/array.xml
index e56844e2e8..2c4bc135e5 100644
--- a/vector/src/main/res/values/array.xml
+++ b/vector/src/main/res/values/array.xml
@@ -92,12 +92,14 @@
 
     <!-- Theme -->
     <string-array name="theme_entries">
+        <item>@string/system_theme</item>
         <item>@string/light_theme</item>
         <item>@string/dark_theme</item>
         <item>@string/black_them</item>
     </string-array>
 
     <string-array name="theme_values">
+        <item>system</item>
         <item>light</item>
         <item>dark</item>
         <item>black</item>
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 76541460d2..355ac4d6d6 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -8,6 +8,7 @@
     <string name="resources_script">Latn</string>
 
     <!-- theme -->
+    <string name="system_theme">System Default</string>
     <string name="light_theme">Light Theme</string>
     <string name="dark_theme">Dark Theme</string>
     <string name="black_them">Black Theme</string>
diff --git a/vector/src/main/res/xml/vector_settings_preferences.xml b/vector/src/main/res/xml/vector_settings_preferences.xml
index 74b1f882ee..6297b89e6c 100644
--- a/vector/src/main/res/xml/vector_settings_preferences.xml
+++ b/vector/src/main/res/xml/vector_settings_preferences.xml
@@ -13,7 +13,7 @@
             app:fragment="im.vector.app.features.settings.locale.LocalePickerFragment" />
 
         <im.vector.app.core.preference.VectorListPreference
-            android:defaultValue="light"
+            android:defaultValue="system"
             android:entries="@array/theme_entries"
             android:entryValues="@array/theme_values"
             android:key="APPLICATION_THEME_KEY"

From c9eacec44976eabee0cf057d496764a3a39f2e73 Mon Sep 17 00:00:00 2001
From: Tigermouthbear <tigermouthbear@tigr.dev>
Date: Tue, 29 Dec 2020 00:22:12 -0500
Subject: [PATCH 27/41] Fix formatting in ThemeUtils

---
 .../src/main/java/im/vector/app/features/themes/ThemeUtils.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
index 172303cff7..e3ef6a2b0b 100644
--- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
@@ -102,7 +102,7 @@ object ThemeUtils {
     fun setApplicationTheme(context: Context, aTheme: String) {
         currentTheme.set(aTheme)
         when (aTheme) {
-            SYSTEM_THEME_VALUE -> context.setTheme(if(isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light)
+            SYSTEM_THEME_VALUE -> context.setTheme(if (isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light)
             THEME_DARK_VALUE   -> context.setTheme(R.style.AppTheme_Dark)
             THEME_BLACK_VALUE  -> context.setTheme(R.style.AppTheme_Black)
             else               -> context.setTheme(R.style.AppTheme_Light)
@@ -119,7 +119,7 @@ object ThemeUtils {
      */
     fun setActivityTheme(activity: Activity, otherThemes: ActivityOtherThemes) {
         when (getApplicationTheme(activity)) {
-            SYSTEM_THEME_VALUE -> if(isSystemDarkTheme(activity.resources)) activity.setTheme(otherThemes.dark)
+            SYSTEM_THEME_VALUE -> if (isSystemDarkTheme(activity.resources)) activity.setTheme(otherThemes.dark)
             THEME_DARK_VALUE   -> activity.setTheme(otherThemes.dark)
             THEME_BLACK_VALUE  -> activity.setTheme(otherThemes.black)
         }

From f1f1613f008260079851a369422a60b18a766f2d Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 29 Dec 2020 09:56:15 +0100
Subject: [PATCH 28/41] Cleanup and format

---
 .../vector/app/features/themes/ThemeUtils.kt  | 64 ++++++-------------
 1 file changed, 18 insertions(+), 46 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
index e3ef6a2b0b..0856ac4b35 100644
--- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
@@ -46,6 +46,9 @@ object ThemeUtils {
     private const val THEME_LIGHT_VALUE = "light"
     private const val THEME_BLACK_VALUE = "black"
 
+    // The default theme
+    private const val DEFAULT_THEME = SYSTEM_THEME_VALUE
+
     private var currentTheme = AtomicReference<String>(null)
 
     private val mColorByAttr = HashMap<Int, Int>()
@@ -57,11 +60,12 @@ object ThemeUtils {
     }
 
     /**
-     * @return true if current theme is Light/Status or current theme is System and system theme is light
+     * @return true if current theme is Light or current theme is System and system theme is light
      */
     fun isLightTheme(context: Context): Boolean {
         val theme = getApplicationTheme(context)
-        return theme == THEME_LIGHT_VALUE || (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources))
+        return theme == THEME_LIGHT_VALUE
+                || (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources))
     }
 
     /**
@@ -74,11 +78,11 @@ object ThemeUtils {
         val currentTheme = this.currentTheme.get()
         return if (currentTheme == null) {
             val prefs = DefaultSharedPreferences.getInstance(context)
-            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, SYSTEM_THEME_VALUE) ?: SYSTEM_THEME_VALUE
+            var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, DEFAULT_THEME) ?: DEFAULT_THEME
             if (themeFromPref == "status") {
-                // Migrate to light theme, which is the closest theme
-                themeFromPref = SYSTEM_THEME_VALUE
-                prefs.edit { putString(APPLICATION_THEME_KEY, SYSTEM_THEME_VALUE) }
+                // Migrate to the default theme
+                themeFromPref = DEFAULT_THEME
+                prefs.edit { putString(APPLICATION_THEME_KEY, DEFAULT_THEME) }
             }
             this.currentTheme.set(themeFromPref)
             themeFromPref
@@ -101,12 +105,14 @@ object ThemeUtils {
      */
     fun setApplicationTheme(context: Context, aTheme: String) {
         currentTheme.set(aTheme)
-        when (aTheme) {
-            SYSTEM_THEME_VALUE -> context.setTheme(if (isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light)
-            THEME_DARK_VALUE   -> context.setTheme(R.style.AppTheme_Dark)
-            THEME_BLACK_VALUE  -> context.setTheme(R.style.AppTheme_Black)
-            else               -> context.setTheme(R.style.AppTheme_Light)
-        }
+        context.setTheme(
+                when (aTheme) {
+                    SYSTEM_THEME_VALUE -> if (isSystemDarkTheme(context.resources)) R.style.AppTheme_Dark else R.style.AppTheme_Light
+                    THEME_DARK_VALUE   -> R.style.AppTheme_Dark
+                    THEME_BLACK_VALUE  -> R.style.AppTheme_Black
+                    else               -> R.style.AppTheme_Light
+                }
+        )
 
         // Clear the cache
         mColorByAttr.clear()
@@ -127,40 +133,6 @@ object ThemeUtils {
         mColorByAttr.clear()
     }
 
-    /**
-     * Set the TabLayout colors.
-     * It seems that there is no proper way to manage it with the manifest file.
-     *
-     * @param activity the activity
-     * @param layout   the layout
-     */
-    /*
-    fun setTabLayoutTheme(activity: Activity, layout: TabLayout) {
-        if (activity is VectorGroupDetailsActivity) {
-            val textColor: Int
-            val underlineColor: Int
-            val backgroundColor: Int
-
-            if (TextUtils.equals(getApplicationTheme(activity), THEME_LIGHT_VALUE)) {
-                textColor = ContextCompat.getColor(activity, android.R.color.white)
-                underlineColor = textColor
-                backgroundColor = ContextCompat.getColor(activity, R.color.tab_groups)
-            } else if (TextUtils.equals(getApplicationTheme(activity), THEME_STATUS_VALUE)) {
-                textColor = ContextCompat.getColor(activity, android.R.color.white)
-                underlineColor = textColor
-                backgroundColor = getColor(activity, R.attr.colorPrimary)
-            } else {
-                textColor = ContextCompat.getColor(activity, R.color.tab_groups)
-                underlineColor = textColor
-                backgroundColor = getColor(activity, R.attr.colorPrimary)
-            }
-
-            layout.setTabTextColors(textColor, textColor)
-            layout.setSelectedTabIndicatorColor(underlineColor)
-            layout.setBackgroundColor(backgroundColor)
-        }
-    }    */
-
     /**
      * Translates color attributes to colors
      *

From 3ea3d0fc918e4b213ab9700155d54b2b80c0262a Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 29 Dec 2020 15:42:12 +0100
Subject: [PATCH 29/41] Unspecced msgType field in m.sticker (#2580)

---
 CHANGES.md                                                       | 1 +
 .../sdk/api/session/room/model/message/MessageStickerContent.kt  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/CHANGES.md b/CHANGES.md
index 373c3aa985..a35c2fdd10 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,7 @@ Improvements 🙌:
   - Add System theme option and set as default (#904) (#2387)
 
 Bugfix 🐛:
+ - Unspecced msgType field in m.sticker (#2580)
  - Wait for all room members to be known before sending a message to a e2e room (#2518)
  - Url previews sometimes attached to wrong message (#2561)
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
index 00fa68c0ac..280316d4b5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageStickerContent.kt
@@ -27,6 +27,7 @@ data class MessageStickerContent(
         /**
          * Set in local, not from server
          */
+        @Transient
         override val msgType: String = MessageType.MSGTYPE_STICKER_LOCAL,
 
         /**

From 3240cadb94b3faed6674eb05d8506e7cb9956b46 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 30 Dec 2020 12:08:27 +0300
Subject: [PATCH 30/41] Fix membership event visibility condition.

---
 CHANGES.md                                                      | 1 +
 .../home/room/detail/timeline/helper/TimelineSettingsFactory.kt | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index 373c3aa985..74710baf48 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,7 @@ Improvements 🙌:
 Bugfix 🐛:
  - Wait for all room members to be known before sending a message to a e2e room (#2518)
  - Url previews sometimes attached to wrong message (#2561)
+ - Fix member state filter (#2581)
 
 Translations 🗣:
  -
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
index 1983b05ed3..01c7ad3986 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt
@@ -57,7 +57,7 @@ class TimelineSettingsFactory @Inject constructor(
         return map {
             EventTypeFilter(
                     eventType = it,
-                    stateKey = if (it == EventType.STATE_ROOM_MEMBER && userPreferencesProvider.shouldShowRoomMemberStateEvents()) session.myUserId else null
+                    stateKey = if (it == EventType.STATE_ROOM_MEMBER && !userPreferencesProvider.shouldShowRoomMemberStateEvents()) session.myUserId else null
             )
         }
     }

From 05013d25594ccd14a8a19f058d2b5a2c330da5a7 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 30 Dec 2020 12:17:25 +0300
Subject: [PATCH 31/41] Fix changelog.

---
 CHANGES.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index 74710baf48..1fa3eb8e4b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,7 +10,7 @@ Improvements 🙌:
 Bugfix 🐛:
  - Wait for all room members to be known before sending a message to a e2e room (#2518)
  - Url previews sometimes attached to wrong message (#2561)
- - Fix member state filter (#2581)
+ - Hiding membership events works the exact opposite (#2603)
 
 Translations 🗣:
  -

From 11367488e6726012526bd6141ec4d954c419bb86 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 4 Jan 2021 14:21:57 +0100
Subject: [PATCH 32/41] Fix crash in AttachmentViewerActivity (after
 ViewBindings) And improve code a bit

---
 .../AttachmentViewerActivity.kt               | 22 ++++++++++---------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
index ae095be41a..9b1345cd39 100644
--- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
+++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
@@ -40,13 +40,16 @@ import kotlin.math.abs
 
 abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
 
-    lateinit var pager2: ViewPager2
-    lateinit var imageTransitionView: ImageView
-    lateinit var transitionImageContainer: ViewGroup
+    protected val pager2: ViewPager2
+        get() = views.attachmentPager
+    protected val imageTransitionView: ImageView
+        get() = views.transitionImageView
+    protected val transitionImageContainer: ViewGroup
+        get() = views.transitionImageContainer
 
-    var topInset = 0
-    var bottomInset = 0
-    var systemUiVisibility = true
+    private var topInset = 0
+    private var bottomInset = 0
+    private var systemUiVisibility = true
 
     private var overlayView: View? = null
         set(value) {
@@ -65,14 +68,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
     private lateinit var gestureDetector: GestureDetectorCompat
 
     var currentPosition = 0
+        private set
 
     private var swipeDirection: SwipeDirection? = null
 
     private fun isScaled() = attachmentsAdapter.isScaled(currentPosition)
 
+    private val attachmentsAdapter = AttachmentsAdapter()
+
     private var wasScaled: Boolean = false
     private var isSwipeToDismissAllowed: Boolean = true
-    private lateinit var attachmentsAdapter: AttachmentsAdapter
     private var isOverlayWasClicked = false
 
 //    private val shouldDismissToBottom: Boolean
@@ -101,10 +106,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
         views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
         setContentView(views.root)
         views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
-        attachmentsAdapter = AttachmentsAdapter()
         views.attachmentPager.adapter = attachmentsAdapter
-        imageTransitionView = views.transitionImageView
-        pager2 = views.attachmentPager
         directionDetector = createSwipeDirectionDetector()
         gestureDetector = createGestureDetector()
 

From c11a50f7ff0748d88d75fbee745338c7ed910a8c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 4 Jan 2021 18:07:47 +0100
Subject: [PATCH 33/41] Malformed matrix.to link: display a dialog instead of a
 toast

---
 .../main/java/im/vector/app/features/home/HomeActivity.kt  | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 9d7beb13a3..c4c8da551c 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -39,7 +39,6 @@ import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.ToolbarConfigurable
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.pushers.PushersManager
-import im.vector.app.core.utils.toast
 import im.vector.app.databinding.ActivityHomeBinding
 import im.vector.app.features.disclaimer.showDisclaimerDialog
 import im.vector.app.features.matrixto.MatrixToBottomSheet
@@ -190,7 +189,11 @@ class HomeActivity :
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe { isHandled ->
                         if (!isHandled) {
-                            toast(R.string.permalink_malformed)
+                            AlertDialog.Builder(this)
+                                    .setTitle(R.string.dialog_title_error)
+                                    .setMessage(R.string.permalink_malformed)
+                                    .setPositiveButton(R.string.ok, null)
+                                    .show()
                         }
                     }
                     .disposeOnDestroy()

From c34fea2932dd9e17a04b9d5196e67e349788d413 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 4 Jan 2021 18:16:12 +0100
Subject: [PATCH 34/41] Do not defien Element Stuff in the SDK

---
 .../sdk/api/session/permalinks/PermalinkService.kt    |  1 -
 .../java/im/vector/app/features/home/HomeActivity.kt  | 11 ++++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
index aefc086b43..ac1d726d03 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
@@ -25,7 +25,6 @@ interface PermalinkService {
 
     companion object {
         const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
-        const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
     }
 
     /**
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index c4c8da551c..3d8d8bb961 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
@@ -165,8 +165,8 @@ class HomeActivity :
     private fun handleIntent(intent: Intent?) {
         intent?.dataString?.let { deepLink ->
             val resolvedLink = when {
-                deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE)               -> deepLink
-                deepLink.startsWith(PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> {
+                deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> deepLink
+                deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE)    -> {
                     // This is a bit ugly, but for now just convert to matrix.to link for compatibility
                     when {
                         deepLink.startsWith(USER_LINK_PREFIX) -> deepLink.substring(USER_LINK_PREFIX.length)
@@ -176,7 +176,7 @@ class HomeActivity :
                         activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(it)
                     }
                 }
-                else                                                                   -> null
+                else                                                     -> null
             }
 
             permalinkHandler.launch(
@@ -413,7 +413,8 @@ class HomeActivity :
                     }
         }
 
-        private const val ROOM_LINK_PREFIX = "${PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
-        private const val USER_LINK_PREFIX = "${PermalinkService.MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
+        private const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
+        private const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/"
+        private const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/"
     }
 }

From 4f59ec37ca37a414d4ce43bf882f726ad4fc5de0 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 4 Jan 2021 18:18:34 +0100
Subject: [PATCH 35/41] Tapping drawer having more than 1 room in notifications
 gives "malformed link" error (#2605)

---
 CHANGES.md                                                      | 1 +
 .../src/main/java/im/vector/app/features/home/HomeActivity.kt   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index eaaa4c1b0a..7d4678fe40 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,7 @@ Bugfix 🐛:
  - Wait for all room members to be known before sending a message to a e2e room (#2518)
  - Url previews sometimes attached to wrong message (#2561)
  - Hiding membership events works the exact opposite (#2603)
+ - Tapping drawer having more than 1 room in notifications gives "malformed link" error (#2605)
 
 Translations 🗣:
  -
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 3d8d8bb961..108e0512a7 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -176,7 +176,7 @@ class HomeActivity :
                         activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(it)
                     }
                 }
-                else                                                     -> null
+                else                                                     -> return@let
             }
 
             permalinkHandler.launch(

From 3a9b80127f807e263e5143b03fe60ad0cd70d9bb Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 5 Jan 2021 11:46:59 +0100
Subject: [PATCH 36/41] Warn user when he is leaving a not public room (#1460)

---
 CHANGES.md                                    |  3 +-
 .../room/state/StateServiceExtension.kt       | 33 +++++++++++++++++
 .../home/room/list/RoomListFragment.kt        | 36 ++++++++++++++-----
 .../home/room/list/RoomListViewModel.kt       |  5 +++
 .../roomprofile/RoomProfileFragment.kt        | 17 ++++++++-
 .../roomprofile/RoomProfileViewModel.kt       |  5 +++
 vector/src/main/res/values/strings.xml        |  1 +
 7 files changed, 89 insertions(+), 11 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateServiceExtension.kt

diff --git a/CHANGES.md b/CHANGES.md
index 7d4678fe40..0ddd9572ea 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,7 +5,8 @@ Features ✨:
  - Enable url previews for notices (#2562)
 
 Improvements 🙌:
-  - Add System theme option and set as default (#904) (#2387)
+ - Add System theme option and set as default (#904, #2387)
+ - Warn user when he is leaving a not public room (#1460)
 
 Bugfix 🐛:
  - Unspecced msgType field in m.sticker (#2580)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateServiceExtension.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateServiceExtension.kt
new file mode 100644
index 0000000000..c625a7f088
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateServiceExtension.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.session.room.state
+
+import org.matrix.android.sdk.api.query.QueryStringValue
+import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
+import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
+
+/**
+ * Return true if a room can be joined by anyone (RoomJoinRules.PUBLIC)
+ */
+fun StateService.isPublic(): Boolean {
+    return getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition)
+            ?.content
+            ?.toModel<RoomJoinRulesContent>()
+            ?.joinRules == RoomJoinRules.PUBLIC
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
index 02a277f377..30cb360a9d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.home.room.list
 
+import android.content.DialogInterface
 import android.os.Bundle
 import android.os.Parcelable
 import android.view.LayoutInflater
@@ -36,6 +37,7 @@ import com.airbnb.mvrx.args
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.app.R
+import im.vector.app.core.dialogs.withColoredButton
 import im.vector.app.core.epoxy.LayoutManagerStateRestorer
 import im.vector.app.core.extensions.cleanup
 import im.vector.app.core.extensions.exhaustive
@@ -246,19 +248,35 @@ class RoomListFragment @Inject constructor(
                 roomListViewModel.handle(RoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
             }
             is RoomListQuickActionsSharedAction.Leave                     -> {
-                AlertDialog.Builder(requireContext())
-                        .setTitle(R.string.room_participants_leave_prompt_title)
-                        .setMessage(R.string.room_participants_leave_prompt_msg)
-                        .setPositiveButton(R.string.leave) { _, _ ->
-                            roomListViewModel.handle(RoomListAction.LeaveRoom(quickAction.roomId))
-                        }
-                        .setNegativeButton(R.string.cancel, null)
-                        .show()
-                Unit
+                promptLeaveRoom(quickAction.roomId)
             }
         }.exhaustive
     }
 
+    private fun promptLeaveRoom(roomId: String) {
+        val isPublicRoom = roomListViewModel.isPublicRoom(roomId)
+        val message = buildString {
+            append(getString(R.string.room_participants_leave_prompt_msg))
+            if (!isPublicRoom) {
+                append("\n\n")
+                append(getString(R.string.room_participants_leave_private_warning))
+            }
+        }
+        AlertDialog.Builder(requireContext())
+                .setTitle(R.string.room_participants_leave_prompt_title)
+                .setMessage(message)
+                .setPositiveButton(R.string.leave) { _, _ ->
+                    roomListViewModel.handle(RoomListAction.LeaveRoom(roomId))
+                }
+                .setNegativeButton(R.string.cancel, null)
+                .show()
+                .apply {
+                    if (!isPublicRoom) {
+                        withColoredButton(DialogInterface.BUTTON_POSITIVE)
+                    }
+                }
+    }
+
     override fun invalidate() = withState(roomListViewModel) { state ->
         when (state.asyncFilteredRooms) {
             is Incomplete -> renderLoading()
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
index 84652506cd..6e5081a31c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
@@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
+import org.matrix.android.sdk.api.session.room.state.isPublic
 import org.matrix.android.sdk.rx.rx
 import timber.log.Timber
 import java.lang.Exception
@@ -78,6 +79,10 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
         }.exhaustive
     }
 
+    fun isPublicRoom(roomId: String): Boolean {
+        return session.getRoom(roomId)?.isPublic().orFalse()
+    }
+
     // PRIVATE METHODS *****************************************************************************
 
     private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 473c1d4324..53c5b25d6d 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -17,6 +17,7 @@
 
 package im.vector.app.features.roomprofile
 
+import android.content.DialogInterface
 import android.os.Bundle
 import android.os.Parcelable
 import android.view.LayoutInflater
@@ -34,6 +35,7 @@ import com.airbnb.mvrx.withState
 import im.vector.app.R
 import im.vector.app.core.animations.AppBarStateChangeListener
 import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
+import im.vector.app.core.dialogs.withColoredButton
 import im.vector.app.core.extensions.cleanup
 import im.vector.app.core.extensions.configureWith
 import im.vector.app.core.extensions.copyOnLongClick
@@ -247,14 +249,27 @@ class RoomProfileFragment @Inject constructor(
     }
 
     override fun onLeaveRoomClicked() {
+        val isPublicRoom = roomProfileViewModel.isPublicRoom()
+        val message = buildString {
+            append(getString(R.string.room_participants_leave_prompt_msg))
+            if (!isPublicRoom) {
+                append("\n\n")
+                append(getString(R.string.room_participants_leave_private_warning))
+            }
+        }
         AlertDialog.Builder(requireContext())
                 .setTitle(R.string.room_participants_leave_prompt_title)
-                .setMessage(R.string.room_participants_leave_prompt_msg)
+                .setMessage(message)
                 .setPositiveButton(R.string.leave) { _, _ ->
                     roomProfileViewModel.handle(RoomProfileAction.LeaveRoom)
                 }
                 .setNegativeButton(R.string.cancel, null)
                 .show()
+                .apply {
+                    if (!isPublicRoom) {
+                        withColoredButton(DialogInterface.BUTTON_POSITIVE)
+                    }
+                }
     }
 
     override fun onRoomIdClicked() {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
index ec772ffcaa..cfdf1d9c0b 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt
@@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
+import org.matrix.android.sdk.api.session.room.state.isPublic
 import org.matrix.android.sdk.rx.RxRoom
 import org.matrix.android.sdk.rx.rx
 import org.matrix.android.sdk.rx.unwrap
@@ -109,6 +110,10 @@ class RoomProfileViewModel @AssistedInject constructor(
         }.exhaustive
     }
 
+    fun isPublicRoom(): Boolean {
+        return room.isPublic()
+    }
+
     private fun handleEnableEncryption() {
         postLoading(true)
 
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 355ac4d6d6..4504febd1a 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -491,6 +491,7 @@
     <!--  Chat participants -->
     <string name="room_participants_leave_prompt_title">Leave room</string>
     <string name="room_participants_leave_prompt_msg">Are you sure you want to leave the room?</string>
+    <string name="room_participants_leave_private_warning">This room is not public. You will not be able to rejoin without an invite.</string>
     <string name="room_participants_remove_prompt_msg">Are you sure you want to remove %s from this chat?</string>
     <string name="room_participants_create">Create</string>
 

From 18d4b66c9779999f572f25750a904e4dfbdbec7f Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 5 Jan 2021 15:06:50 +0300
Subject: [PATCH 37/41] Fix copy topic on long click.

---
 .../main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
index eb8d4a41c4..218a22533a 100644
--- a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
+++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt
@@ -26,6 +26,7 @@ import androidx.core.view.isVisible
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
+import im.vector.app.core.extensions.copyOnLongClick
 
 @EpoxyModelClass(layout = R.layout.item_expandable_textview)
 abstract class ExpandableTextItem : VectorEpoxyModel<ExpandableTextItem.Holder>() {
@@ -45,6 +46,7 @@ abstract class ExpandableTextItem : VectorEpoxyModel<ExpandableTextItem.Holder>(
     override fun bind(holder: Holder) {
         super.bind(holder)
         holder.content.text = content
+        holder.content.copyOnLongClick()
         holder.content.movementMethod = movementMethod
 
         holder.content.doOnPreDraw {

From 963c30a27562508c401c0f32c5faf5a4ca57bb1d Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 5 Jan 2021 18:55:09 +0100
Subject: [PATCH 38/41] Remove dependency to org.greenrobot.eventbus library

---
 CHANGES.md                                    |  2 +-
 matrix-sdk-android/build.gradle               |  3 -
 matrix-sdk-android/proguard-rules.pro         |  8 +--
 .../internal/network/GlobalErrorReceiver.kt}  | 18 ++----
 .../crypto/CancelGossipRequestWorker.kt       |  2 -
 .../crypto/SendGossipRequestWorker.kt         |  2 -
 .../sdk/internal/crypto/SendGossipWorker.kt   |  2 -
 .../tasks/CreateKeysBackupVersionTask.kt      |  6 +-
 .../keysbackup/tasks/DeleteBackupTask.kt      |  6 +-
 .../tasks/DeleteRoomSessionDataTask.kt        |  6 +-
 .../tasks/DeleteRoomSessionsDataTask.kt       |  6 +-
 .../tasks/DeleteSessionsDataTask.kt           |  6 +-
 .../tasks/GetKeysBackupLastVersionTask.kt     |  6 +-
 .../tasks/GetKeysBackupVersionTask.kt         |  6 +-
 .../tasks/GetRoomSessionDataTask.kt           |  6 +-
 .../tasks/GetRoomSessionsDataTask.kt          |  6 +-
 .../keysbackup/tasks/GetSessionsDataTask.kt   |  6 +-
 .../tasks/StoreRoomSessionDataTask.kt         |  6 +-
 .../tasks/StoreRoomSessionsDataTask.kt        |  6 +-
 .../keysbackup/tasks/StoreSessionsDataTask.kt |  6 +-
 .../tasks/UpdateKeysBackupVersionTask.kt      |  6 +-
 .../ClaimOneTimeKeysForUsersDeviceTask.kt     |  6 +-
 .../internal/crypto/tasks/DeleteDeviceTask.kt |  6 +-
 .../tasks/DeleteDeviceWithUserPasswordTask.kt |  6 +-
 .../crypto/tasks/DownloadKeysForUsersTask.kt  |  6 +-
 .../crypto/tasks/GetDeviceInfoTask.kt         |  6 +-
 .../internal/crypto/tasks/GetDevicesTask.kt   |  6 +-
 .../crypto/tasks/GetKeyChangesTask.kt         |  6 +-
 .../internal/crypto/tasks/RedactEventTask.kt  |  6 +-
 .../internal/crypto/tasks/SendEventTask.kt    |  6 +-
 .../internal/crypto/tasks/SendToDeviceTask.kt |  6 +-
 .../tasks/SendVerificationMessageTask.kt      |  6 +-
 .../crypto/tasks/SetDeviceNameTask.kt         |  6 +-
 .../internal/crypto/tasks/UploadKeysTask.kt   |  6 +-
 .../crypto/tasks/UploadSignaturesTask.kt      |  6 +-
 .../crypto/tasks/UploadSigningKeysTask.kt     |  6 +-
 .../internal/network/GlobalErrorHandler.kt    | 54 ++++++++++++++++
 .../android/sdk/internal/network/Request.kt   |  9 ++-
 .../internal/network/RetrofitExtensions.kt    | 19 +++---
 .../sdk/internal/session/DefaultSession.kt    | 30 +++------
 .../sdk/internal/session/SessionModule.kt     | 17 ++---
 .../session/account/ChangePasswordTask.kt     |  8 +--
 .../session/account/DeactivateAccountTask.kt  |  6 +-
 .../session/call/GetTurnServerTask.kt         |  6 +-
 .../internal/session/content/FileUploader.kt  |  6 +-
 .../internal/session/filter/SaveFilterTask.kt |  6 +-
 .../session/group/GetGroupDataTask.kt         | 10 +--
 .../GetHomeServerCapabilitiesTask.kt          |  8 +--
 .../session/media/GetPreviewUrlTask.kt        |  6 +-
 .../session/media/GetRawPreviewUrlTask.kt     |  6 +-
 .../session/openid/GetOpenIdTokenTask.kt      |  6 +-
 .../session/profile/AddThreePidTask.kt        |  8 +--
 .../session/profile/BindThreePidsTask.kt      |  6 +-
 .../session/profile/DeleteThreePidTask.kt     |  6 +-
 .../profile/FinalizeAddingThreePidTask.kt     |  6 +-
 .../session/profile/GetProfileInfoTask.kt     |  6 +-
 .../profile/RefreshUserThreePidsTask.kt       |  6 +-
 .../session/profile/SetAvatarUrlTask.kt       |  6 +-
 .../session/profile/SetDisplayNameTask.kt     |  6 +-
 .../session/profile/UnbindThreePidsTask.kt    |  6 +-
 .../session/profile/ValidateSmsCodeTask.kt    |  6 +-
 .../session/pushers/AddHttpPusherWorker.kt    |  6 +-
 .../session/pushers/AddPushRuleTask.kt        |  6 +-
 .../session/pushers/GetPushRulesTask.kt       |  6 +-
 .../session/pushers/GetPushersTask.kt         |  6 +-
 .../session/pushers/RemovePushRuleTask.kt     |  6 +-
 .../session/pushers/RemovePusherTask.kt       |  6 +-
 .../pushers/UpdatePushRuleActionsTask.kt      |  8 +--
 .../pushers/UpdatePushRuleEnableStatusTask.kt |  6 +-
 .../session/room/alias/AddRoomAliasTask.kt    |  6 +-
 .../session/room/alias/DeleteRoomAliasTask.kt |  6 +-
 .../room/alias/GetRoomIdByAliasTask.kt        |  6 +-
 .../room/alias/GetRoomLocalAliasesTask.kt     |  6 +-
 .../alias/RoomAliasAvailabilityChecker.kt     |  6 +-
 .../session/room/create/CreateRoomTask.kt     |  6 +-
 .../room/directory/GetPublicRoomTask.kt       |  6 +-
 .../GetRoomDirectoryVisibilityTask.kt         |  6 +-
 .../directory/GetThirdPartyProtocolsTask.kt   |  6 +-
 .../SetRoomDirectoryVisibilityTask.kt         |  6 +-
 .../room/membership/LoadRoomMembersTask.kt    |  6 +-
 .../room/membership/joining/InviteTask.kt     |  6 +-
 .../room/membership/joining/JoinRoomTask.kt   |  6 +-
 .../room/membership/leaving/LeaveRoomTask.kt  |  6 +-
 .../membership/threepid/InviteThreePidTask.kt |  6 +-
 .../room/peeking/ResolveRoomStateTask.kt      |  6 +-
 .../session/room/read/SetReadMarkersTask.kt   |  6 +-
 .../room/relation/FetchEditHistoryTask.kt     |  6 +-
 .../room/relation/SendRelationWorker.kt       |  6 +-
 .../room/reporting/ReportContentTask.kt       |  6 +-
 .../session/room/send/LocalEchoRepository.kt  |  9 ++-
 .../session/room/send/RedactEventWorker.kt    |  6 +-
 .../session/room/send/SendEventWorker.kt      |  2 -
 .../session/room/state/SendStateTask.kt       |  6 +-
 .../session/room/tags/AddTagToRoomTask.kt     |  6 +-
 .../room/tags/DeleteTagFromRoomTask.kt        |  6 +-
 .../session/room/timeline/DefaultTimeline.kt  | 62 ++++++++-----------
 .../room/timeline/DefaultTimelineService.kt   |  5 +-
 .../timeline/FetchTokenAndPaginateTask.kt     |  6 +-
 .../room/timeline/GetContextOfEventTask.kt    |  6 +-
 .../session/room/timeline/GetEventTask.kt     |  6 +-
 .../session/room/timeline/PaginationTask.kt   |  6 +-
 .../session/room/timeline/TimelineInput.kt    | 45 ++++++++++++++
 .../session/room/typing/SendTypingTask.kt     |  6 +-
 .../session/room/uploads/GetUploadsTask.kt    |  6 +-
 .../sdk/internal/session/search/SearchTask.kt |  6 +-
 .../session/signout/SignInAgainTask.kt        |  6 +-
 .../internal/session/signout/SignOutTask.kt   |  6 +-
 .../internal/session/sync/RoomSyncHandler.kt  |  7 +--
 .../sdk/internal/session/sync/SyncTask.kt     |  6 +-
 .../accountdata/UpdateIgnoredUserIdsTask.kt   |  6 +-
 .../accountdata/UpdateUserAccountDataTask.kt  |  6 +-
 .../session/user/model/SearchUserTask.kt      |  6 +-
 .../session/widgets/CreateWidgetTask.kt       |  6 +-
 .../src/main/assets/open_source_licenses.html |  5 --
 114 files changed, 458 insertions(+), 425 deletions(-)
 rename matrix-sdk-android/src/{main/java/org/matrix/android/sdk/internal/eventbus/EventBusTimberLogger.kt => debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt} (58%)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorHandler.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt

diff --git a/CHANGES.md b/CHANGES.md
index 7d4678fe40..56c7a1c1a8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,7 +21,7 @@ SDK API changes ⚠️:
  -
 
 Build 🧱:
- -
+ - Remove dependency to org.greenrobot.eventbus library
 
 Test:
  -
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index d72e5bda41..6a2f7575e5 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -167,9 +167,6 @@ dependencies {
     implementation 'com.jakewharton.timber:timber:4.7.1'
     implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
 
-    // Bus
-    implementation 'org.greenrobot:eventbus:3.1.1'
-
     // Phone number https://github.com/google/libphonenumber
     implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
 
diff --git a/matrix-sdk-android/proguard-rules.pro b/matrix-sdk-android/proguard-rules.pro
index fa860d8049..182f9473e8 100644
--- a/matrix-sdk-android/proguard-rules.pro
+++ b/matrix-sdk-android/proguard-rules.pro
@@ -20,14 +20,8 @@
 # hide the original source file name.
 #-renamesourcefileattribute SourceFile
 
-
-### EVENT BUS ###
-
+# BMA: Not sure I can delete this one without side effect
 -keepattributes *Annotation*
--keepclassmembers class * {
-    @org.greenrobot.eventbus.Subscribe <methods>;
-}
--keep enum org.greenrobot.eventbus.ThreadMode { *; }
 
 ### MOSHI ###
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/eventbus/EventBusTimberLogger.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt
similarity index 58%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/eventbus/EventBusTimberLogger.kt
rename to matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt
index 2cbd7ba7f0..47607ba893 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/eventbus/EventBusTimberLogger.kt
+++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2021 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,10 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.eventbus
+package org.matrix.android.sdk.internal.network
 
-import org.greenrobot.eventbus.Logger
-import timber.log.Timber
-import java.util.logging.Level
+import org.matrix.android.sdk.api.failure.GlobalError
 
-class EventBusTimberLogger : Logger {
-    override fun log(level: Level, msg: String) {
-        Timber.d(msg)
-    }
-
-    override fun log(level: Level, msg: String, th: Throwable) {
-        Timber.e(th, msg)
-    }
+internal interface GlobalErrorReceiver {
+    fun handleGlobalError(globalError: GlobalError)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
index c2c81894fb..7e92ff3e88 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
@@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
 import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.failure.shouldBeRetried
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -60,7 +59,6 @@ internal class CancelGossipRequestWorker(context: Context,
 
     @Inject lateinit var sendToDeviceTask: SendToDeviceTask
     @Inject lateinit var cryptoStore: IMXCryptoStore
-    @Inject lateinit var eventBus: EventBus
     @Inject lateinit var credentials: Credentials
 
     override fun injectWith(injector: SessionComponent) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
index 085469e9d9..e8d567b944 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
@@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
 import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.failure.shouldBeRetried
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -52,7 +51,6 @@ internal class SendGossipRequestWorker(context: Context,
 
     @Inject lateinit var sendToDeviceTask: SendToDeviceTask
     @Inject lateinit var cryptoStore: IMXCryptoStore
-    @Inject lateinit var eventBus: EventBus
     @Inject lateinit var credentials: Credentials
 
     override fun injectWith(injector: SessionComponent) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
index bcaa16f356..8c68057056 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
@@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
 import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.failure.shouldBeRetried
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -54,7 +53,6 @@ internal class SendGossipWorker(context: Context,
 
     @Inject lateinit var sendToDeviceTask: SendToDeviceTask
     @Inject lateinit var cryptoStore: IMXCryptoStore
-    @Inject lateinit var eventBus: EventBus
     @Inject lateinit var credentials: Credentials
     @Inject lateinit var messageEncrypter: MessageEncrypter
     @Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt
index 36b667911d..5c59cfd80e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt
@@ -19,20 +19,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface CreateKeysBackupVersionTask : Task<CreateKeysBackupVersionBody, KeysVersion>
 
 internal class DefaultCreateKeysBackupVersionTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : CreateKeysBackupVersionTask {
 
     override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.createKeysBackupVersion(params)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt
index d174be20a2..ec09da7240 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
@@ -30,11 +30,11 @@ internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
 
 internal class DefaultDeleteBackupTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteBackupTask {
 
     override suspend fun execute(params: DeleteBackupTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.deleteBackup(params.version)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt
index 6826596ba4..9c477efb78 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Params, Unit> {
@@ -32,11 +32,11 @@ internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Pa
 
 internal class DefaultDeleteRoomSessionDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteRoomSessionDataTask {
 
     override suspend fun execute(params: DeleteRoomSessionDataTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.deleteRoomSessionData(
                     params.roomId,
                     params.sessionId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt
index 5c9aacc1ac..82d022f3ab 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.Params, Unit> {
@@ -31,11 +31,11 @@ internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.
 
 internal class DefaultDeleteRoomSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteRoomSessionsDataTask {
 
     override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.deleteRoomSessionsData(
                     params.roomId,
                     params.version)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt
index 3c9cab3fa0..e4df379963 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params, Unit> {
@@ -30,11 +30,11 @@ internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params,
 
 internal class DefaultDeleteSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteSessionsDataTask {
 
     override suspend fun execute(params: DeleteSessionsDataTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.deleteSessionsData(params.version)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt
index 25f8f85448..3566ff0e68 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt
@@ -18,20 +18,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetKeysBackupLastVersionTask : Task<Unit, KeysVersionResult>
 
 internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetKeysBackupLastVersionTask {
 
     override suspend fun execute(params: Unit): KeysVersionResult {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.getKeysBackupLastVersion()
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt
index dd2dd70e4d..13c99fb0f4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt
@@ -18,20 +18,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetKeysBackupVersionTask : Task<String, KeysVersionResult>
 
 internal class DefaultGetKeysBackupVersionTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetKeysBackupVersionTask {
 
     override suspend fun execute(params: String): KeysVersionResult {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.getKeysBackupVersion(params)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt
index 8ca03491a1..168020d9cd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params, KeyBackupData> {
@@ -33,11 +33,11 @@ internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params,
 
 internal class DefaultGetRoomSessionDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRoomSessionDataTask {
 
     override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.getRoomSessionData(
                     params.roomId,
                     params.sessionId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt
index d4c28418b5..95d5ef2e53 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params, RoomKeysBackupData> {
@@ -32,11 +32,11 @@ internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params
 
 internal class DefaultGetRoomSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRoomSessionsDataTask {
 
     override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.getRoomSessionsData(
                     params.roomId,
                     params.version)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt
index c5df82b5ae..e41a13e3eb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBackupData> {
@@ -31,11 +31,11 @@ internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBa
 
 internal class DefaultGetSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetSessionsDataTask {
 
     override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.getSessionsData(params.version)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt
index 588a861a8a..3954277e39 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Params, BackupKeysResult> {
@@ -35,11 +35,11 @@ internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Para
 
 internal class DefaultStoreRoomSessionDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : StoreRoomSessionDataTask {
 
     override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.storeRoomSessionData(
                     params.roomId,
                     params.sessionId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt
index b77e31e387..4e209b4abc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Params, BackupKeysResult> {
@@ -34,11 +34,11 @@ internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Pa
 
 internal class DefaultStoreRoomSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : StoreRoomSessionsDataTask {
 
     override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.storeRoomSessionsData(
                     params.roomId,
                     params.version,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt
index 3a8198073e..a607477d21 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, BackupKeysResult> {
@@ -33,11 +33,11 @@ internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, Ba
 
 internal class DefaultStoreSessionsDataTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : StoreSessionsDataTask {
 
     override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.storeSessionsData(
                     params.version,
                     params.keysBackupData)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt
index 50726c66ac..f012cd13eb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
 
 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UpdateKeysBackupVersionTask : Task<UpdateKeysBackupVersionTask.Params, Unit> {
@@ -32,11 +32,11 @@ internal interface UpdateKeysBackupVersionTask : Task<UpdateKeysBackupVersionTas
 
 internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(
         private val roomKeysApi: RoomKeysApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UpdateKeysBackupVersionTask {
 
     override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt
index ae72c7198a..3df6312adb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt
@@ -21,9 +21,9 @@ import org.matrix.android.sdk.internal.crypto.model.MXKey
 import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -36,13 +36,13 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysFor
 
 internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : ClaimOneTimeKeysForUsersDeviceTask {
 
     override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap<MXKey> {
         val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
 
-        val keysClaimResponse = executeRequest<KeysClaimResponse>(eventBus) {
+        val keysClaimResponse = executeRequest<KeysClaimResponse>(globalErrorReceiver) {
             apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
         }
         val map = MXUsersDevicesMap<MXKey>()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
index e5078d5b4e..8f1569a037 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt
@@ -20,9 +20,9 @@ import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
@@ -33,12 +33,12 @@ internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
 
 internal class DefaultDeleteDeviceTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteDeviceTask {
 
     override suspend fun execute(params: DeleteDeviceTask.Params) {
         try {
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
             }
         } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
index 38eee7f932..b4c1e6d27c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt
@@ -21,9 +21,9 @@ import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
 import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserPasswordTask.Params, Unit> {
@@ -37,11 +37,11 @@ internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserP
 internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(
         private val cryptoApi: CryptoApi,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteDeviceWithUserPasswordTask {
 
     override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.deleteDevice(params.deviceId,
                     DeleteDeviceParams(
                             userPasswordAuth = UserPasswordAuth(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DownloadKeysForUsersTask.kt
index 7268c48113..5eb24b116a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DownloadKeysForUsersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DownloadKeysForUsersTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryBody
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DownloadKeysForUsersTask : Task<DownloadKeysForUsersTask.Params, KeysQueryResponse> {
@@ -35,7 +35,7 @@ internal interface DownloadKeysForUsersTask : Task<DownloadKeysForUsersTask.Para
 
 internal class DefaultDownloadKeysForUsers @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DownloadKeysForUsersTask {
 
     override suspend fun execute(params: DownloadKeysForUsersTask.Params): KeysQueryResponse {
@@ -46,7 +46,7 @@ internal class DefaultDownloadKeysForUsers @Inject constructor(
                 token = params.token?.takeIf { it.isNotEmpty() }
         )
 
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.downloadKeysForUsers(body)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDeviceInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDeviceInfoTask.kt
index 6bd69c6a07..5f6d2e344f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDeviceInfoTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDeviceInfoTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetDeviceInfoTask : Task<GetDeviceInfoTask.Params, DeviceInfo> {
@@ -29,11 +29,11 @@ internal interface GetDeviceInfoTask : Task<GetDeviceInfoTask.Params, DeviceInfo
 
 internal class DefaultGetDeviceInfoTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetDeviceInfoTask {
 
     override suspend fun execute(params: GetDeviceInfoTask.Params): DeviceInfo {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.getDeviceInfo(params.deviceId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDevicesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDevicesTask.kt
index 731e1ca031..ea33a918bc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDevicesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetDevicesTask.kt
@@ -18,20 +18,20 @@ package org.matrix.android.sdk.internal.crypto.tasks
 
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetDevicesTask : Task<Unit, DevicesListResponse>
 
 internal class DefaultGetDevicesTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetDevicesTask {
 
     override suspend fun execute(params: Unit): DevicesListResponse {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.getDevices()
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetKeyChangesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetKeyChangesTask.kt
index 289a5226f5..4cc9ab2fcb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetKeyChangesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/GetKeyChangesTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.KeyChangesResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetKeyChangesTask : Task<GetKeyChangesTask.Params, KeyChangesResponse> {
@@ -34,11 +34,11 @@ internal interface GetKeyChangesTask : Task<GetKeyChangesTask.Params, KeyChanges
 
 internal class DefaultGetKeyChangesTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetKeyChangesTask {
 
     override suspend fun execute(params: GetKeyChangesTask.Params): KeyChangesResponse {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.getKeyChanges(params.from, params.to)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt
index f35d1b63e8..5226e52b33 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt
@@ -15,7 +15,7 @@
  */
 package org.matrix.android.sdk.internal.crypto.tasks
 
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.send.SendResponse
@@ -33,10 +33,10 @@ internal interface RedactEventTask : Task<RedactEventTask.Params, String> {
 
 internal class DefaultRedactEventTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus) : RedactEventTask {
+        private val globalErrorReceiver: GlobalErrorReceiver) : RedactEventTask {
 
     override suspend fun execute(params: RedactEventTask.Params): String {
-        val executeRequest = executeRequest<SendResponse>(eventBus) {
+        val executeRequest = executeRequest<SendResponse>(globalErrorReceiver) {
             apiCall = roomAPI.redactEvent(
                     txId = params.txID,
                     roomId = params.roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
index 5c8c7dfb25..b772bfbce2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt
@@ -15,9 +15,9 @@
  */
 package org.matrix.android.sdk.internal.crypto.tasks
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
@@ -38,7 +38,7 @@ internal class DefaultSendEventTask @Inject constructor(
         private val encryptEventTask: DefaultEncryptEventTask,
         private val loadRoomMembersTask: LoadRoomMembersTask,
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus) : SendEventTask {
+        private val globalErrorReceiver: GlobalErrorReceiver) : SendEventTask {
 
     override suspend fun execute(params: SendEventTask.Params): String {
         try {
@@ -53,7 +53,7 @@ internal class DefaultSendEventTask @Inject constructor(
             val localId = event.eventId!!
 
             localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENDING)
-            val executeRequest = executeRequest<SendResponse>(eventBus) {
+            val executeRequest = executeRequest<SendResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.send(
                         localId,
                         roomId = event.roomId ?: "",
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt
index 37e0bbc887..d2af91601b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
 import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 import kotlin.random.Random
 
@@ -38,7 +38,7 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
 
 internal class DefaultSendToDeviceTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SendToDeviceTask {
 
     override suspend fun execute(params: SendToDeviceTask.Params) {
@@ -46,7 +46,7 @@ internal class DefaultSendToDeviceTask @Inject constructor(
                 messages = params.contentMap.map
         )
 
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.sendToDevice(
                     params.eventType,
                     params.transactionId ?: Random.nextInt(Integer.MAX_VALUE).toString(),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt
index cedb7a6618..c39dfb1016 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt
@@ -15,10 +15,10 @@
  */
 package org.matrix.android.sdk.internal.crypto.tasks
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
@@ -37,7 +37,7 @@ internal class DefaultSendVerificationMessageTask @Inject constructor(
         private val encryptEventTask: DefaultEncryptEventTask,
         private val roomAPI: RoomAPI,
         private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
-        private val eventBus: EventBus) : SendVerificationMessageTask {
+        private val globalErrorReceiver: GlobalErrorReceiver) : SendVerificationMessageTask {
 
     override suspend fun execute(params: SendVerificationMessageTask.Params): String {
         val event = handleEncryption(params)
@@ -45,7 +45,7 @@ internal class DefaultSendVerificationMessageTask @Inject constructor(
 
         try {
             localEchoRepository.updateSendState(localId, event.roomId, SendState.SENDING)
-            val executeRequest = executeRequest<SendResponse>(eventBus) {
+            val executeRequest = executeRequest<SendResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.send(
                         localId,
                         roomId = event.roomId ?: "",
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SetDeviceNameTask.kt
index 51b9624554..b835d46236 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SetDeviceNameTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SetDeviceNameTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.UpdateDeviceInfoBody
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface SetDeviceNameTask : Task<SetDeviceNameTask.Params, Unit> {
@@ -34,14 +34,14 @@ internal interface SetDeviceNameTask : Task<SetDeviceNameTask.Params, Unit> {
 
 internal class DefaultSetDeviceNameTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SetDeviceNameTask {
 
     override suspend fun execute(params: SetDeviceNameTask.Params) {
         val body = UpdateDeviceInfoBody(
                 displayName = params.deviceName
         )
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
index 6216a3a305..eb53bbbf8d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
@@ -21,9 +21,9 @@ import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.DeviceKeys
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
 import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -38,7 +38,7 @@ internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadRespon
 
 internal class DefaultUploadKeysTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UploadKeysTask {
 
     override suspend fun execute(params: UploadKeysTask.Params): KeysUploadResponse {
@@ -49,7 +49,7 @@ internal class DefaultUploadKeysTask @Inject constructor(
 
         Timber.i("## Uploading device keys -> $body")
 
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = cryptoApi.uploadKeys(body)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSignaturesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSignaturesTask.kt
index a4e10ddbfc..c50faf37b1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSignaturesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSignaturesTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.tasks
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.internal.crypto.api.CryptoApi
 import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UploadSignaturesTask : Task<UploadSignaturesTask.Params, Unit> {
@@ -31,12 +31,12 @@ internal interface UploadSignaturesTask : Task<UploadSignaturesTask.Params, Unit
 
 internal class DefaultUploadSignaturesTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UploadSignaturesTask {
 
     override suspend fun execute(params: UploadSignaturesTask.Params) {
         try {
-            val response = executeRequest<SignatureUploadResponse>(eventBus) {
+            val response = executeRequest<SignatureUploadResponse>(globalErrorReceiver) {
                 this.isRetryable = true
                 this.maxRetryCount = 10
                 this.apiCall = cryptoApi.uploadSignatures(params.signatures)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
index 038ef9dbd3..cceff355bb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt
@@ -25,9 +25,9 @@ import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
 import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody
 import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
 import org.matrix.android.sdk.internal.crypto.model.toRest
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UploadSigningKeysTask : Task<UploadSigningKeysTask.Params, Unit> {
@@ -55,7 +55,7 @@ data class UploadSigningKeys(val failures: Map<String, Any>?) : Failure.FeatureF
 
 internal class DefaultUploadSigningKeysTask @Inject constructor(
         private val cryptoApi: CryptoApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UploadSigningKeysTask {
 
     override suspend fun execute(params: UploadSigningKeysTask.Params) {
@@ -87,7 +87,7 @@ internal class DefaultUploadSigningKeysTask @Inject constructor(
     }
 
     private suspend fun doRequest(uploadQuery: UploadSigningKeysBody) {
-        val keysQueryResponse = executeRequest<KeysQueryResponse>(eventBus) {
+        val keysQueryResponse = executeRequest<KeysQueryResponse>(globalErrorReceiver) {
             apiCall = cryptoApi.uploadSigningKeys(uploadQuery)
         }
         if (keysQueryResponse.failures?.isNotEmpty() == true) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorHandler.kt
new file mode 100644
index 0000000000..9afdb40ed1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorHandler.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.network
+
+import org.matrix.android.sdk.api.failure.GlobalError
+import org.matrix.android.sdk.internal.auth.SessionParamsStore
+import org.matrix.android.sdk.internal.di.SessionId
+import org.matrix.android.sdk.internal.session.SessionScope
+import org.matrix.android.sdk.internal.task.TaskExecutor
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+@SessionScope
+internal class GlobalErrorHandler @Inject constructor(
+        private val taskExecutor: TaskExecutor,
+        private val sessionParamsStore: SessionParamsStore,
+        @SessionId private val sessionId: String
+) : GlobalErrorReceiver {
+
+    var listener: Listener? = null
+
+    override fun handleGlobalError(globalError: GlobalError) {
+        Timber.e("Global error received: $globalError")
+
+        if (globalError is GlobalError.InvalidToken && globalError.softLogout) {
+            // Mark the token has invalid
+            taskExecutor.executorScope.launch(Dispatchers.IO) {
+                sessionParamsStore.setTokenInvalid(sessionId)
+            }
+        }
+
+        listener?.onGlobalError(globalError)
+    }
+
+    internal interface Listener {
+        fun onGlobalError(globalError: GlobalError)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
index 2535a5347a..442029127d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.network
 
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.delay
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.shouldBeRetried
 import org.matrix.android.sdk.internal.network.ssl.CertUtil
@@ -27,10 +26,10 @@ import retrofit2.awaitResponse
 import timber.log.Timber
 import java.io.IOException
 
-internal suspend inline fun <DATA : Any> executeRequest(eventBus: EventBus?,
-                                                        block: Request<DATA>.() -> Unit) = Request<DATA>(eventBus).apply(block).execute()
+internal suspend inline fun <DATA : Any> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
+                                                        block: Request<DATA>.() -> Unit) = Request<DATA>(globalErrorReceiver).apply(block).execute()
 
-internal class Request<DATA : Any>(private val eventBus: EventBus?) {
+internal class Request<DATA : Any>(private val globalErrorReceiver: GlobalErrorReceiver?) {
 
     var isRetryable = false
     var initialDelay: Long = 100L
@@ -47,7 +46,7 @@ internal class Request<DATA : Any>(private val eventBus: EventBus?) {
                 response.body()
                         ?: throw IllegalStateException("The request returned a null body")
             } else {
-                throw response.toFailure(eventBus)
+                throw response.toFailure(globalErrorReceiver)
             }
         } catch (exception: Throwable) {
             // Log some details about the request which has failed
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt
index c54af571d8..dd5a69dd3c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt
@@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.failure.MatrixError
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import kotlinx.coroutines.suspendCancellableCoroutine
 import okhttp3.ResponseBody
-import org.greenrobot.eventbus.EventBus
 import retrofit2.Response
 import timber.log.Timber
 import java.io.IOException
@@ -54,18 +53,18 @@ internal suspend fun okhttp3.Call.awaitResponse(): okhttp3.Response {
 /**
  * Convert a retrofit Response to a Failure, and eventually parse errorBody to convert it to a MatrixError
  */
-internal fun <T> Response<T>.toFailure(eventBus: EventBus?): Failure {
-    return toFailure(errorBody(), code(), eventBus)
+internal fun <T> Response<T>.toFailure(globalErrorReceiver: GlobalErrorReceiver?): Failure {
+    return toFailure(errorBody(), code(), globalErrorReceiver)
 }
 
 /**
  * Convert a okhttp3 Response to a Failure, and eventually parse errorBody to convert it to a MatrixError
  */
-internal fun okhttp3.Response.toFailure(eventBus: EventBus?): Failure {
-    return toFailure(body, code, eventBus)
+internal fun okhttp3.Response.toFailure(globalErrorReceiver: GlobalErrorReceiver?): Failure {
+    return toFailure(body, code, globalErrorReceiver)
 }
 
-private fun toFailure(errorBody: ResponseBody?, httpCode: Int, eventBus: EventBus?): Failure {
+private fun toFailure(errorBody: ResponseBody?, httpCode: Int, globalErrorReceiver: GlobalErrorReceiver?): Failure {
     if (errorBody == null) {
         return Failure.Unknown(RuntimeException("errorBody should not be null"))
     }
@@ -79,12 +78,12 @@ private fun toFailure(errorBody: ResponseBody?, httpCode: Int, eventBus: EventBu
 
         if (matrixError != null) {
             if (matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank()) {
-                // Also send this error to the bus, for a global management
-                eventBus?.post(GlobalError.ConsentNotGivenError(matrixError.consentUri))
+                // Also send this error to the globalErrorReceiver, for a global management
+                globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri))
             } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */
                     && matrixError.code == MatrixError.M_UNKNOWN_TOKEN) {
-                // Also send this error to the bus, for a global management
-                eventBus?.post(GlobalError.InvalidToken(matrixError.isSoftLogout))
+                // Also send this error to the globalErrorReceiver, for a global management
+                globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout))
             }
 
             return Failure.ServerError(matrixError, httpCode)
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 c5f3f65a34..fa07b16c32 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
@@ -19,12 +19,7 @@ package org.matrix.android.sdk.internal.session
 import androidx.annotation.MainThread
 import dagger.Lazy
 import io.realm.RealmConfiguration
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
 import okhttp3.OkHttpClient
-import org.greenrobot.eventbus.EventBus
-import org.greenrobot.eventbus.Subscribe
-import org.greenrobot.eventbus.ThreadMode
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.auth.data.SessionParams
 import org.matrix.android.sdk.api.failure.GlobalError
@@ -65,13 +60,12 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.SessionId
 import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
 import org.matrix.android.sdk.internal.di.WorkManagerProvider
+import org.matrix.android.sdk.internal.network.GlobalErrorHandler
 import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
 import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
 import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
 import org.matrix.android.sdk.internal.session.sync.job.SyncThread
 import org.matrix.android.sdk.internal.session.sync.job.SyncWorker
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
 import org.matrix.android.sdk.internal.util.createUIHandler
 import timber.log.Timber
 import javax.inject.Inject
@@ -81,7 +75,7 @@ import javax.inject.Provider
 internal class DefaultSession @Inject constructor(
         override val sessionParams: SessionParams,
         private val workManagerProvider: WorkManagerProvider,
-        private val eventBus: EventBus,
+        private val globalErrorHandler: GlobalErrorHandler,
         @SessionId
         override val sessionId: String,
         @SessionDatabase private val realmConfiguration: RealmConfiguration,
@@ -117,10 +111,8 @@ internal class DefaultSession @Inject constructor(
         private val accountDataService: Lazy<AccountDataService>,
         private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
         private val accountService: Lazy<AccountService>,
-        private val coroutineDispatchers: MatrixCoroutineDispatchers,
         private val defaultIdentityService: DefaultIdentityService,
         private val integrationManagerService: IntegrationManagerService,
-        private val taskExecutor: TaskExecutor,
         private val callSignalingService: Lazy<CallSignalingService>,
         @UnauthenticatedWithCertificate
         private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>,
@@ -140,7 +132,8 @@ internal class DefaultSession @Inject constructor(
         HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
         ProfileService by profileService.get(),
         AccountDataService by accountDataService.get(),
-        AccountService by accountService.get() {
+        AccountService by accountService.get(),
+        GlobalErrorHandler.Listener {
 
     override val sharedSecretStorageService: SharedSecretStorageService
         get() = _sharedSecretStorageService.get()
@@ -162,7 +155,7 @@ internal class DefaultSession @Inject constructor(
         uiHandler.post {
             lifecycleObservers.forEach { it.onStart() }
         }
-        eventBus.register(this)
+        globalErrorHandler.listener = this
         eventSenderProcessor.start()
     }
 
@@ -206,7 +199,7 @@ internal class DefaultSession @Inject constructor(
         }
         cryptoService.get().close()
         isOpen = false
-        eventBus.unregister(this)
+        globalErrorHandler.listener = null
         eventSenderProcessor.interrupt()
     }
 
@@ -234,16 +227,7 @@ internal class DefaultSession @Inject constructor(
         workManagerProvider.cancelAllWorks()
     }
 
-    @Subscribe(threadMode = ThreadMode.MAIN)
-    fun onGlobalError(globalError: GlobalError) {
-        if (globalError is GlobalError.InvalidToken
-                && globalError.softLogout) {
-            // Mark the token has invalid
-            taskExecutor.executorScope.launch(Dispatchers.IO) {
-                sessionParamsStore.setTokenInvalid(sessionId)
-            }
-        }
-
+    override fun onGlobalError(globalError: GlobalError) {
         sessionListeners.dispatchGlobalError(globalError)
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index 96b44917bd..468c193ad3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -26,7 +26,6 @@ import dagger.Provides
 import dagger.multibindings.IntoSet
 import io.realm.RealmConfiguration
 import okhttp3.OkHttpClient
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.MatrixConfiguration
 import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -61,9 +60,10 @@ import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
 import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.di.UserMd5
-import org.matrix.android.sdk.internal.eventbus.EventBusTimberLogger
 import org.matrix.android.sdk.internal.network.DefaultNetworkConnectivityChecker
 import org.matrix.android.sdk.internal.network.FallbackNetworkCallbackStrategy
+import org.matrix.android.sdk.internal.network.GlobalErrorHandler
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.NetworkCallbackStrategy
 import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
 import org.matrix.android.sdk.internal.network.PreferredNetworkCallbackStrategy
@@ -256,16 +256,6 @@ internal abstract class SessionModule {
                     .create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUri.toString())
         }
 
-        @JvmStatic
-        @Provides
-        @SessionScope
-        fun providesEventBus(): EventBus {
-            return EventBus
-                    .builder()
-                    .logger(EventBusTimberLogger())
-                    .build()
-        }
-
         @JvmStatic
         @Provides
         @SessionScope
@@ -294,6 +284,9 @@ internal abstract class SessionModule {
     @Binds
     abstract fun bindSession(session: DefaultSession): Session
 
+    @Binds
+    abstract fun bindGlobalErrorReceiver(handler: GlobalErrorHandler): GlobalErrorReceiver
+
     @Binds
     abstract fun bindNetworkConnectivityChecker(checker: DefaultNetworkConnectivityChecker): NetworkConnectivityChecker
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt
index 869d3f516c..1f043b0a9d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.account
 
 import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface ChangePasswordTask : Task<ChangePasswordTask.Params, Unit> {
@@ -32,14 +32,14 @@ internal interface ChangePasswordTask : Task<ChangePasswordTask.Params, Unit> {
 
 internal class DefaultChangePasswordTask @Inject constructor(
         private val accountAPI: AccountAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         @UserId private val userId: String
 ) : ChangePasswordTask {
 
     override suspend fun execute(params: ChangePasswordTask.Params) {
         val changePasswordParams = ChangePasswordParams.create(userId, params.password, params.newPassword)
         try {
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 apiCall = accountAPI.changePassword(changePasswordParams)
             }
         } catch (throwable: Throwable) {
@@ -49,7 +49,7 @@ internal class DefaultChangePasswordTask @Inject constructor(
                     /* Avoid infinite loop */
                     && changePasswordParams.auth?.session == null) {
                 // Retry with authentication
-                executeRequest<Unit>(eventBus) {
+                executeRequest<Unit>(globalErrorReceiver) {
                     apiCall = accountAPI.changePassword(
                             changePasswordParams.copy(auth = changePasswordParams.auth?.copy(session = registrationFlowResponse.session))
                     )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
index ac5febcdae..9fb1cbb7d7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/DeactivateAccountTask.kt
@@ -17,11 +17,11 @@
 package org.matrix.android.sdk.internal.session.account
 
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.cleanup.CleanupSession
 import org.matrix.android.sdk.internal.session.identity.IdentityDisconnectTask
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -34,7 +34,7 @@ internal interface DeactivateAccountTask : Task<DeactivateAccountTask.Params, Un
 
 internal class DefaultDeactivateAccountTask @Inject constructor(
         private val accountAPI: AccountAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         @UserId private val userId: String,
         private val identityDisconnectTask: IdentityDisconnectTask,
         private val cleanupSession: CleanupSession
@@ -43,7 +43,7 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
     override suspend fun execute(params: DeactivateAccountTask.Params) {
         val deactivateAccountParams = DeactivateAccountParams.create(userId, params.password, params.eraseAllData)
 
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = accountAPI.deactivate(deactivateAccountParams)
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/GetTurnServerTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/GetTurnServerTask.kt
index 1cedee3374..b21ec1113a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/GetTurnServerTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/GetTurnServerTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.session.call
 
 import org.matrix.android.sdk.api.session.call.TurnServerResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class GetTurnServerTask : Task<GetTurnServerTask.Params, TurnServerResponse> {
@@ -27,10 +27,10 @@ internal abstract class GetTurnServerTask : Task<GetTurnServerTask.Params, TurnS
 }
 
 internal class DefaultGetTurnServerTask @Inject constructor(private val voipAPI: VoipApi,
-                                                            private val eventBus: EventBus) : GetTurnServerTask() {
+                                                            private val globalErrorReceiver: GlobalErrorReceiver) : GetTurnServerTask() {
 
     override suspend fun execute(params: Params): TurnServerResponse {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = voipAPI.getTurnServer()
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
index 4fc1d67c05..8fa595db30 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
@@ -30,10 +30,10 @@ import okhttp3.RequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import okio.BufferedSink
 import okio.source
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentUrlResolver
 import org.matrix.android.sdk.internal.di.Authenticated
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.ProgressRequestBody
 import org.matrix.android.sdk.internal.network.awaitResponse
 import org.matrix.android.sdk.internal.network.toFailure
@@ -45,7 +45,7 @@ import javax.inject.Inject
 
 internal class FileUploader @Inject constructor(@Authenticated
                                                 private val okHttpClient: OkHttpClient,
-                                                private val eventBus: EventBus,
+                                                private val globalErrorReceiver: GlobalErrorReceiver,
                                                 private val context: Context,
                                                 contentUrlResolver: ContentUrlResolver,
                                                 moshi: Moshi) {
@@ -115,7 +115,7 @@ internal class FileUploader @Inject constructor(@Authenticated
 
         return okHttpClient.newCall(request).awaitResponse().use { response ->
             if (!response.isSuccessful) {
-                throw response.toFailure(eventBus)
+                throw response.toFailure(globalErrorReceiver)
             } else {
                 response.body?.source()?.let {
                     responseAdapter.fromJson(it)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt
index 69ced92fe5..da747934e2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.filter
 
 import org.matrix.android.sdk.api.session.sync.FilterService
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 /**
@@ -37,7 +37,7 @@ internal class DefaultSaveFilterTask @Inject constructor(
         @UserId private val userId: String,
         private val filterAPI: FilterApi,
         private val filterRepository: FilterRepository,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SaveFilterTask {
 
     override suspend fun execute(params: SaveFilterTask.Params) {
@@ -59,7 +59,7 @@ internal class DefaultSaveFilterTask @Inject constructor(
         }
         val updated = filterRepository.storeFilter(filterBody, roomFilter)
         if (updated) {
-            val filterResponse = executeRequest<FilterResponse>(eventBus) {
+            val filterResponse = executeRequest<FilterResponse>(globalErrorReceiver) {
                 // TODO auto retry
                 apiCall = filterAPI.uploadFilter(userId, filterBody)
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt
index dd703a5e93..9836164aec 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt
@@ -23,13 +23,13 @@ import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.group.model.GroupRooms
 import org.matrix.android.sdk.internal.session.group.model.GroupSummaryResponse
 import org.matrix.android.sdk.internal.session.group.model.GroupUsers
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -43,7 +43,7 @@ internal interface GetGroupDataTask : Task<GetGroupDataTask.Params, Unit> {
 internal class DefaultGetGroupDataTask @Inject constructor(
         private val groupAPI: GroupAPI,
         @SessionDatabase private val monarchy: Monarchy,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetGroupDataTask {
 
     private data class GroupData(
@@ -64,13 +64,13 @@ internal class DefaultGetGroupDataTask @Inject constructor(
         }
         Timber.v("Fetch data for group with ids: ${groupIds.joinToString(";")}")
         val data = groupIds.map { groupId ->
-            val groupSummary = executeRequest<GroupSummaryResponse>(eventBus) {
+            val groupSummary = executeRequest<GroupSummaryResponse>(globalErrorReceiver) {
                 apiCall = groupAPI.getSummary(groupId)
             }
-            val groupRooms = executeRequest<GroupRooms>(eventBus) {
+            val groupRooms = executeRequest<GroupRooms>(globalErrorReceiver) {
                 apiCall = groupAPI.getRooms(groupId)
             }
-            val groupUsers = executeRequest<GroupUsers>(eventBus) {
+            val groupUsers = executeRequest<GroupUsers>(globalErrorReceiver) {
                 apiCall = groupAPI.getUsers(groupId)
             }
             GroupData(groupId, groupSummary, groupRooms, groupUsers)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index f3686b02d3..845cfb392e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.internal.session.homeserver
 
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
@@ -27,6 +26,7 @@ import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEnti
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerConfigExtractor
 import org.matrix.android.sdk.internal.session.media.GetMediaConfigResult
@@ -44,7 +44,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
         private val capabilitiesAPI: CapabilitiesAPI,
         private val mediaAPI: MediaAPI,
         @SessionDatabase private val monarchy: Monarchy,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         private val getWellknownTask: GetWellknownTask,
         private val configExtractor: IntegrationManagerConfigExtractor,
         private val homeServerConnectionConfig: HomeServerConnectionConfig,
@@ -65,13 +65,13 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
         }
 
         val capabilities = runCatching {
-            executeRequest<GetCapabilitiesResult>(eventBus) {
+            executeRequest<GetCapabilitiesResult>(globalErrorReceiver) {
                 apiCall = capabilitiesAPI.getCapabilities()
             }
         }.getOrNull()
 
         val mediaConfig = runCatching {
-            executeRequest<GetMediaConfigResult>(eventBus) {
+            executeRequest<GetMediaConfigResult>(globalErrorReceiver) {
                 apiCall = mediaAPI.getMediaConfig()
             }
         }.getOrNull()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
index 69cdfa8faa..a218f3f93c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.internal.session.media
 
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.cache.CacheStrategy
 import org.matrix.android.sdk.api.session.media.PreviewUrlData
 import org.matrix.android.sdk.api.util.JsonDict
@@ -25,6 +24,7 @@ import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntity
 import org.matrix.android.sdk.internal.database.query.get
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -41,7 +41,7 @@ internal interface GetPreviewUrlTask : Task<GetPreviewUrlTask.Params, PreviewUrl
 
 internal class DefaultGetPreviewUrlTask @Inject constructor(
         private val mediaAPI: MediaAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         @SessionDatabase private val monarchy: Monarchy
 ) : GetPreviewUrlTask {
 
@@ -64,7 +64,7 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
     }
 
     private suspend fun doRequest(url: String, timestamp: Long?): PreviewUrlData {
-        return executeRequest<JsonDict>(eventBus) {
+        return executeRequest<JsonDict>(globalErrorReceiver) {
             apiCall = mediaAPI.getPreviewUrlData(url, timestamp)
         }
                 .toPreviewUrlData(url)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt
index 6c5dad2422..32305cd4e4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt
@@ -16,8 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.media
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import javax.inject.Inject
@@ -31,11 +31,11 @@ internal interface GetRawPreviewUrlTask : Task<GetRawPreviewUrlTask.Params, Json
 
 internal class DefaultGetRawPreviewUrlTask @Inject constructor(
         private val mediaAPI: MediaAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRawPreviewUrlTask {
 
     override suspend fun execute(params: GetRawPreviewUrlTask.Params): JsonDict {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = mediaAPI.getPreviewUrlData(params.url, params.timestamp)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/GetOpenIdTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/GetOpenIdTokenTask.kt
index 5d7cfd1719..f83c6b770a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/GetOpenIdTokenTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/GetOpenIdTokenTask.kt
@@ -17,9 +17,9 @@
 package org.matrix.android.sdk.internal.session.openid
 
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetOpenIdTokenTask : Task<Unit, RequestOpenIdTokenResponse>
@@ -27,10 +27,10 @@ internal interface GetOpenIdTokenTask : Task<Unit, RequestOpenIdTokenResponse>
 internal class DefaultGetOpenIdTokenTask @Inject constructor(
         @UserId private val userId: String,
         private val openIdAPI: OpenIdAPI,
-        private val eventBus: EventBus) : GetOpenIdTokenTask {
+        private val globalErrorReceiver: GlobalErrorReceiver) : GetOpenIdTokenTask {
 
     override suspend fun execute(params: Unit): RequestOpenIdTokenResponse {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = openIdAPI.openIdToken(userId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt
index 2e8a0b3884..6d6d70bb0d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt
@@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.profile
 
 import com.google.i18n.phonenumbers.PhoneNumberUtil
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -37,7 +37,7 @@ internal class DefaultAddThreePidTask @Inject constructor(
         private val profileAPI: ProfileAPI,
         @SessionDatabase private val monarchy: Monarchy,
         private val pendingThreePidMapper: PendingThreePidMapper,
-        private val eventBus: EventBus) : AddThreePidTask() {
+        private val globalErrorReceiver: GlobalErrorReceiver) : AddThreePidTask() {
 
     override suspend fun execute(params: Params) {
         when (params.threePid) {
@@ -50,7 +50,7 @@ internal class DefaultAddThreePidTask @Inject constructor(
         val clientSecret = UUID.randomUUID().toString()
         val sendAttempt = 1
 
-        val result = executeRequest<AddEmailResponse>(eventBus) {
+        val result = executeRequest<AddEmailResponse>(globalErrorReceiver) {
             val body = AddEmailBody(
                     clientSecret = clientSecret,
                     email = threePid.email,
@@ -84,7 +84,7 @@ internal class DefaultAddThreePidTask @Inject constructor(
         val countryCode = parsedNumber.countryCode
         val country = phoneNumberUtil.getRegionCodeForCountryCode(countryCode)
 
-        val result = executeRequest<AddMsisdnResponse>(eventBus) {
+        val result = executeRequest<AddMsisdnResponse>(globalErrorReceiver) {
             val body = AddMsisdnBody(
                     clientSecret = clientSecret,
                     country = country,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/BindThreePidsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/BindThreePidsTask.kt
index dbe6bff508..a37e5380bc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/BindThreePidsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/BindThreePidsTask.kt
@@ -19,12 +19,12 @@ package org.matrix.android.sdk.internal.session.profile
 import org.matrix.android.sdk.api.session.identity.IdentityServiceError
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
 import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
 import org.matrix.android.sdk.internal.session.identity.data.getIdentityServerUrlWithoutProtocol
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class BindThreePidsTask : Task<BindThreePidsTask.Params, Unit> {
@@ -37,13 +37,13 @@ internal class DefaultBindThreePidsTask @Inject constructor(private val profileA
                                                             private val identityStore: IdentityStore,
                                                             @AuthenticatedIdentity
                                                             private val accessTokenProvider: AccessTokenProvider,
-                                                            private val eventBus: EventBus) : BindThreePidsTask() {
+                                                            private val globalErrorReceiver: GlobalErrorReceiver) : BindThreePidsTask() {
     override suspend fun execute(params: Params) {
         val identityServerUrlWithoutProtocol = identityStore.getIdentityServerUrlWithoutProtocol() ?: throw IdentityServiceError.NoIdentityServerConfigured
         val identityServerAccessToken = accessTokenProvider.getToken() ?: throw IdentityServiceError.NoIdentityServerConfigured
         val identityPendingBinding = identityStore.getPendingBinding(params.threePid) ?: throw IdentityServiceError.NoCurrentBindingError
 
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = profileAPI.bindThreePid(
                     BindThreePidBody(
                             clientSecret = identityPendingBinding.clientSecret,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DeleteThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DeleteThreePidTask.kt
index 3f43cbe599..3549f3613f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DeleteThreePidTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DeleteThreePidTask.kt
@@ -16,9 +16,9 @@
 
 package org.matrix.android.sdk.internal.session.profile
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.api.session.identity.toMedium
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import javax.inject.Inject
@@ -31,10 +31,10 @@ internal abstract class DeleteThreePidTask : Task<DeleteThreePidTask.Params, Uni
 
 internal class DefaultDeleteThreePidTask @Inject constructor(
         private val profileAPI: ProfileAPI,
-        private val eventBus: EventBus) : DeleteThreePidTask() {
+        private val globalErrorReceiver: GlobalErrorReceiver) : DeleteThreePidTask() {
 
     override suspend fun execute(params: Params) {
-        executeRequest<DeleteThreePidResponse>(eventBus) {
+        executeRequest<DeleteThreePidResponse>(globalErrorReceiver) {
             val body = DeleteThreePidBody(
                     medium = params.threePid.toMedium(),
                     address = params.threePid.value
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
index 0b1bf88280..1e3a2cb501 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.internal.session.profile
 
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
 import org.matrix.android.sdk.api.session.identity.ThreePid
@@ -26,6 +25,7 @@ import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
 import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -45,7 +45,7 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         private val pendingThreePidMapper: PendingThreePidMapper,
         @UserId private val userId: String,
-        private val eventBus: EventBus) : FinalizeAddingThreePidTask() {
+        private val globalErrorReceiver: GlobalErrorReceiver) : FinalizeAddingThreePidTask() {
 
     override suspend fun execute(params: Params) {
         if (params.userWantsToCancel.not()) {
@@ -58,7 +58,7 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
                     ?: throw IllegalArgumentException("unknown threepid")
 
             try {
-                executeRequest<Unit>(eventBus) {
+                executeRequest<Unit>(globalErrorReceiver) {
                     val body = FinalizeAddThreePidBody(
                             clientSecret = pendingThreePids.clientSecret,
                             sid = pendingThreePids.sid,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
index 5f1f621ddb..ed60c4a368 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
@@ -18,9 +18,9 @@
 package org.matrix.android.sdk.internal.session.profile
 
 import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class GetProfileInfoTask : Task<GetProfileInfoTask.Params, JsonDict> {
@@ -30,10 +30,10 @@ internal abstract class GetProfileInfoTask : Task<GetProfileInfoTask.Params, Jso
 }
 
 internal class DefaultGetProfileInfoTask @Inject constructor(private val profileAPI: ProfileAPI,
-                                                             private val eventBus: EventBus) : GetProfileInfoTask() {
+                                                             private val globalErrorReceiver: GlobalErrorReceiver) : GetProfileInfoTask() {
 
     override suspend fun execute(params: Params): JsonDict {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = profileAPI.getProfile(params.userId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt
index 5dd092cceb..552ad874ee 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt
@@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.session.profile
 import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.internal.database.model.UserThreePidEntity
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.internal.util.awaitTransaction
 import timber.log.Timber
 import javax.inject.Inject
@@ -30,10 +30,10 @@ internal abstract class RefreshUserThreePidsTask : Task<Unit, Unit>
 
 internal class DefaultRefreshUserThreePidsTask @Inject constructor(private val profileAPI: ProfileAPI,
                                                                    @SessionDatabase private val monarchy: Monarchy,
-                                                                   private val eventBus: EventBus) : RefreshUserThreePidsTask() {
+                                                                   private val globalErrorReceiver: GlobalErrorReceiver) : RefreshUserThreePidsTask() {
 
     override suspend fun execute(params: Unit) {
-        val accountThreePidsResponse = executeRequest<AccountThreePidsResponse>(eventBus) {
+        val accountThreePidsResponse = executeRequest<AccountThreePidsResponse>(globalErrorReceiver) {
             apiCall = profileAPI.getThreePIDs()
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetAvatarUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetAvatarUrlTask.kt
index 4b863c2098..b29153d665 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetAvatarUrlTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetAvatarUrlTask.kt
@@ -16,9 +16,9 @@
 
 package org.matrix.android.sdk.internal.session.profile
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class SetAvatarUrlTask : Task<SetAvatarUrlTask.Params, Unit> {
@@ -30,10 +30,10 @@ internal abstract class SetAvatarUrlTask : Task<SetAvatarUrlTask.Params, Unit> {
 
 internal class DefaultSetAvatarUrlTask @Inject constructor(
         private val profileAPI: ProfileAPI,
-        private val eventBus: EventBus) : SetAvatarUrlTask() {
+        private val globalErrorReceiver: GlobalErrorReceiver) : SetAvatarUrlTask() {
 
     override suspend fun execute(params: Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             val body = SetAvatarUrlBody(
                     avatarUrl = params.newAvatarUrl
             )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetDisplayNameTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetDisplayNameTask.kt
index 1fa84f98c1..3f236bc589 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetDisplayNameTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/SetDisplayNameTask.kt
@@ -16,9 +16,9 @@
 
 package org.matrix.android.sdk.internal.session.profile
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class SetDisplayNameTask : Task<SetDisplayNameTask.Params, Unit> {
@@ -30,10 +30,10 @@ internal abstract class SetDisplayNameTask : Task<SetDisplayNameTask.Params, Uni
 
 internal class DefaultSetDisplayNameTask @Inject constructor(
         private val profileAPI: ProfileAPI,
-        private val eventBus: EventBus) : SetDisplayNameTask() {
+        private val globalErrorReceiver: GlobalErrorReceiver) : SetDisplayNameTask() {
 
     override suspend fun execute(params: Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             val body = SetDisplayNameBody(
                     displayName = params.newDisplayName
             )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/UnbindThreePidsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/UnbindThreePidsTask.kt
index 96b0717edb..3439f6f840 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/UnbindThreePidsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/UnbindThreePidsTask.kt
@@ -19,11 +19,11 @@ package org.matrix.android.sdk.internal.session.profile
 import org.matrix.android.sdk.api.session.identity.IdentityServiceError
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.api.session.identity.toMedium
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
 import org.matrix.android.sdk.internal.session.identity.data.getIdentityServerUrlWithoutProtocol
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal abstract class UnbindThreePidsTask : Task<UnbindThreePidsTask.Params, Boolean> {
@@ -34,12 +34,12 @@ internal abstract class UnbindThreePidsTask : Task<UnbindThreePidsTask.Params, B
 
 internal class DefaultUnbindThreePidsTask @Inject constructor(private val profileAPI: ProfileAPI,
                                                               private val identityStore: IdentityStore,
-                                                              private val eventBus: EventBus) : UnbindThreePidsTask() {
+                                                              private val globalErrorReceiver: GlobalErrorReceiver) : UnbindThreePidsTask() {
     override suspend fun execute(params: Params): Boolean {
         val identityServerUrlWithoutProtocol = identityStore.getIdentityServerUrlWithoutProtocol()
                 ?: throw IdentityServiceError.NoIdentityServerConfigured
 
-        return executeRequest<UnbindThreePidResponse>(eventBus) {
+        return executeRequest<UnbindThreePidResponse>(globalErrorReceiver) {
             apiCall = profileAPI.unbindThreePid(
                     UnbindThreePidBody(
                             identityServerUrlWithoutProtocol,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ValidateSmsCodeTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ValidateSmsCodeTask.kt
index 36804e06fe..efb6c6e836 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ValidateSmsCodeTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ValidateSmsCodeTask.kt
@@ -17,13 +17,13 @@
 package org.matrix.android.sdk.internal.session.profile
 
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.internal.auth.registration.SuccessResult
 import org.matrix.android.sdk.internal.auth.registration.ValidationCodeBody
 import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import javax.inject.Inject
@@ -40,7 +40,7 @@ internal class DefaultValidateSmsCodeTask @Inject constructor(
         @SessionDatabase
         private val monarchy: Monarchy,
         private val pendingThreePidMapper: PendingThreePidMapper,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : ValidateSmsCodeTask {
 
     override suspend fun execute(params: ValidateSmsCodeTask.Params) {
@@ -58,7 +58,7 @@ internal class DefaultValidateSmsCodeTask @Inject constructor(
                 sid = pendingThreePids.sid,
                 code = params.code
         )
-        val result = executeRequest<SuccessResult>(eventBus) {
+        val result = executeRequest<SuccessResult>(globalErrorReceiver) {
             apiCall = profileAPI.validateMsisdn(url, body)
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt
index 31c5cda5ec..d0f7cbfca3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt
@@ -19,13 +19,13 @@ import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.pushers.PusherState
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.PusherEntity
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.SessionComponent
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -45,7 +45,7 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
 
     @Inject lateinit var pushersAPI: PushersAPI
     @Inject @SessionDatabase lateinit var monarchy: Monarchy
-    @Inject lateinit var eventBus: EventBus
+    @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
 
     override fun injectWith(injector: SessionComponent) {
         injector.inject(this)
@@ -81,7 +81,7 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
     }
 
     private suspend fun setPusher(pusher: JsonPusher) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = pushersAPI.setPusher(pusher)
         }
         monarchy.awaitTransaction { realm ->
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPushRuleTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPushRuleTask.kt
index 28ac5db52e..03748b1528 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPushRuleTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPushRuleTask.kt
@@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.pushers
 
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface AddPushRuleTask : Task<AddPushRuleTask.Params, Unit> {
@@ -31,11 +31,11 @@ internal interface AddPushRuleTask : Task<AddPushRuleTask.Params, Unit> {
 
 internal class DefaultAddPushRuleTask @Inject constructor(
         private val pushRulesApi: PushRulesApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : AddPushRuleTask {
 
     override suspend fun execute(params: AddPushRuleTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = pushRulesApi.addRule(params.kind.value, params.pushRule.ruleId, params.pushRule)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushRulesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushRulesTask.kt
index 0c532cedbc..9fb2d51664 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushRulesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushRulesTask.kt
@@ -16,9 +16,9 @@
 package org.matrix.android.sdk.internal.session.pushers
 
 import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetPushRulesTask : Task<GetPushRulesTask.Params, Unit> {
@@ -31,11 +31,11 @@ internal interface GetPushRulesTask : Task<GetPushRulesTask.Params, Unit> {
 internal class DefaultGetPushRulesTask @Inject constructor(
         private val pushRulesApi: PushRulesApi,
         private val savePushRulesTask: SavePushRulesTask,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetPushRulesTask {
 
     override suspend fun execute(params: GetPushRulesTask.Params) {
-        val response = executeRequest<GetPushRulesResponse>(eventBus) {
+        val response = executeRequest<GetPushRulesResponse>(globalErrorReceiver) {
             apiCall = pushRulesApi.getAllRules()
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
index 39e970f4a8..4c7d370446 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
@@ -20,10 +20,10 @@ import org.matrix.android.sdk.api.session.pushers.PusherState
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.PusherEntity
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetPushersTask : Task<Unit, Unit>
@@ -31,11 +31,11 @@ internal interface GetPushersTask : Task<Unit, Unit>
 internal class DefaultGetPushersTask @Inject constructor(
         private val pushersAPI: PushersAPI,
         @SessionDatabase private val monarchy: Monarchy,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetPushersTask {
 
     override suspend fun execute(params: Unit) {
-        val response = executeRequest<GetPushersResponse>(eventBus) {
+        val response = executeRequest<GetPushersResponse>(globalErrorReceiver) {
             apiCall = pushersAPI.getPushers()
         }
         monarchy.awaitTransaction { realm ->
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePushRuleTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePushRuleTask.kt
index 2fc97cf023..ff3122f566 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePushRuleTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePushRuleTask.kt
@@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.pushers
 
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface RemovePushRuleTask : Task<RemovePushRuleTask.Params, Unit> {
@@ -31,11 +31,11 @@ internal interface RemovePushRuleTask : Task<RemovePushRuleTask.Params, Unit> {
 
 internal class DefaultRemovePushRuleTask @Inject constructor(
         private val pushRulesApi: PushRulesApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : RemovePushRuleTask {
 
     override suspend fun execute(params: RemovePushRuleTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = pushRulesApi.deleteRule(params.kind.value, params.pushRule.ruleId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt
index 1f10863799..e3f4fdb789 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt
@@ -26,7 +26,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
 import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import javax.inject.Inject
 
 internal interface RemovePusherTask : Task<RemovePusherTask.Params, Unit> {
@@ -37,7 +37,7 @@ internal interface RemovePusherTask : Task<RemovePusherTask.Params, Unit> {
 internal class DefaultRemovePusherTask @Inject constructor(
         private val pushersAPI: PushersAPI,
         @SessionDatabase private val monarchy: Monarchy,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : RemovePusherTask {
 
     override suspend fun execute(params: RemovePusherTask.Params) {
@@ -62,7 +62,7 @@ internal class DefaultRemovePusherTask @Inject constructor(
                 data = JsonPusherData(existing.data.url, existing.data.format),
                 append = false
         )
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = pushersAPI.setPusher(deleteBody)
         }
         monarchy.awaitTransaction {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleActionsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleActionsTask.kt
index c2dca8a9a5..a5c220e662 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleActionsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleActionsTask.kt
@@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.pushers
 
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UpdatePushRuleActionsTask : Task<UpdatePushRuleActionsTask.Params, Unit> {
@@ -32,13 +32,13 @@ internal interface UpdatePushRuleActionsTask : Task<UpdatePushRuleActionsTask.Pa
 
 internal class DefaultUpdatePushRuleActionsTask @Inject constructor(
         private val pushRulesApi: PushRulesApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UpdatePushRuleActionsTask {
 
     override suspend fun execute(params: UpdatePushRuleActionsTask.Params) {
         if (params.oldPushRule.enabled != params.newPushRule.enabled) {
             // First change enabled state
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 apiCall = pushRulesApi.updateEnableRuleStatus(params.kind.value, params.newPushRule.ruleId, params.newPushRule.enabled)
             }
         }
@@ -47,7 +47,7 @@ internal class DefaultUpdatePushRuleActionsTask @Inject constructor(
             // Also ensure the actions are up to date
             val body = mapOf("actions" to params.newPushRule.actions)
 
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 apiCall = pushRulesApi.updateRuleActions(params.kind.value, params.newPushRule.ruleId, body)
             }
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt
index 4100071c90..f36b5c55fb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt
@@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.pushers
 
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import org.matrix.android.sdk.api.pushrules.rest.PushRule
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UpdatePushRuleEnableStatusTask : Task<UpdatePushRuleEnableStatusTask.Params, Unit> {
@@ -30,11 +30,11 @@ internal interface UpdatePushRuleEnableStatusTask : Task<UpdatePushRuleEnableSta
 
 internal class DefaultUpdatePushRuleEnableStatusTask @Inject constructor(
         private val pushRulesApi: PushRulesApi,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UpdatePushRuleEnableStatusTask {
 
     override suspend fun execute(params: UpdatePushRuleEnableStatusTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = pushRulesApi.updateEnableRuleStatus(params.kind.value, params.pushRule.ruleId, params.enabled)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt
index 9793750fa0..9e4ec6f777 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt
@@ -16,8 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.room.alias
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker.Companion.toFullLocalAlias
@@ -39,13 +39,13 @@ internal class DefaultAddRoomAliasTask @Inject constructor(
         @UserId private val userId: String,
         private val directoryAPI: DirectoryAPI,
         private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : AddRoomAliasTask {
 
     override suspend fun execute(params: AddRoomAliasTask.Params) {
         aliasAvailabilityChecker.check(params.aliasLocalPart)
 
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = directoryAPI.addRoomAlias(
                     roomAlias = params.aliasLocalPart.toFullLocalAlias(userId),
                     body = AddRoomAliasBody(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt
index 3400fd994c..6ad3db90a9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt
@@ -16,7 +16,7 @@
 
 package org.matrix.android.sdk.internal.session.room.alias
 
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import org.matrix.android.sdk.internal.task.Task
@@ -30,11 +30,11 @@ internal interface DeleteRoomAliasTask : Task<DeleteRoomAliasTask.Params, Unit>
 
 internal class DefaultDeleteRoomAliasTask @Inject constructor(
         private val directoryAPI: DirectoryAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteRoomAliasTask {
 
     override suspend fun execute(params: DeleteRoomAliasTask.Params) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = directoryAPI.deleteRoomAlias(
                     roomAlias = params.roomAlias
             )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
index 543d605707..a53ffc4fcd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
@@ -18,12 +18,12 @@ package org.matrix.android.sdk.internal.session.room.alias
 
 import com.zhuinden.monarchy.Monarchy
 import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.query.findByAlias
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import org.matrix.android.sdk.internal.task.Task
@@ -39,7 +39,7 @@ internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Opti
 internal class DefaultGetRoomIdByAliasTask @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         private val directoryAPI: DirectoryAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRoomIdByAliasTask {
 
     override suspend fun execute(params: GetRoomIdByAliasTask.Params): Optional<RoomAliasDescription> {
@@ -52,7 +52,7 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor(
             Optional.from(null)
         } else {
             val description  = tryOrNull("## Failed to get roomId from alias") {
-                executeRequest<RoomAliasDescription>(eventBus) {
+                executeRequest<RoomAliasDescription>(globalErrorReceiver) {
                     apiCall = directoryAPI.getRoomIdByAlias(params.roomAlias)
                 }
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt
index 7cfce4ecdc..202cb1f6de 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt
@@ -16,7 +16,7 @@
 
 package org.matrix.android.sdk.internal.session.room.alias
 
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
@@ -30,12 +30,12 @@ internal interface GetRoomLocalAliasesTask : Task<GetRoomLocalAliasesTask.Params
 
 internal class DefaultGetRoomLocalAliasesTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRoomLocalAliasesTask {
 
     override suspend fun execute(params: GetRoomLocalAliasesTask.Params): List<String> {
         // We do not check for "org.matrix.msc2432", so the API may be missing
-        val response = executeRequest<GetAliasesResponse>(eventBus) {
+        val response = executeRequest<GetAliasesResponse>(globalErrorReceiver) {
             apiCall = roomAPI.getAliases(roomId = params.roomId)
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
index 25ba493891..51a849a35e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
@@ -16,10 +16,10 @@
 
 package org.matrix.android.sdk.internal.session.room.alias
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import javax.inject.Inject
@@ -27,7 +27,7 @@ import javax.inject.Inject
 internal class RoomAliasAvailabilityChecker @Inject constructor(
         @UserId private val userId: String,
         private val directoryAPI: DirectoryAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) {
     /**
      * @param aliasLocalPart the local part of the alias.
@@ -41,7 +41,7 @@ internal class RoomAliasAvailabilityChecker @Inject constructor(
         // Check alias availability
         val fullAlias = aliasLocalPart.toFullLocalAlias(userId)
         try {
-            executeRequest<RoomAliasDescription>(eventBus) {
+            executeRequest<RoomAliasDescription>(globalErrorReceiver) {
                 apiCall = directoryAPI.getRoomIdByAlias(fullAlias)
             }
         } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt
index ef792ab98e..9c16bd1b0f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt
@@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.session.room.create
 import com.zhuinden.monarchy.Monarchy
 import io.realm.RealmConfiguration
 import kotlinx.coroutines.TimeoutCancellationException
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.MatrixError
 import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
@@ -32,6 +31,7 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker
@@ -55,7 +55,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
         @SessionDatabase
         private val realmConfiguration: RealmConfiguration,
         private val createRoomBodyBuilder: CreateRoomBodyBuilder,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : CreateRoomTask {
 
     override suspend fun execute(params: CreateRoomParams): String {
@@ -75,7 +75,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
         val createRoomBody = createRoomBodyBuilder.build(params)
 
         val createRoomResponse = try {
-            executeRequest<CreateRoomResponse>(eventBus) {
+            executeRequest<CreateRoomResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.createRoom(createRoomBody)
             }
         } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetPublicRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetPublicRoomTask.kt
index f2bd0c5f69..edd8ae9b0d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetPublicRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetPublicRoomTask.kt
@@ -18,10 +18,10 @@ package org.matrix.android.sdk.internal.session.room.directory
 
 import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
 import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetPublicRoomTask : Task<GetPublicRoomTask.Params, PublicRoomsResponse> {
@@ -33,11 +33,11 @@ internal interface GetPublicRoomTask : Task<GetPublicRoomTask.Params, PublicRoom
 
 internal class DefaultGetPublicRoomTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetPublicRoomTask {
 
     override suspend fun execute(params: GetPublicRoomTask.Params): PublicRoomsResponse {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomAPI.publicRooms(params.server, params.publicRoomsParams)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt
index fbdd6a03eb..8d71001ef9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt
@@ -16,8 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.room.directory
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
@@ -32,11 +32,11 @@ internal interface GetRoomDirectoryVisibilityTask : Task<GetRoomDirectoryVisibil
 
 internal class DefaultGetRoomDirectoryVisibilityTask @Inject constructor(
         private val directoryAPI: DirectoryAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetRoomDirectoryVisibilityTask {
 
     override suspend fun execute(params: GetRoomDirectoryVisibilityTask.Params): RoomDirectoryVisibility {
-        return executeRequest<RoomDirectoryVisibilityJson>(eventBus) {
+        return executeRequest<RoomDirectoryVisibilityJson>(globalErrorReceiver) {
             apiCall = directoryAPI.getRoomDirectoryVisibility(params.roomId)
         }
                 .visibility
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt
index 5e08284706..3477aa671e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetThirdPartyProtocolsTask.kt
@@ -17,21 +17,21 @@
 package org.matrix.android.sdk.internal.session.room.directory
 
 import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetThirdPartyProtocolsTask : Task<Unit, Map<String, ThirdPartyProtocol>>
 
 internal class DefaultGetThirdPartyProtocolsTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetThirdPartyProtocolsTask {
 
     override suspend fun execute(params: Unit): Map<String, ThirdPartyProtocol> {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomAPI.thirdPartyProtocols()
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt
index 33b12aa1ca..cbb0b6d5d1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt
@@ -16,8 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.room.directory
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
 import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
@@ -33,11 +33,11 @@ internal interface SetRoomDirectoryVisibilityTask : Task<SetRoomDirectoryVisibil
 
 internal class DefaultSetRoomDirectoryVisibilityTask @Inject constructor(
         private val directoryAPI: DirectoryAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SetRoomDirectoryVisibilityTask {
 
     override suspend fun execute(params: SetRoomDirectoryVisibilityTask.Params) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = directoryAPI.setRoomDirectoryVisibility(
                     params.roomId,
                     RoomDirectoryVisibilityJson(visibility = params.roomDirectoryVisibility)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
index 53fa73c624..2be90bf8e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt
@@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy
 import io.realm.Realm
 import io.realm.kotlin.createObject
 import kotlinx.coroutines.TimeoutCancellationException
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
@@ -34,6 +33,7 @@ import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
@@ -57,7 +57,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
         private val syncTokenStore: SyncTokenStore,
         private val roomSummaryUpdater: RoomSummaryUpdater,
         private val roomMemberEventHandler: RoomMemberEventHandler,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : LoadRoomMembersTask {
 
     override suspend fun execute(params: LoadRoomMembersTask.Params) {
@@ -86,7 +86,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
 
         val lastToken = syncTokenStore.getLastToken()
         val response = try {
-            executeRequest<RoomMembersResponse>(eventBus) {
+            executeRequest<RoomMembersResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value)
             }
         } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/InviteTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/InviteTask.kt
index 854a332679..05503bd643 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/InviteTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/InviteTask.kt
@@ -16,10 +16,10 @@
 
 package org.matrix.android.sdk.internal.session.room.membership.joining
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface InviteTask : Task<InviteTask.Params, Unit> {
@@ -32,11 +32,11 @@ internal interface InviteTask : Task<InviteTask.Params, Unit> {
 
 internal class DefaultInviteTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : InviteTask {
 
     override suspend fun execute(params: InviteTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             val body = InviteBody(params.userId, params.reason)
             apiCall = roomAPI.invite(params.roomId, body)
             isRetryable = true
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt
index dd1dc5fa8a..140c6841f5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt
@@ -30,7 +30,7 @@ import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
 import org.matrix.android.sdk.internal.task.Task
 import io.realm.RealmConfiguration
 import kotlinx.coroutines.TimeoutCancellationException
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
@@ -48,13 +48,13 @@ internal class DefaultJoinRoomTask @Inject constructor(
         @SessionDatabase
         private val realmConfiguration: RealmConfiguration,
         private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : JoinRoomTask {
 
     override suspend fun execute(params: JoinRoomTask.Params) {
         roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining)
         val joinRoomResponse = try {
-            executeRequest<JoinRoomResponse>(eventBus) {
+            executeRequest<JoinRoomResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.join(params.roomIdOrAlias, params.viaServers, mapOf("reason" to params.reason))
             }
         } catch (failure: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/leaving/LeaveRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/leaving/LeaveRoomTask.kt
index 58e34a15ec..37bb7570d1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/leaving/LeaveRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/leaving/LeaveRoomTask.kt
@@ -21,13 +21,13 @@ import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
 import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
 import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -40,7 +40,7 @@ internal interface LeaveRoomTask : Task<LeaveRoomTask.Params, Unit> {
 
 internal class DefaultLeaveRoomTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         private val stateEventDataSource: StateEventDataSource,
         private val roomSummaryDataSource: RoomSummaryDataSource,
         private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource
@@ -68,7 +68,7 @@ internal class DefaultLeaveRoomTask @Inject constructor(
             leaveRoom(predecessorRoomId, reason)
         }
         try {
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 apiCall = roomAPI.leave(roomId, mapOf("reason" to reason))
             }
         } catch (failure: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/InviteThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/InviteThreePidTask.kt
index 80af00fc78..d237ec795e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/InviteThreePidTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/threepid/InviteThreePidTask.kt
@@ -16,11 +16,11 @@
 
 package org.matrix.android.sdk.internal.session.room.membership.threepid
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.identity.IdentityServiceError
 import org.matrix.android.sdk.api.session.identity.ThreePid
 import org.matrix.android.sdk.api.session.identity.toMedium
 import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
 import org.matrix.android.sdk.internal.session.identity.EnsureIdentityTokenTask
@@ -39,7 +39,7 @@ internal interface InviteThreePidTask : Task<InviteThreePidTask.Params, Unit> {
 
 internal class DefaultInviteThreePidTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         private val identityStore: IdentityStore,
         private val ensureIdentityTokenTask: EnsureIdentityTokenTask,
         @AuthenticatedIdentity
@@ -52,7 +52,7 @@ internal class DefaultInviteThreePidTask @Inject constructor(
         val identityServerUrlWithoutProtocol = identityStore.getIdentityServerUrlWithoutProtocol() ?: throw IdentityServiceError.NoIdentityServerConfigured
         val identityServerAccessToken = accessTokenProvider.getToken() ?: throw IdentityServiceError.NoIdentityServerConfigured
 
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             val body = ThreePidInviteBody(
                     idServer = identityServerUrlWithoutProtocol,
                     idAccessToken = identityServerAccessToken,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt
index 03ea2408f0..dbec6b555c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt
@@ -16,8 +16,8 @@
 
 package org.matrix.android.sdk.internal.session.room.peeking
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
@@ -31,11 +31,11 @@ internal interface ResolveRoomStateTask : Task<ResolveRoomStateTask.Params, List
 
 internal class DefaultResolveRoomStateTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : ResolveRoomStateTask {
 
     override suspend fun execute(params: ResolveRoomStateTask.Params): List<Event> {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomAPI.getRoomState(params.roomId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt
index a98bb02c83..c7f962a699 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt
@@ -33,7 +33,7 @@ import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
 import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import timber.log.Timber
 import javax.inject.Inject
 import kotlin.collections.set
@@ -58,7 +58,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
         private val roomFullyReadHandler: RoomFullyReadHandler,
         private val readReceiptHandler: ReadReceiptHandler,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SetReadMarkersTask {
 
     override suspend fun execute(params: SetReadMarkersTask.Params) {
@@ -96,7 +96,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
             updateDatabase(params.roomId, markers, shouldUpdateRoomSummary)
         }
         if (markers.isNotEmpty()) {
-            executeRequest<Unit>(eventBus) {
+            executeRequest<Unit>(globalErrorReceiver) {
                 isRetryable = true
                 apiCall = roomAPI.sendReadMarker(params.roomId, markers)
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
index 51eecb8c2a..99d02b50da 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
@@ -18,10 +18,10 @@ package org.matrix.android.sdk.internal.session.room.relation
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface FetchEditHistoryTask : Task<FetchEditHistoryTask.Params, List<Event>> {
@@ -35,11 +35,11 @@ internal interface FetchEditHistoryTask : Task<FetchEditHistoryTask.Params, List
 
 internal class DefaultFetchEditHistoryTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : FetchEditHistoryTask {
 
     override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
-        val response = executeRequest<RelationsResponse>(eventBus) {
+        val response = executeRequest<RelationsResponse>(globalErrorReceiver) {
             apiCall = roomAPI.getRelations(params.roomId,
                     params.eventId,
                     RelationType.REPLACE,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt
index 25dfe32cbb..c12597bea0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt
@@ -18,12 +18,12 @@ package org.matrix.android.sdk.internal.session.room.relation
 import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
 import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.SessionComponent
 import org.matrix.android.sdk.internal.session.room.RoomAPI
@@ -47,7 +47,7 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters)
     ) : SessionWorkerParams
 
     @Inject lateinit var roomAPI: RoomAPI
-    @Inject lateinit var eventBus: EventBus
+    @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
     @Inject lateinit var localEchoRepository: LocalEchoRepository
 
     override fun injectWith(injector: SessionComponent) {
@@ -84,7 +84,7 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters)
     }
 
     private suspend fun sendRelation(roomId: String, relationType: String, relatedEventId: String, localEvent: Event) {
-        executeRequest<SendResponse>(eventBus) {
+        executeRequest<SendResponse>(globalErrorReceiver) {
             apiCall = roomAPI.sendRelation(
                     roomId = roomId,
                     parentId = relatedEventId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/ReportContentTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/ReportContentTask.kt
index bd11937676..9c6e9907a4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/ReportContentTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/ReportContentTask.kt
@@ -16,10 +16,10 @@
 
 package org.matrix.android.sdk.internal.session.room.reporting
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface ReportContentTask : Task<ReportContentTask.Params, Unit> {
@@ -33,11 +33,11 @@ internal interface ReportContentTask : Task<ReportContentTask.Params, Unit> {
 
 internal class DefaultReportContentTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : ReportContentTask {
 
     override suspend fun execute(params: ReportContentTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomAPI.reportContent(params.roomId, params.eventId, ReportContentBody(params.score, params.reason))
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
index f4871ab35d..f742271fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.room.send
 
 import com.zhuinden.monarchy.Monarchy
 import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
@@ -42,7 +41,7 @@ import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
-import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline
+import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
 import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.util.awaitTransaction
 import timber.log.Timber
@@ -52,7 +51,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
                                                        private val taskExecutor: TaskExecutor,
                                                        private val realmSessionProvider: RealmSessionProvider,
                                                        private val roomSummaryUpdater: RoomSummaryUpdater,
-                                                       private val eventBus: EventBus,
+                                                       private val timelineInput: TimelineInput,
                                                        private val timelineEventMapper: TimelineEventMapper) {
 
     fun createLocalEcho(event: Event) {
@@ -76,7 +75,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
             }
         }
         val timelineEvent = timelineEventMapper.map(timelineEventEntity)
-        eventBus.post(DefaultTimeline.OnLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent))
+        timelineInput.onLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent)
         taskExecutor.executorScope.asyncTransaction(monarchy) { realm ->
             val eventInsertEntity = EventInsertEntity(event.eventId, event.type).apply {
                 this.insertType = EventInsertType.LOCAL_ECHO
@@ -90,7 +89,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
 
     fun updateSendState(eventId: String, roomId: String?, sendState: SendState) {
         Timber.v("## SendEvent: [${System.currentTimeMillis()}] Update local state of $eventId to ${sendState.name}")
-        eventBus.post(DefaultTimeline.OnLocalEchoUpdated(roomId ?: "", eventId, sendState))
+        timelineInput.onLocalEchoUpdated(roomId = roomId ?: "", eventId = eventId, sendState = sendState)
         updateEchoAsync(eventId) { realm, sendingEventEntity ->
             if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
                 // If already synced, do not put as sent
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
index 682865eaee..c901c7e18e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
@@ -18,8 +18,8 @@ package org.matrix.android.sdk.internal.session.room.send
 import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.SessionComponent
 import org.matrix.android.sdk.internal.session.room.RoomAPI
@@ -46,7 +46,7 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters)
     ) : SessionWorkerParams
 
     @Inject lateinit var roomAPI: RoomAPI
-    @Inject lateinit var eventBus: EventBus
+    @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
 
     override fun injectWith(injector: SessionComponent) {
         injector.inject(this)
@@ -55,7 +55,7 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters)
     override suspend fun doSafeWork(params: Params): Result {
         val eventId = params.eventId
         return runCatching {
-            executeRequest<SendResponse>(eventBus) {
+            executeRequest<SendResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.redactEvent(
                         params.txID,
                         params.roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
index 37a429d242..c1fc2fd9fe 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
@@ -20,7 +20,6 @@ import android.content.Context
 import androidx.work.WorkerParameters
 import com.squareup.moshi.JsonClass
 import io.realm.RealmConfiguration
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.failure.shouldBeRetried
 import org.matrix.android.sdk.api.session.crypto.CryptoService
 import org.matrix.android.sdk.api.session.room.send.SendState
@@ -54,7 +53,6 @@ internal class SendEventWorker(context: Context,
     @Inject lateinit var localEchoRepository: LocalEchoRepository
     @Inject lateinit var sendEventTask: SendEventTask
     @Inject lateinit var cryptoService: CryptoService
-    @Inject lateinit var eventBus: EventBus
     @Inject lateinit var cancelSendTracker: CancelSendTracker
     @SessionDatabase @Inject lateinit var realmConfiguration: RealmConfiguration
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt
index 642f68c15b..63691d9207 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SendStateTask.kt
@@ -17,10 +17,10 @@
 package org.matrix.android.sdk.internal.session.room.state
 
 import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface SendStateTask : Task<SendStateTask.Params, Unit> {
@@ -34,11 +34,11 @@ internal interface SendStateTask : Task<SendStateTask.Params, Unit> {
 
 internal class DefaultSendStateTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SendStateTask {
 
     override suspend fun execute(params: SendStateTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = if (params.stateKey == null) {
                 roomAPI.sendStateEvent(
                         roomId = params.roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/AddTagToRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/AddTagToRoomTask.kt
index 013fc86d5c..c3b5c3f78f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/AddTagToRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/AddTagToRoomTask.kt
@@ -17,10 +17,10 @@
 package org.matrix.android.sdk.internal.session.room.tags
 
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface AddTagToRoomTask : Task<AddTagToRoomTask.Params, Unit> {
@@ -35,11 +35,11 @@ internal interface AddTagToRoomTask : Task<AddTagToRoomTask.Params, Unit> {
 internal class DefaultAddTagToRoomTask @Inject constructor(
         private val roomAPI: RoomAPI,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : AddTagToRoomTask {
 
     override suspend fun execute(params: AddTagToRoomTask.Params) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = roomAPI.putTag(
                     userId = userId,
                     roomId = params.roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DeleteTagFromRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DeleteTagFromRoomTask.kt
index b22355d431..d578d21fde 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DeleteTagFromRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DeleteTagFromRoomTask.kt
@@ -17,10 +17,10 @@
 package org.matrix.android.sdk.internal.session.room.tags
 
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface DeleteTagFromRoomTask : Task<DeleteTagFromRoomTask.Params, Unit> {
@@ -34,11 +34,11 @@ internal interface DeleteTagFromRoomTask : Task<DeleteTagFromRoomTask.Params, Un
 internal class DefaultDeleteTagFromRoomTask @Inject constructor(
         private val roomAPI: RoomAPI,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : DeleteTagFromRoomTask {
 
     override suspend fun execute(params: DeleteTagFromRoomTask.Params) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = roomAPI.deleteTag(
                     userId = userId,
                     roomId = params.roomId,
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 dd58529412..ae90282d52 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
@@ -23,9 +23,6 @@ import io.realm.RealmConfiguration
 import io.realm.RealmQuery
 import io.realm.RealmResults
 import io.realm.Sort
-import org.greenrobot.eventbus.EventBus
-import org.greenrobot.eventbus.Subscribe
-import org.greenrobot.eventbus.ThreadMode
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.NoOpMatrixCallback
 import org.matrix.android.sdk.api.extensions.orFalse
@@ -81,15 +78,13 @@ internal class DefaultTimeline(
         private val timelineEventMapper: TimelineEventMapper,
         private val settings: TimelineSettings,
         private val hiddenReadReceipts: TimelineHiddenReadReceipts,
-        private val eventBus: EventBus,
+        private val timelineInput: TimelineInput,
         private val eventDecryptor: TimelineEventDecryptor,
         private val realmSessionProvider: RealmSessionProvider,
         private val loadRoomMembersTask: LoadRoomMembersTask
-) : Timeline, TimelineHiddenReadReceipts.Delegate {
-
-    data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
-    data class OnLocalEchoCreated(val roomId: String, val timelineEvent: TimelineEvent)
-    data class OnLocalEchoUpdated(val roomId: String, val eventId: String, val sendState: SendState)
+) : Timeline,
+        TimelineHiddenReadReceipts.Delegate,
+        TimelineInput.Listener {
 
     companion object {
         val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
@@ -161,7 +156,7 @@ internal class DefaultTimeline(
     override fun start() {
         if (isStarted.compareAndSet(false, true)) {
             Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
-            eventBus.register(this)
+            timelineInput.listeners.add(this)
             BACKGROUND_HANDLER.post {
                 eventDecryptor.start()
                 val realm = Realm.getInstance(realmConfiguration)
@@ -206,7 +201,7 @@ internal class DefaultTimeline(
     override fun dispose() {
         if (isStarted.compareAndSet(true, false)) {
             isReady.set(false)
-            eventBus.unregister(this)
+            timelineInput.listeners.remove(this)
             Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
             cancelableBag.cancel()
             BACKGROUND_HANDLER.removeCallbacksAndMessages(null)
@@ -323,25 +318,22 @@ internal class DefaultTimeline(
         postSnapshot()
     }
 
-    @Subscribe(threadMode = ThreadMode.MAIN)
-    fun onNewTimelineEvents(onNewTimelineEvents: OnNewTimelineEvents) {
-        if (isLive && onNewTimelineEvents.roomId == roomId) {
+    override fun onNewTimelineEvents(roomId: String, eventIds: List<String>) {
+        if (isLive && this.roomId == roomId) {
             listeners.forEach {
-                it.onNewTimelineEvents(onNewTimelineEvents.eventIds)
+                it.onNewTimelineEvents(eventIds)
             }
         }
     }
 
-    @Subscribe(threadMode = ThreadMode.MAIN)
-    fun onLocalEchoCreated(onLocalEchoCreated: OnLocalEchoCreated) {
-        if (uiEchoManager.onLocalEchoCreated(onLocalEchoCreated)) {
+    override fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent) {
+        if (uiEchoManager.onLocalEchoCreated(roomId, timelineEvent)) {
             postSnapshot()
         }
     }
 
-    @Subscribe(threadMode = ThreadMode.MAIN)
-    fun onLocalEchoUpdated(onLocalEchoUpdated: OnLocalEchoUpdated) {
-        if (uiEchoManager.onLocalEchoUpdated(onLocalEchoUpdated)) {
+    override fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState) {
+        if (uiEchoManager.onLocalEchoUpdated(roomId, eventId, sendState)) {
             postSnapshot()
         }
     }
@@ -858,11 +850,11 @@ internal class DefaultTimeline(
             }
         }
 
-        fun onLocalEchoUpdated(onLocalEchoUpdated: OnLocalEchoUpdated): Boolean {
-            if (isLive && onLocalEchoUpdated.roomId == roomId) {
-                val existingState = inMemorySendingStates[onLocalEchoUpdated.eventId]
-                inMemorySendingStates[onLocalEchoUpdated.eventId] = onLocalEchoUpdated.sendState
-                if (existingState != onLocalEchoUpdated.sendState) {
+        fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState): Boolean {
+            if (isLive && roomId == this@DefaultTimeline.roomId) {
+                val existingState = inMemorySendingStates[eventId]
+                inMemorySendingStates[eventId] = sendState
+                if (existingState != sendState) {
                     return true
                 }
             }
@@ -870,22 +862,22 @@ internal class DefaultTimeline(
         }
 
         // return true if should update
-        fun onLocalEchoCreated(onLocalEchoCreated: OnLocalEchoCreated): Boolean {
+        fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent): Boolean {
             var postSnapshot = false
-            if (isLive && onLocalEchoCreated.roomId == roomId) {
+            if (isLive && roomId == this@DefaultTimeline.roomId) {
                 // Manage some ui echos (do it before filter because actual event could be filtered out)
-                when (onLocalEchoCreated.timelineEvent.root.getClearType()) {
+                when (timelineEvent.root.getClearType()) {
                     EventType.REDACTION -> {
                     }
                     EventType.REACTION  -> {
-                        val content = onLocalEchoCreated.timelineEvent.root.content?.toModel<ReactionContent>()
+                        val content = timelineEvent.root.content?.toModel<ReactionContent>()
                         if (RelationType.ANNOTATION == content?.relatesTo?.type) {
                             val reaction = content.relatesTo.key
                             val relatedEventID = content.relatesTo.eventId
                             inMemoryReactions.getOrPut(relatedEventID) { mutableListOf() }
                                     .add(
                                             ReactionUiEchoData(
-                                                    localEchoId = onLocalEchoCreated.timelineEvent.eventId,
+                                                    localEchoId = timelineEvent.eventId,
                                                     reactedOnEventId = relatedEventID,
                                                     reaction = reaction
                                             )
@@ -898,12 +890,12 @@ internal class DefaultTimeline(
                 }
 
                 // do not add events that would have been filtered
-                if (listOf(onLocalEchoCreated.timelineEvent).filterEventsWithSettings().isNotEmpty()) {
+                if (listOf(timelineEvent).filterEventsWithSettings().isNotEmpty()) {
                     listeners.forEach {
-                        it.onNewTimelineEvents(listOf(onLocalEchoCreated.timelineEvent.eventId))
+                        it.onNewTimelineEvents(listOf(timelineEvent.eventId))
                     }
-                    Timber.v("On local echo created: ${onLocalEchoCreated.timelineEvent.eventId}")
-                    inMemorySendingEvents.add(0, onLocalEchoCreated.timelineEvent)
+                    Timber.v("On local echo created: ${timelineEvent.eventId}")
+                    inMemorySendingEvents.add(0, timelineEvent)
                     postSnapshot = true
                 }
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index d02e906d00..fce09cc97c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -23,7 +23,6 @@ import com.squareup.inject.assisted.AssistedInject
 import com.zhuinden.monarchy.Monarchy
 import io.realm.Sort
 import io.realm.kotlin.where
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.isImageMessage
 import org.matrix.android.sdk.api.session.events.model.isVideoMessage
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
@@ -45,7 +44,7 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
 internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
                                                                   @SessionDatabase private val monarchy: Monarchy,
                                                                   private val realmSessionProvider: RealmSessionProvider,
-                                                                  private val eventBus: EventBus,
+                                                                  private val timelineInput: TimelineInput,
                                                                   private val taskExecutor: TaskExecutor,
                                                                   private val contextOfEventTask: GetContextOfEventTask,
                                                                   private val eventDecryptor: TimelineEventDecryptor,
@@ -72,7 +71,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                 timelineEventMapper = timelineEventMapper,
                 settings = settings,
                 hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
-                eventBus = eventBus,
+                timelineInput = timelineInput,
                 eventDecryptor = eventDecryptor,
                 fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
                 realmSessionProvider = realmSessionProvider,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/FetchTokenAndPaginateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/FetchTokenAndPaginateTask.kt
index d1bfa1adcb..76c4b3812c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/FetchTokenAndPaginateTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/FetchTokenAndPaginateTask.kt
@@ -20,12 +20,12 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.query.findIncludingEvent
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.filter.FilterRepository
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface FetchTokenAndPaginateTask : Task<FetchTokenAndPaginateTask.Params, TokenChunkEventPersistor.Result> {
@@ -43,12 +43,12 @@ internal class DefaultFetchTokenAndPaginateTask @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         private val filterRepository: FilterRepository,
         private val paginationTask: PaginationTask,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : FetchTokenAndPaginateTask {
 
     override suspend fun execute(params: FetchTokenAndPaginateTask.Params): TokenChunkEventPersistor.Result {
         val filter = filterRepository.getRoomFilter()
-        val response = executeRequest<EventContextResponse>(eventBus) {
+        val response = executeRequest<EventContextResponse>(globalErrorReceiver) {
             apiCall = roomAPI.getContextOfEvent(params.roomId, params.lastKnownEventId, 0, filter)
         }
         val fromToken = if (params.direction == PaginationDirection.FORWARDS) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt
index 7a611dd350..d02a7bafe9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt
@@ -16,11 +16,11 @@
 
 package org.matrix.android.sdk.internal.session.room.timeline
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.filter.FilterRepository
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetContextOfEventTask : Task<GetContextOfEventTask.Params, TokenChunkEventPersistor.Result> {
@@ -35,12 +35,12 @@ internal class DefaultGetContextOfEventTask @Inject constructor(
         private val roomAPI: RoomAPI,
         private val filterRepository: FilterRepository,
         private val tokenChunkEventPersistor: TokenChunkEventPersistor,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : GetContextOfEventTask {
 
     override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
         val filter = filterRepository.getRoomFilter()
-        val response = executeRequest<EventContextResponse>(eventBus) {
+        val response = executeRequest<EventContextResponse>(globalErrorReceiver) {
             // We are limiting the response to the event with eventId to be sure we don't have any issue with potential merging process.
             apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter)
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt
index acac3929ae..b8585b1e74 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt
@@ -17,17 +17,17 @@
 package org.matrix.android.sdk.internal.session.room.timeline
 
 import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 // TODO Add parent task
 
 internal class GetEventTask @Inject constructor(
         private val roomAPI: RoomAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : Task<GetEventTask.Params, Event> {
 
     internal data class Params(
@@ -36,7 +36,7 @@ internal class GetEventTask @Inject constructor(
     )
 
     override suspend fun execute(params: Params): Event {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = roomAPI.getEvent(params.roomId, params.eventId)
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt
index b663d03bd7..1f99893e17 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt
@@ -16,11 +16,11 @@
 
 package org.matrix.android.sdk.internal.session.room.timeline
 
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.filter.FilterRepository
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface PaginationTask : Task<PaginationTask.Params, TokenChunkEventPersistor.Result> {
@@ -37,12 +37,12 @@ internal class DefaultPaginationTask @Inject constructor(
         private val roomAPI: RoomAPI,
         private val filterRepository: FilterRepository,
         private val tokenChunkEventPersistor: TokenChunkEventPersistor,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : PaginationTask {
 
     override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
         val filter = filterRepository.getRoomFilter()
-        val chunk = executeRequest<PaginationResponse>(eventBus) {
+        val chunk = executeRequest<PaginationResponse>(globalErrorReceiver) {
             isRetryable = true
             apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter)
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt
new file mode 100644
index 0000000000..002ab1dd8a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineInput.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.session.room.timeline
+
+import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.internal.session.SessionScope
+import javax.inject.Inject
+
+@SessionScope
+internal class TimelineInput @Inject constructor() {
+    fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent) {
+        listeners.toSet().forEach { it.onLocalEchoCreated(roomId, timelineEvent) }
+    }
+
+    fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState) {
+        listeners.toSet().forEach { it.onLocalEchoUpdated(roomId, eventId, sendState) }
+    }
+
+    fun onNewTimelineEvents(roomId: String, eventIds: List<String>) {
+        listeners.toSet().forEach { it.onNewTimelineEvents(roomId, eventIds) }
+    }
+
+    val listeners = mutableSetOf<Listener>()
+
+    internal interface Listener {
+        fun onLocalEchoCreated(roomId: String, timelineEvent: TimelineEvent)
+        fun onLocalEchoUpdated(roomId: String, eventId: String, sendState: SendState)
+        fun onNewTimelineEvents(roomId: String, eventIds: List<String>)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt
index c8cbb08e2c..3b56d04872 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt
@@ -21,7 +21,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
 import kotlinx.coroutines.delay
-import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import javax.inject.Inject
 
 internal interface SendTypingTask : Task<SendTypingTask.Params, Unit> {
@@ -38,13 +38,13 @@ internal interface SendTypingTask : Task<SendTypingTask.Params, Unit> {
 internal class DefaultSendTypingTask @Inject constructor(
         private val roomAPI: RoomAPI,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SendTypingTask {
 
     override suspend fun execute(params: SendTypingTask.Params) {
         delay(params.delay ?: -1)
 
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = roomAPI.sendTypingState(
                     params.roomId,
                     userId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
index d0439ce7f9..0c0e6a8ed0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt
@@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.database.model.EventEntityFields
 import org.matrix.android.sdk.internal.database.query.TimelineEventFilter
 import org.matrix.android.sdk.internal.database.query.whereType
 import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.filter.FilterFactory
 import org.matrix.android.sdk.internal.session.room.RoomAPI
@@ -39,7 +40,6 @@ import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
 import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
 import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface GetUploadsTask : Task<GetUploadsTask.Params, GetUploadsResult> {
@@ -56,7 +56,7 @@ internal class DefaultGetUploadsTask @Inject constructor(
         private val roomAPI: RoomAPI,
         private val tokenStore: SyncTokenStore,
         @SessionDatabase private val monarchy: Monarchy,
-        private val eventBus: EventBus)
+        private val globalErrorReceiver: GlobalErrorReceiver)
     : GetUploadsTask {
 
     override suspend fun execute(params: GetUploadsTask.Params): GetUploadsResult {
@@ -86,7 +86,7 @@ internal class DefaultGetUploadsTask @Inject constructor(
             val since = params.since ?: tokenStore.getLastToken() ?: throw IllegalStateException("No token available")
 
             val filter = FilterFactory.createUploadsFilter(params.numberOfEvents).toJSONString()
-            val chunk = executeRequest<PaginationResponse>(eventBus) {
+            val chunk = executeRequest<PaginationResponse>(globalErrorReceiver) {
                 apiCall = roomAPI.getRoomMessagesFrom(params.roomId, since, PaginationDirection.BACKWARDS.value, params.numberOfEvents, filter)
             }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt
index 4f574e5ead..402602e4d5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/search/SearchTask.kt
@@ -16,10 +16,10 @@
 
 package org.matrix.android.sdk.internal.session.search
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.search.EventAndSender
 import org.matrix.android.sdk.api.session.search.SearchResult
 import org.matrix.android.sdk.api.util.MatrixItem
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody
 import org.matrix.android.sdk.internal.session.search.request.SearchRequestCategories
@@ -47,11 +47,11 @@ internal interface SearchTask : Task<SearchTask.Params, SearchResult> {
 
 internal class DefaultSearchTask @Inject constructor(
         private val searchAPI: SearchAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SearchTask {
 
     override suspend fun execute(params: SearchTask.Params): SearchResult {
-        return executeRequest<SearchResponse>(eventBus) {
+        return executeRequest<SearchResponse>(globalErrorReceiver) {
             val searchRequestBody = SearchRequestBody(
                     searchCategories = SearchRequestCategories(
                             roomEvents = SearchRequestRoomEvents(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt
index 3bed0bdbff..2c3cd5d270 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt
@@ -20,9 +20,9 @@ import org.matrix.android.sdk.api.auth.data.Credentials
 import org.matrix.android.sdk.api.auth.data.SessionParams
 import org.matrix.android.sdk.internal.auth.SessionParamsStore
 import org.matrix.android.sdk.internal.auth.data.PasswordLoginParams
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface SignInAgainTask : Task<SignInAgainTask.Params, Unit> {
@@ -35,11 +35,11 @@ internal class DefaultSignInAgainTask @Inject constructor(
         private val signOutAPI: SignOutAPI,
         private val sessionParams: SessionParams,
         private val sessionParamsStore: SessionParamsStore,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SignInAgainTask {
 
     override suspend fun execute(params: SignInAgainTask.Params) {
-        val newCredentials = executeRequest<Credentials>(eventBus) {
+        val newCredentials = executeRequest<Credentials>(globalErrorReceiver) {
             apiCall = signOutAPI.loginAgain(
                     PasswordLoginParams.userIdentifier(
                             // Reuse the same userId
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt
index 153ea5a6fd..0cb8704782 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt
@@ -18,11 +18,11 @@ package org.matrix.android.sdk.internal.session.signout
 
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.MatrixError
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.cleanup.CleanupSession
 import org.matrix.android.sdk.internal.session.identity.IdentityDisconnectTask
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import java.net.HttpURLConnection
 import javax.inject.Inject
@@ -35,7 +35,7 @@ internal interface SignOutTask : Task<SignOutTask.Params, Unit> {
 
 internal class DefaultSignOutTask @Inject constructor(
         private val signOutAPI: SignOutAPI,
-        private val eventBus: EventBus,
+        private val globalErrorReceiver: GlobalErrorReceiver,
         private val identityDisconnectTask: IdentityDisconnectTask,
         private val cleanupSession: CleanupSession
 ) : SignOutTask {
@@ -45,7 +45,7 @@ internal class DefaultSignOutTask @Inject constructor(
         if (params.signOutFromHomeserver) {
             Timber.d("SignOut: send request...")
             try {
-                executeRequest<Unit>(eventBus) {
+                executeRequest<Unit>(globalErrorReceiver) {
                     apiCall = signOutAPI.signOut()
                 }
             } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
index b1b2f65dc2..456b0f9c26 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.sync
 
 import io.realm.Realm
 import io.realm.kotlin.createObject
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.R
 import org.matrix.android.sdk.api.session.crypto.MXCryptoError
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -55,8 +54,8 @@ import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembers
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler
 import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
-import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline
 import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
+import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
 import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent
 import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync
 import org.matrix.android.sdk.internal.session.sync.model.RoomSync
@@ -75,7 +74,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
                                                    private val roomTypingUsersHandler: RoomTypingUsersHandler,
                                                    private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
                                                    @UserId private val userId: String,
-                                                   private val eventBus: EventBus) {
+                                                   private val timelineInput: TimelineInput) {
 
     sealed class HandlingStrategy {
         data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
@@ -348,7 +347,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
             }
         }
         // posting new events to timeline if any is registered
-        eventBus.post(DefaultTimeline.OnNewTimelineEvents(roomId = roomId, eventIds = eventIds))
+        timelineInput.onNewTimelineEvents(roomId = roomId, eventIds = eventIds)
         return chunkEntity
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
index b4fd6e7386..7c38230065 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
@@ -16,9 +16,9 @@
 
 package org.matrix.android.sdk.internal.session.sync
 
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.R
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.TimeOutInterceptor
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService
@@ -48,7 +48,7 @@ internal class DefaultSyncTask @Inject constructor(
         private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask,
         private val userStore: UserStore,
         private val syncTaskSequencer: SyncTaskSequencer,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SyncTask {
 
     override suspend fun execute(params: SyncTask.Params) = syncTaskSequencer.post {
@@ -81,7 +81,7 @@ internal class DefaultSyncTask @Inject constructor(
 
         val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
 
-        val syncResponse = executeRequest<SyncResponse>(eventBus) {
+        val syncResponse = executeRequest<SyncResponse>(globalErrorReceiver) {
             apiCall = syncAPI.sync(
                     params = requestParams,
                     readTimeOut = readTimeOut
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt
index d1393c8b37..26e8d3380a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt
@@ -17,11 +17,11 @@
 package org.matrix.android.sdk.internal.session.user.accountdata
 
 import com.zhuinden.monarchy.Monarchy
-import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
 import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.sync.model.accountdata.IgnoredUsersContent
 import org.matrix.android.sdk.internal.task.Task
@@ -40,7 +40,7 @@ internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UpdateIgnoredUserIdsTask {
 
     override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
@@ -63,7 +63,7 @@ internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(
         val list = ignoredUserIds.toList()
         val body = IgnoredUsersContent.createWithUserIds(list)
 
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = accountDataApi.setAccountData(userId, UserAccountDataTypes.TYPE_IGNORED_USER_LIST, body)
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
index 80ab79b228..dba28253a7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
@@ -24,8 +24,8 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTe
 import org.matrix.android.sdk.internal.session.sync.model.accountdata.BreadcrumbsContent
 import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent
 import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Params, Unit> {
@@ -100,11 +100,11 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
 internal class DefaultUpdateUserAccountDataTask @Inject constructor(
         private val accountDataApi: AccountDataAPI,
         @UserId private val userId: String,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : UpdateUserAccountDataTask {
 
     override suspend fun execute(params: UpdateUserAccountDataTask.Params) {
-        return executeRequest(eventBus) {
+        return executeRequest(globalErrorReceiver) {
             apiCall = accountDataApi.setAccountData(userId, params.type, params.getData())
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/model/SearchUserTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/model/SearchUserTask.kt
index cd9be0e7dd..380fa6e209 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/model/SearchUserTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/model/SearchUserTask.kt
@@ -17,10 +17,10 @@
 package org.matrix.android.sdk.internal.session.user.model
 
 import org.matrix.android.sdk.api.session.user.model.User
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.user.SearchUserAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface SearchUserTask : Task<SearchUserTask.Params, List<User>> {
@@ -34,11 +34,11 @@ internal interface SearchUserTask : Task<SearchUserTask.Params, List<User>> {
 
 internal class DefaultSearchUserTask @Inject constructor(
         private val searchUserAPI: SearchUserAPI,
-        private val eventBus: EventBus
+        private val globalErrorReceiver: GlobalErrorReceiver
 ) : SearchUserTask {
 
     override suspend fun execute(params: SearchUserTask.Params): List<User> {
-        val response = executeRequest<SearchUsersResponse>(eventBus) {
+        val response = executeRequest<SearchUsersResponse>(globalErrorReceiver) {
             apiCall = searchUserAPI.searchUsers(SearchUsersParams(params.search, params.limit))
         }
         return response.users.map {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/CreateWidgetTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/CreateWidgetTask.kt
index 422615af2d..ae807ce30f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/CreateWidgetTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/CreateWidgetTask.kt
@@ -25,10 +25,10 @@ import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFie
 import org.matrix.android.sdk.internal.database.query.whereStateKey
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.task.Task
-import org.greenrobot.eventbus.EventBus
 import javax.inject.Inject
 
 internal interface CreateWidgetTask : Task<CreateWidgetTask.Params, Unit> {
@@ -43,10 +43,10 @@ internal interface CreateWidgetTask : Task<CreateWidgetTask.Params, Unit> {
 internal class DefaultCreateWidgetTask @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
                                                            private val roomAPI: RoomAPI,
                                                            @UserId private val userId: String,
-                                                           private val eventBus: EventBus) : CreateWidgetTask {
+                                                           private val globalErrorReceiver: GlobalErrorReceiver) : CreateWidgetTask {
 
     override suspend fun execute(params: CreateWidgetTask.Params) {
-        executeRequest<Unit>(eventBus) {
+        executeRequest<Unit>(globalErrorReceiver) {
             apiCall = roomAPI.sendStateEvent(
                     roomId = params.roomId,
                     stateEventType = EventType.STATE_ROOM_WIDGET_LEGACY,
diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html
index 17d79e3655..4ce4e9a210 100755
--- a/vector/src/main/assets/open_source_licenses.html
+++ b/vector/src/main/assets/open_source_licenses.html
@@ -332,11 +332,6 @@ SOFTWARE.
         <br/>
         Copyright 2018 The diff-match-patch Authors. https://github.com/google/diff-match-patch
     </li>
-    <li>
-        <b>EventBus</b>
-        <br/>
-        Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org)
-    </li>
     <li>
         <b>LazyThreeTenBp</b>
         <br/>

From 6e8d93bc6fd699290883de657232e04950c0b2b4 Mon Sep 17 00:00:00 2001
From: Yves Quemener <yves.quemener@gmail.com>
Date: Wed, 6 Jan 2021 11:22:25 +0900
Subject: [PATCH 39/41] Fix in the test homeserver set-up instruction

I think I removed a typo. You probably don't want a new virtualenv every time you start a homeserver?
---
 docs/ui-tests.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/docs/ui-tests.md b/docs/ui-tests.md
index ff01da0b31..6ebb52abe8 100644
--- a/docs/ui-tests.md
+++ b/docs/ui-tests.md
@@ -27,7 +27,6 @@ $ source env/bin/activate
 Every time you want to launch these test homeservers, type:
 
 ```shell script
-$ virtualenv -p python3 env
 $ source env/bin/activate
 (env) $ demo/start.sh --no-rate-limit
 ```

From 2a365d677653906e6269e8f1d7808a0d32785802 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Thu, 7 Jan 2021 18:28:05 +0100
Subject: [PATCH 40/41] Move interface to correct location

---
 .../matrix/android/sdk/internal/network/GlobalErrorReceiver.kt    | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename matrix-sdk-android/src/{debug => main}/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt (100%)

diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt
similarity index 100%
rename from matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/GlobalErrorReceiver.kt

From d91ed2985d2a0b7c5b03ef0834d4c6ad0cf9c4be Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Fri, 8 Jan 2021 12:40:20 +0100
Subject: [PATCH 41/41] Sync: fix initial sync is not retried correctly when
 there is some network error. [#2632]

---
 CHANGES.md                                    |  2 +-
 .../internal/session/sync/job/SyncService.kt  | 24 ++++---
 .../app/core/services/VectorSyncService.kt    | 71 ++++++++-----------
 3 files changed, 44 insertions(+), 53 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index bff1a7480e..8a69aea6fd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,7 +15,7 @@ Bugfix 🐛:
  - Room Topic not displayed correctly after visiting a link (#2551)
  - Hiding membership events works the exact opposite (#2603)
  - Tapping drawer having more than 1 room in notifications gives "malformed link" error (#2605)
-
+ - Initial sync is not retried correctly when there is some network error. (#2632)
 Translations 🗣:
  -
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
index 6d100a71f9..9d854229df 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
@@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
 import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
 import timber.log.Timber
+import java.net.SocketTimeoutException
 import java.util.concurrent.atomic.AtomicBoolean
 
 /**
@@ -68,14 +69,12 @@ abstract class SyncService : Service() {
 
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         Timber.i("## Sync: onStartCommand [$this] $intent with action: ${intent?.action}")
-
-        // We should start we have to ensure we fulfill contract to show notification
-        // for foreground service (as per design for this service)
-        // TODO can we check if it's really in foreground
-        onStart(isInitialSync)
         when (intent?.action) {
             ACTION_STOP -> {
                 Timber.i("## Sync: stop command received")
+                // We should start we have to ensure we fulfill contract to show notification
+                // for foreground service (as per design for this service)
+                onStart(isInitialSync)
                 // If it was periodic we ensure that it will not reschedule itself
                 preventReschedule = true
                 // we don't want to cancel initial syncs, let it finish
@@ -85,11 +84,12 @@ abstract class SyncService : Service() {
             }
             else        -> {
                 val isInit = initialize(intent)
+                onStart(isInitialSync)
                 if (isInit) {
                     periodic = intent?.getBooleanExtra(EXTRA_PERIODIC, false) ?: false
                     val onNetworkBack = intent?.getBooleanExtra(EXTRA_NETWORK_BACK_RESTART, false) ?: false
                     Timber.d("## Sync: command received, periodic: $periodic  networkBack: $onNetworkBack")
-                    if (onNetworkBack && !backgroundDetectionObserver.isInBackground) {
+                    if (!isInitialSync && onNetworkBack && !backgroundDetectionObserver.isInBackground) {
                         // the restart after network occurs while the app is in foreground
                         // so just stop. It will be restarted when entering background
                         preventReschedule = true
@@ -165,10 +165,16 @@ abstract class SyncService : Service() {
                 preventReschedule = true
             }
             if (throwable is Failure.NetworkConnection) {
-                // Network is off, no need to reschedule endless alarms :/
+                // Timeout is not critical, so retry as soon as possible.
+                val retryDelay = if (isInitialSync || throwable.cause is SocketTimeoutException) {
+                    0
+                } else {
+                    syncDelaySeconds
+                }
+                // Network might be off, no need to reschedule endless alarms :/
                 preventReschedule = true
-                // Instead start a work to restart background sync when network is back
-                onNetworkError(sessionId ?: "", isInitialSync, syncTimeoutSeconds, syncDelaySeconds)
+                // Instead start a work to restart background sync when network is on
+                onNetworkError(sessionId ?: "", isInitialSync, syncTimeoutSeconds, retryDelay)
             }
             // JobCancellation could be caught here when onDestroy cancels the coroutine context
             if (isRunning.get()) stopMe()
diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
index bf78d5b7fb..0950bdf121 100644
--- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
@@ -21,7 +21,6 @@ import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
 import android.os.Build
-import androidx.core.content.ContextCompat.getSystemService
 import androidx.core.content.getSystemService
 import androidx.work.Constraints
 import androidx.work.Data
@@ -49,22 +48,19 @@ class VectorSyncService : SyncService() {
             }
         }
 
-        fun newPeriodicIntent(context: Context, sessionId: String, timeoutSeconds: Int, delayInSeconds: Int): Intent {
+        fun newPeriodicIntent(
+                context: Context,
+                sessionId: String,
+                timeoutSeconds: Int,
+                delayInSeconds: Int,
+                networkBack: Boolean = false
+        ): Intent {
             return Intent(context, VectorSyncService::class.java).also {
                 it.putExtra(EXTRA_SESSION_ID, sessionId)
                 it.putExtra(EXTRA_TIMEOUT_SECONDS, timeoutSeconds)
                 it.putExtra(EXTRA_PERIODIC, true)
                 it.putExtra(EXTRA_DELAY_SECONDS, delayInSeconds)
-            }
-        }
-
-        fun newPeriodicNetworkBackIntent(context: Context, sessionId: String, timeoutSeconds: Int, delayInSeconds: Int): Intent {
-            return Intent(context, VectorSyncService::class.java).also {
-                it.putExtra(EXTRA_SESSION_ID, sessionId)
-                it.putExtra(EXTRA_TIMEOUT_SECONDS, timeoutSeconds)
-                it.putExtra(EXTRA_PERIODIC, true)
-                it.putExtra(EXTRA_DELAY_SECONDS, delayInSeconds)
-                it.putExtra(EXTRA_NETWORK_BACK_RESTART, true)
+                it.putExtra(EXTRA_NETWORK_BACK_RESTART, networkBack)
             }
         }
 
@@ -93,12 +89,12 @@ class VectorSyncService : SyncService() {
     }
 
     override fun onRescheduleAsked(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int) {
-        reschedule(sessionId, timeout, delay)
+        rescheduleSyncService(sessionId, timeout, delay)
     }
 
     override fun onNetworkError(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int) {
         Timber.d("## Sync: A network error occured during sync")
-        val uploadWorkRequest: WorkRequest =
+        val rescheduleSyncWorkRequest: WorkRequest =
                 OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
                         .setInputData(Data.Builder()
                                 .putString("sessionId", sessionId)
@@ -115,7 +111,7 @@ class VectorSyncService : SyncService() {
         Timber.d("## Sync: Schedule a work to restart service when network will be on")
         WorkManager
                 .getInstance(applicationContext)
-                .enqueue(uploadWorkRequest)
+                .enqueue(rescheduleSyncWorkRequest)
     }
 
     override fun onDestroy() {
@@ -128,42 +124,31 @@ class VectorSyncService : SyncService() {
         notificationManager.cancel(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE)
     }
 
-    private fun reschedule(sessionId: String, timeout: Int, delay: Int) {
-        val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            PendingIntent.getForegroundService(this, 0, newPeriodicIntent(this, sessionId, timeout, delay), 0)
-        } else {
-            PendingIntent.getService(this, 0, newPeriodicIntent(this, sessionId, timeout, delay), 0)
-        }
-        val firstMillis = System.currentTimeMillis() + delay * 1000L
-        val alarmMgr = getSystemService<AlarmManager>()!!
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
-        } else {
-            alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
-        }
-    }
-
     class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
             Worker(appContext, workerParams) {
         override fun doWork(): Result {
             val sessionId = inputData.getString("sessionId") ?: return Result.failure()
             val timeout = inputData.getInt("timeout", 6)
             val delay = inputData.getInt("delay", 60)
-
-            val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                PendingIntent.getForegroundService(applicationContext, 0, newPeriodicNetworkBackIntent(applicationContext, sessionId, timeout, delay), 0)
-            } else {
-                PendingIntent.getService(applicationContext, 0, newPeriodicNetworkBackIntent(applicationContext, sessionId, timeout, delay), 0)
-            }
-            val firstMillis = System.currentTimeMillis() + delay * 1000L
-            val alarmMgr = getSystemService<AlarmManager>(applicationContext, AlarmManager::class.java)!!
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-                alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
-            } else {
-                alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
-            }
+            applicationContext.rescheduleSyncService(sessionId, timeout, delay, true)
             // Indicate whether the work finished successfully with the Result
             return Result.success()
         }
     }
 }
+
+private fun Context.rescheduleSyncService(sessionId: String, timeout: Int, delay: Int, networkBack: Boolean = false) {
+    val periodicIntent = VectorSyncService.newPeriodicIntent(this, sessionId, timeout, delay, networkBack)
+    val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+        PendingIntent.getForegroundService(this, 0, periodicIntent, 0)
+    } else {
+        PendingIntent.getService(this, 0, periodicIntent, 0)
+    }
+    val firstMillis = System.currentTimeMillis() + delay * 1000L
+    val alarmMgr = getSystemService<AlarmManager>()!!
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
+    } else {
+        alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
+    }
+}