diff --git a/changelog.d/3890.misc b/changelog.d/3890.misc
new file mode 100644
index 0000000000..3bace80fa7
--- /dev/null
+++ b/changelog.d/3890.misc
@@ -0,0 +1 @@
+Migrate to MvRx2 (Mavericks)
\ No newline at end of file
diff --git a/dependencies.gradle b/dependencies.gradle
index 882a4fde09..1e3c492149 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -19,6 +19,7 @@ def moshi = "1.12.0"
def lifecycle = "2.2.0"
def rxBinding = "3.1.0"
def epoxy = "4.6.2"
+def mavericks = "2.4.0"
def glide = "4.12.0"
def bigImageViewer = "1.8.1"
def jjwt = "0.11.2"
@@ -98,7 +99,9 @@ ext.libs = [
'epoxyGlide' : "com.airbnb.android:epoxy-glide-preloading:$epoxy",
'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy",
'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy",
- 'mvrx' : "com.airbnb.android:mvrx:1.5.1"
+ 'mavericks' : "com.airbnb.android:mavericks:$mavericks",
+ 'mavericksRx' : "com.airbnb.android:mavericks-rxjava2:$mavericks",
+ 'mavericksTesting' : "com.airbnb.android:mavericks-testing:$mavericks"
],
mockk : [
'mockk' : "io.mockk:mockk:$mockk",
diff --git a/docs/mavericks_migration.md b/docs/mavericks_migration.md
new file mode 100644
index 0000000000..a36ae8261a
--- /dev/null
+++ b/docs/mavericks_migration.md
@@ -0,0 +1,11 @@
+Useful links:
+- https://airbnb.io/mavericks/#/new-2x
+
+Mavericks 2 is replacing MvRx, by removing usage of Rx by Flow, both internally and in the API.
+See the link ^ to have more intel, but basically, the changes are:
+
+session.rx() => session.flow()
+room.rx() => room.flow()
+subscribe { }.disposeOnClear() => onEach { }.launchIn(viewModelScope)
+
+Only using manually onEach requires to add launchIn,any other methods provided by Mavericks on viewModel and activity/fragment are already taking care of lifecycle.
\ No newline at end of file
diff --git a/matrix-sdk-android-flow/.gitignore b/matrix-sdk-android-flow/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/matrix-sdk-android-flow/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle
new file mode 100644
index 0000000000..fd2e2e0824
--- /dev/null
+++ b/matrix-sdk-android-flow/build.gradle
@@ -0,0 +1,48 @@
+
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ compileSdk versions.compileSdk
+
+ defaultConfig {
+ minSdk versions.minSdk
+ targetSdk versions.targetSdk
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility versions.sourceCompat
+ targetCompatibility versions.targetCompat
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+}
+
+dependencies {
+
+ implementation project(":matrix-sdk-android")
+ implementation libs.androidx.appCompat
+
+ implementation libs.jetbrains.kotlinStdlibJdk7
+ implementation libs.jetbrains.coroutinesCore
+ implementation libs.jetbrains.coroutinesAndroid
+ implementation libs.androidx.lifecycleLivedata
+
+ // Paging
+ implementation libs.androidx.pagingRuntimeKtx
+
+ // Logging
+ implementation libs.jakewharton.timber
+}
diff --git a/matrix-sdk-android-flow/consumer-rules.pro b/matrix-sdk-android-flow/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/matrix-sdk-android-flow/proguard-rules.pro b/matrix-sdk-android-flow/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/matrix-sdk-android-flow/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/matrix-sdk-android-flow/src/main/AndroidManifest.xml b/matrix-sdk-android-flow/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..2392c0bfcb
--- /dev/null
+++ b/matrix-sdk-android-flow/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt
new file mode 100644
index 0000000000..72493325c3
--- /dev/null
+++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 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.flow
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+internal fun Flow.startWith(dispatcher: CoroutineDispatcher, supplier: suspend () -> T): Flow {
+ return onStart {
+ val value = withContext(dispatcher) {
+ supplier()
+ }
+ emit(value)
+ }
+}
diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt
new file mode 100644
index 0000000000..42c1476b79
--- /dev/null
+++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.flow
+
+import androidx.lifecycle.asFlow
+import kotlinx.coroutines.flow.Flow
+import org.matrix.android.sdk.api.query.QueryStringValue
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
+import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
+import org.matrix.android.sdk.api.session.room.model.ReadReceipt
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
+import org.matrix.android.sdk.api.session.room.send.UserDraft
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.util.Optional
+import org.matrix.android.sdk.api.util.toOptional
+
+class FlowRoom(private val room: Room) {
+
+ fun liveRoomSummary(): Flow> {
+ return room.getRoomSummaryLive().asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.roomSummary().toOptional()
+ }
+ }
+
+ fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> {
+ return room.getRoomMembersLive(queryParams).asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getRoomMembers(queryParams)
+ }
+ }
+
+ fun liveAnnotationSummary(eventId: String): Flow> {
+ return room.getEventAnnotationsSummaryLive(eventId).asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getEventAnnotationsSummary(eventId).toOptional()
+ }
+ }
+
+ fun liveTimelineEvent(eventId: String): Flow> {
+ return room.getTimeLineEventLive(eventId).asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getTimeLineEvent(eventId).toOptional()
+ }
+ }
+
+ fun liveStateEvent(eventType: String, stateKey: QueryStringValue): Flow> {
+ return room.getStateEventLive(eventType, stateKey).asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getStateEvent(eventType, stateKey).toOptional()
+ }
+ }
+
+ fun liveStateEvents(eventTypes: Set): Flow> {
+ return room.getStateEventsLive(eventTypes).asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getStateEvents(eventTypes)
+ }
+ }
+
+ fun liveReadMarker(): Flow> {
+ return room.getReadMarkerLive().asFlow()
+ }
+
+ fun liveReadReceipt(): Flow> {
+ return room.getMyReadReceiptLive().asFlow()
+ }
+
+ fun liveEventReadReceipts(eventId: String): Flow> {
+ return room.getEventReadReceiptsLive(eventId).asFlow()
+ }
+
+ fun liveDraft(): Flow> {
+ return room.getDraftLive().asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.getDraft().toOptional()
+ }
+ }
+
+ fun liveNotificationState(): Flow {
+ return room.getLiveRoomNotificationState().asFlow()
+ }
+}
+
+fun Room.flow(): FlowRoom {
+ return FlowRoom(this)
+}
diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt
new file mode 100644
index 0000000000..13fd097bcd
--- /dev/null
+++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt
@@ -0,0 +1,180 @@
+/*
+ * 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.flow
+
+import androidx.lifecycle.asFlow
+import androidx.paging.PagedList
+import kotlinx.coroutines.flow.Flow
+import org.matrix.android.sdk.api.query.QueryStringValue
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
+import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
+import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams
+import org.matrix.android.sdk.api.session.group.model.GroupSummary
+import org.matrix.android.sdk.api.session.identity.ThreePid
+import org.matrix.android.sdk.api.session.pushers.Pusher
+import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
+import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent
+import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
+import org.matrix.android.sdk.api.session.sync.SyncState
+import org.matrix.android.sdk.api.session.user.model.User
+import org.matrix.android.sdk.api.session.widgets.model.Widget
+import org.matrix.android.sdk.api.util.Optional
+import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
+import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
+
+class FlowSession(private val session: Session) {
+
+ fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Flow> {
+ return session.getRoomSummariesLive(queryParams).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.getRoomSummaries(queryParams)
+ }
+ }
+
+ fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Flow> {
+ return session.getGroupSummariesLive(queryParams).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.getGroupSummaries(queryParams)
+ }
+ }
+
+ fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Flow> {
+ return session.spaceService().getSpaceSummariesLive(queryParams).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.spaceService().getSpaceSummaries(queryParams)
+ }
+ }
+
+ fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Flow> {
+ return session.getBreadcrumbsLive(queryParams).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.getBreadcrumbs(queryParams)
+ }
+ }
+
+ fun liveMyDevicesInfo(): Flow> {
+ return session.cryptoService().getLiveMyDevicesInfo().asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.cryptoService().getMyDevicesInfo()
+ }
+ }
+
+ fun liveSyncState(): Flow {
+ return session.getSyncStateLive().asFlow()
+ }
+
+ fun livePushers(): Flow> {
+ return session.getPushersLive().asFlow()
+ }
+
+ fun liveUser(userId: String): Flow> {
+ return session.getUserLive(userId).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.getUser(userId).toOptional()
+ }
+ }
+
+ fun liveRoomMember(userId: String, roomId: String): Flow> {
+ return session.getRoomMemberLive(userId, roomId).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.getRoomMember(userId, roomId).toOptional()
+ }
+ }
+
+ fun liveUsers(): Flow> {
+ return session.getUsersLive().asFlow()
+ }
+
+ fun liveIgnoredUsers(): Flow> {
+ return session.getIgnoredUsersLive().asFlow()
+ }
+
+ fun livePagedUsers(filter: String? = null, excludedUserIds: Set? = null): Flow> {
+ return session.getPagedUsersLive(filter, excludedUserIds).asFlow()
+ }
+
+ fun liveThreePIds(refreshData: Boolean): Flow> {
+ return session.getThreePidsLive(refreshData).asFlow()
+ .startWith(session.coroutineDispatchers.io) { session.getThreePids() }
+ }
+
+ fun livePendingThreePIds(): Flow> {
+ return session.getPendingThreePidsLive().asFlow()
+ .startWith(session.coroutineDispatchers.io) { session.getPendingThreePids() }
+ }
+
+ fun liveUserCryptoDevices(userId: String): Flow> {
+ return session.cryptoService().getLiveCryptoDeviceInfo(userId).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.cryptoService().getCryptoDeviceInfo(userId)
+ }
+ }
+
+ fun liveCrossSigningInfo(userId: String): Flow> {
+ return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId).toOptional()
+ }
+ }
+
+ fun liveCrossSigningPrivateKeys(): Flow> {
+ return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional()
+ }
+ }
+
+ fun liveUserAccountData(types: Set): Flow> {
+ return session.accountDataService().getLiveUserAccountDataEvents(types).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.accountDataService().getUserAccountDataEvents(types)
+ }
+ }
+
+ fun liveRoomAccountData(types: Set): Flow> {
+ return session.accountDataService().getLiveRoomAccountDataEvents(types).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.accountDataService().getRoomAccountDataEvents(types)
+ }
+ }
+
+ fun liveRoomWidgets(
+ roomId: String,
+ widgetId: QueryStringValue,
+ widgetTypes: Set? = null,
+ excludedTypes: Set? = null
+ ): Flow> {
+ return session.widgetService().getRoomWidgetsLive(roomId, widgetId, widgetTypes, excludedTypes).asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.widgetService().getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
+ }
+ }
+
+ fun liveRoomChangeMembershipState(): Flow