diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index 26d141c3..183f3be1 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -37,10 +37,7 @@ dependencies { implementation project(":matrix-sdk-android") implementation 'androidx.appcompat:appcompat:1.1.0-alpha01' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' - implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' - - // Paging - implementation 'androidx.paging:paging-runtime:2.0.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index 6c5724be..66ddc550 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -17,6 +17,7 @@ package im.vector.matrix.rx import im.vector.matrix.android.api.session.room.Room +import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import io.reactivex.Observable @@ -30,6 +31,10 @@ class RxRoom(private val room: Room) { return room.getRoomMemberIdsLive().asObservable() } + fun liveAnnotationSummary(eventId: String): Observable> { + return room.getEventSummaryLive(eventId).asObservable() + } + } fun Room.rx(): RxRoom { diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index 4205b049..af149116 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -18,6 +18,7 @@ package im.vector.matrix.rx import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.sync.SyncState import io.reactivex.Observable @@ -36,6 +37,10 @@ class RxSession(private val session: Session) { return session.syncState().asObservable() } + fun livePushers(): Observable> { + return session.livePushers().asObservable() + } + } fun Session.rx(): RxSession { diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 83106060..0e7e381e 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -92,6 +92,7 @@ dependencies { def lifecycle_version = '2.0.0' def coroutines_version = "1.0.1" def markwon_version = '3.0.0' + def daggerVersion = '2.23.1' implementation fileTree(dir: 'libs', include: ['*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -120,7 +121,7 @@ dependencies { kapt 'dk.ilios:realmfieldnameshelper:1.1.1' // Work - implementation "android.arch.work:work-runtime-ktx:1.0.0" + implementation "androidx.work:work-runtime-ktx:2.1.0-beta01" // FP implementation "io.arrow-kt:arrow-core:$arrow_version" @@ -133,8 +134,10 @@ dependencies { implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2' // DI - implementation "org.koin:koin-core:$koin_version" - implementation "org.koin:koin-core-ext:$koin_version" + implementation "com.google.dagger:dagger:$daggerVersion" + kapt "com.google.dagger:dagger-compiler:$daggerVersion" + compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0' + kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' // Logging implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt index 27c1c4da..398507fa 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/auth/AuthenticatorTest.kt @@ -39,16 +39,8 @@ import org.koin.test.KoinTest @RunWith(AndroidJUnit4::class) internal class AuthenticatorTest : InstrumentedTest, KoinTest { - init { - Monarchy.init(context()) - val matrixModule = MatrixModule(context()).definition - val networkModule = NetworkModule().definition - val authModule = AuthModule().definition - loadKoinModules(listOf(matrixModule, networkModule, authModule)) - } - - private val authenticator: Authenticator by inject() - private val okReplayInterceptor: OkReplayInterceptor by inject() + lateinit var authenticator: Authenticator + lateinit var okReplayInterceptor: OkReplayInterceptor private val okReplayConfig = OkReplayConfig.Builder() .tapeRoot(AndroidTapeRoot( diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt index c2d32549..afcb846f 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt @@ -23,7 +23,7 @@ import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import kotlin.random.Random -internal class FakeGetContextOfEventTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask { +internal class FakeGetContextOfEventTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask { override suspend fun execute(params: GetContextOfEventTask.Params): Try { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt index a0bc1f74..e4082b35 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import kotlin.random.Random -internal class FakePaginationTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask { +internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask { override suspend fun execute(params: PaginationTask.Params): Try { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt index 221887c7..5a470ce7 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt @@ -59,8 +59,8 @@ internal class TimelineTest : InstrumentedTest { private fun createTimeline(initialEventId: String? = null): Timeline { val taskExecutor = TaskExecutor(testCoroutineDispatchers) val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy) - val paginationTask = FakePaginationTask(tokenChunkEventPersistor) - val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor) + val paginationTask = FakePaginationTask @Inject constructor(tokenChunkEventPersistor) + val getContextOfEventTask = FakeGetContextOfEventTask @Inject constructor(tokenChunkEventPersistor) val roomMemberExtractor = SenderRoomMemberExtractor(ROOM_ID) val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, EventRelationExtractor()) return DefaultTimeline( diff --git a/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt index edca0156..3d499be3 100644 --- a/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt +++ b/matrix-sdk-android/src/debug/java/im/vector/matrix/android/internal/network/interceptors/CurlLoggingInterceptor.kt @@ -17,12 +17,14 @@ package im.vector.matrix.android.internal.network.interceptors +import im.vector.matrix.android.internal.di.MatrixScope import okhttp3.Interceptor import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor import okio.Buffer import java.io.IOException import java.nio.charset.Charset +import javax.inject.Inject /** * An OkHttp interceptor that logs requests as curl shell commands. They can then @@ -33,7 +35,8 @@ import java.nio.charset.Charset * information. It should only be used in a controlled manner or in a * non-production environment. */ -internal class CurlLoggingInterceptor(private val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT) +@MatrixScope +internal class CurlLoggingInterceptor @Inject constructor(private val logger: HttpLoggingInterceptor.Logger) : Interceptor { /** diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml index f4e0e50f..7191d9c8 100644 --- a/matrix-sdk-android/src/main/AndroidManifest.xml +++ b/matrix-sdk-android/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ @@ -7,9 +8,10 @@ - + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt index d6665f7f..1df81889 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt @@ -18,77 +18,78 @@ package im.vector.matrix.android.api import android.content.Context import androidx.lifecycle.ProcessLifecycleOwner +import androidx.work.Configuration +import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.api.auth.Authenticator -import im.vector.matrix.android.api.pushrules.Action -import im.vector.matrix.android.api.pushrules.PushRuleService -import im.vector.matrix.android.api.session.Session -import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.sync.FilterService -import im.vector.matrix.android.internal.auth.AuthModule -import im.vector.matrix.android.internal.di.MatrixKoinComponent -import im.vector.matrix.android.internal.di.MatrixKoinHolder -import im.vector.matrix.android.internal.di.MatrixModule -import im.vector.matrix.android.internal.di.NetworkModule +import im.vector.matrix.android.internal.SessionManager +import im.vector.matrix.android.internal.di.DaggerMatrixComponent import im.vector.matrix.android.internal.network.UserAgentHolder import im.vector.matrix.android.internal.util.BackgroundDetectionObserver -import org.koin.standalone.get -import org.koin.standalone.inject -import timber.log.Timber +import org.matrix.olm.OlmManager import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject + +data class MatrixConfiguration( + val applicationFlavor: String = "Default-application-flavor" +) { + + interface Provider { + fun providesMatrixConfiguration(): MatrixConfiguration + } + +} /** * This is the main entry point to the matrix sdk. - * This class is automatically init by a provider. * To get the singleton instance, use getInstance static method. */ -class Matrix private constructor(context: Context) : MatrixKoinComponent { +class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) { - private val authenticator by inject() - private val userAgentHolder by inject() - private val backgroundDetectionObserver by inject() - var currentSession: Session? = null + @Inject internal lateinit var authenticator: Authenticator + @Inject internal lateinit var userAgentHolder: UserAgentHolder + @Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver + @Inject internal lateinit var olmManager: OlmManager + @Inject internal lateinit var sessionManager: SessionManager init { Monarchy.init(context) - val matrixModule = MatrixModule(context).definition - val networkModule = NetworkModule().definition - val authModule = AuthModule().definition - MatrixKoinHolder.instance.loadModules(listOf(matrixModule, networkModule, authModule)) - ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) - authenticator.getLastActiveSession()?.also { - currentSession = it - it.open() - it.setFilter(FilterService.FilterPreset.RiotFilter) - it.startSync() + DaggerMatrixComponent.factory().create(context).inject(this) + if (context.applicationContext !is Configuration.Provider) { + WorkManager.initialize(context, Configuration.Builder().build()) } + ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) + userAgentHolder.setApplicationFlavor(matrixConfiguration.applicationFlavor) } + fun getUserAgent() = userAgentHolder.userAgent + fun authenticator(): Authenticator { return authenticator } - /** - * Set application flavor, to alter user agent. - */ - fun setApplicationFlavor(flavor: String) { - userAgentHolder.setApplicationFlavor(flavor) - } - - fun getUserAgent() = userAgentHolder.userAgent - companion object { + private lateinit var instance: Matrix private val isInit = AtomicBoolean(false) - internal fun initialize(context: Context) { + fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) { if (isInit.compareAndSet(false, true)) { - instance = Matrix(context.applicationContext) + instance = Matrix(context.applicationContext, matrixConfiguration) } } - fun getInstance(): Matrix { + fun getInstance(context: Context): Matrix { + if (isInit.compareAndSet(false, true)) { + val appContext = context.applicationContext + if (appContext is MatrixConfiguration.Provider) { + val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration() + instance = Matrix(appContext, matrixConfiguration) + } else { + throw IllegalStateException("Matrix is not initialized properly. You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") + } + } return instance } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt index 633d262e..2ca6d0a2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/Authenticator.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.auth import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.util.Cancelable @@ -40,14 +41,24 @@ interface Authenticator { * Check if there is an active [Session]. * @return true if there is at least one active session. */ - fun hasActiveSessions(): Boolean + fun hasAuthenticatedSessions(): Boolean //TODO remove this method. Shouldn't be managed like that. /** * Get the last active [Session], if there is an active session. * @return the lastActive session if any, or null */ - fun getLastActiveSession(): Session? + fun getLastAuthenticatedSession(): Session? + + /** + * Get an authenticated session. You should at least call authenticate one time before. + * If you logout, this session will no longer be valid. + * + * @param sessionParams the sessionParams to open with. + * @return the associated session if any, or null + */ + fun getSession(sessionParams: SessionParams): Session? + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index ac412678..d947e593 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -54,6 +54,10 @@ interface Session : */ val sessionParams: SessionParams + val myUserId : String + get() = sessionParams.credentials.userId + + /** * This method allow to open a session. It does start some service on the background. */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/MatrixInitProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/MatrixInitProvider.kt deleted file mode 100644 index 9167cbc5..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/MatrixInitProvider.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.matrix.android.internal - -import android.content.ContentProvider -import android.content.ContentValues -import android.database.Cursor -import android.net.Uri -import im.vector.matrix.android.api.Matrix - -internal class MatrixInitProvider : ContentProvider() { - - override fun onCreate(): Boolean { - Matrix.initialize(context!!) - return true - } - - override fun insert(uri: Uri, values: ContentValues?): Uri? { - return null - } - - override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? { - return null - } - - override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int { - return 0 - } - - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { - return 0 - } - - override fun getType(uri: Uri): String? { - return null - } - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/SessionManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/SessionManager.kt new file mode 100644 index 00000000..7e0a67a4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/SessionManager.kt @@ -0,0 +1,67 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal + +import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.internal.auth.SessionParamsStore +import im.vector.matrix.android.internal.di.MatrixComponent +import im.vector.matrix.android.internal.di.MatrixScope +import im.vector.matrix.android.internal.session.DaggerSessionComponent +import im.vector.matrix.android.internal.session.SessionComponent +import javax.inject.Inject + +@MatrixScope +internal class SessionManager @Inject constructor(private val matrixComponent: MatrixComponent, + private val sessionParamsStore: SessionParamsStore) { + + private val sessionComponents = HashMap() + + fun getSessionComponent(userId: String): SessionComponent? { + val sessionParams = sessionParamsStore.get(userId) ?: return null + return getOrCreateSessionComponent(sessionParams) + } + + fun getOrCreateSession(sessionParams: SessionParams): Session { + return getOrCreateSessionComponent(sessionParams).session() + } + + fun releaseSession(userId: String) { + if (sessionComponents.containsKey(userId).not()) { + throw RuntimeException("You don't have a session for the user $userId") + } + sessionComponents.remove(userId)?.also { + it.session().close() + } + } + + private fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent { + val userId = sessionParams.credentials.userId + if (sessionComponents.containsKey(userId)) { + return sessionComponents[userId]!! + } + return DaggerSessionComponent + .factory() + .create(matrixComponent, sessionParams) + .also { + sessionComponents[sessionParams.credentials.userId] = it + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt index 5a49c512..472d653d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthModule.kt @@ -17,38 +17,44 @@ package im.vector.matrix.android.internal.auth import android.content.Context +import dagger.Binds +import dagger.Module +import dagger.Provides import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.internal.auth.db.AuthRealmModule import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore -import im.vector.matrix.android.internal.auth.db.SessionParamsMapper +import im.vector.matrix.android.internal.di.AuthDatabase +import im.vector.matrix.android.internal.di.MatrixScope import io.realm.RealmConfiguration -import org.koin.dsl.module.module import java.io.File -class AuthModule { +@Module +internal abstract class AuthModule { - val definition = module { - - single { - DefaultAuthenticator(get(), get(), get()) as Authenticator - } - - single { - val context: Context = get() + @Module + companion object { + @JvmStatic + @Provides + @AuthDatabase + fun providesRealmConfiguration(context: Context): RealmConfiguration { val old = File(context.filesDir, "matrix-sdk-auth") - if (old.exists()) { old.renameTo(File(context.filesDir, "matrix-sdk-auth.realm")) } - - val mapper = SessionParamsMapper((get())) - val realmConfiguration = RealmConfiguration.Builder() + return RealmConfiguration.Builder() .name("matrix-sdk-auth.realm") .modules(AuthRealmModule()) .deleteRealmIfMigrationNeeded() .build() - RealmSessionParamsStore(mapper, realmConfiguration) as SessionParamsStore } - } + + + + @Binds + abstract fun bindSessionParamsStore(sessionParamsStore: RealmSessionParamsStore): SessionParamsStore + + @Binds + abstract fun bindAuthenticator(authenticator: DefaultAuthenticator): Authenticator + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt index 15520835..81e64f5b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt @@ -19,38 +19,49 @@ package im.vector.matrix.android.internal.auth import android.util.Patterns import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.Authenticator +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.auth.data.PasswordLoginParams -import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.auth.data.ThreePidMedium +import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.extensions.foldToCallback +import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.DefaultSession import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import retrofit2.Retrofit +import okhttp3.OkHttpClient +import javax.inject.Inject -internal class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val sessionParamsStore: SessionParamsStore) : Authenticator { +internal class DefaultAuthenticator @Inject constructor(@Unauthenticated + private val okHttpClient: OkHttpClient, + private val retrofitFactory: RetrofitFactory, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val sessionParamsStore: SessionParamsStore, + private val sessionManager: SessionManager +) : Authenticator { - override fun hasActiveSessions(): Boolean { - return sessionParamsStore.get() != null + override fun hasAuthenticatedSessions(): Boolean { + return sessionParamsStore.getLast() != null } - override fun getLastActiveSession(): Session? { - val sessionParams = sessionParamsStore.get() + override fun getLastAuthenticatedSession(): Session? { + val sessionParams = sessionParamsStore.getLast() return sessionParams?.let { - DefaultSession(it) + sessionManager.getOrCreateSession(it) } } + override fun getSession(sessionParams: SessionParams): Session? { + return sessionManager.getOrCreateSession(sessionParams) + } + override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, @@ -81,13 +92,13 @@ internal class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builde sessionParamsStore.save(sessionParams) sessionParams }.map { - DefaultSession(it) + sessionManager.getOrCreateSession(it) } } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { - val retrofit = retrofitBuilder.baseUrl(homeServerConnectionConfig.homeServerUri.toString()).build() + val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString()) return retrofit.create(AuthAPI::class.java) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt index df8b71e4..e7729d37 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt @@ -21,9 +21,15 @@ import im.vector.matrix.android.api.auth.data.SessionParams internal interface SessionParamsStore { - fun get(): SessionParams? + fun get(userId: String): SessionParams? + + fun getLast(): SessionParams? + + fun getAll(): List fun save(sessionParams: SessionParams): Try - fun delete(): Try + fun delete(userId: String): Try + + fun deleteAll(): Try } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt index 08449586..1bb27d20 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt @@ -19,11 +19,48 @@ package im.vector.matrix.android.internal.auth.db import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.auth.SessionParamsStore +import im.vector.matrix.android.internal.di.AuthDatabase import io.realm.Realm import io.realm.RealmConfiguration +import javax.inject.Inject -internal class RealmSessionParamsStore(private val mapper: SessionParamsMapper, - private val realmConfiguration: RealmConfiguration) : SessionParamsStore { +internal class RealmSessionParamsStore @Inject constructor(private val mapper: SessionParamsMapper, + @AuthDatabase + private val realmConfiguration: RealmConfiguration +) : SessionParamsStore { + + override fun getLast(): SessionParams? { + val realm = Realm.getInstance(realmConfiguration) + val sessionParams = realm + .where(SessionParamsEntity::class.java) + .findAll() + .map { mapper.map(it) } + .lastOrNull() + realm.close() + return sessionParams + } + + override fun get(userId: String): SessionParams? { + val realm = Realm.getInstance(realmConfiguration) + val sessionParams = realm + .where(SessionParamsEntity::class.java) + .equalTo(SessionParamsEntityFields.USER_ID, userId) + .findAll() + .map { mapper.map(it) } + .firstOrNull() + realm.close() + return sessionParams + } + + override fun getAll(): List { + val realm = Realm.getInstance(realmConfiguration) + val sessionParams = realm + .where(SessionParamsEntity::class.java) + .findAll() + .mapNotNull { mapper.map(it) } + realm.close() + return sessionParams + } override fun save(sessionParams: SessionParams): Try { return Try { @@ -39,18 +76,20 @@ internal class RealmSessionParamsStore(private val mapper: SessionParamsMapper, } } - override fun get(): SessionParams? { - val realm = Realm.getInstance(realmConfiguration) - val sessionParams = realm - .where(SessionParamsEntity::class.java) - .findAll() - .map { mapper.map(it) } - .lastOrNull() - realm.close() - return sessionParams + override fun delete(userId: String): Try { + return Try { + val realm = Realm.getInstance(realmConfiguration) + realm.executeTransaction { + it.where(SessionParamsEntity::class.java) + .equalTo(SessionParamsEntityFields.USER_ID, userId) + .findAll() + .deleteAllFromRealm() + } + realm.close() + } } - override fun delete(): Try { + override fun deleteAll(): Try { return Try { val realm = Realm.getInstance(realmConfiguration) realm.executeTransaction { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt index 7ebfbcc4..e6569d28 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt @@ -17,8 +17,10 @@ package im.vector.matrix.android.internal.auth.db import io.realm.RealmObject +import io.realm.annotations.PrimaryKey internal open class SessionParamsEntity( + @PrimaryKey var userId: String = "", var credentialsJson: String = "", var homeServerConnectionConfigJson: String = "" ) : RealmObject() \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt index 7fc0a334..64303ea0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt @@ -17,11 +17,13 @@ package im.vector.matrix.android.internal.auth.db import com.squareup.moshi.Moshi -import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.internal.di.MatrixScope +import javax.inject.Inject -internal class SessionParamsMapper(moshi: Moshi) { +internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { private val credentialsAdapter = moshi.adapter(Credentials::class.java) private val homeServerConnectionConfigAdapter = moshi.adapter(HomeServerConnectionConfig::class.java) @@ -47,7 +49,7 @@ internal class SessionParamsMapper(moshi: Moshi) { if (credentialsJson == null || homeServerConnectionConfigJson == null) { return null } - return SessionParamsEntity(credentialsJson, homeServerConnectionConfigJson) + return SessionParamsEntity(sessionParams.credentials.userId, credentialsJson, homeServerConnectionConfigJson) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index cb5ed968..01506b71 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -59,15 +59,13 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.* -import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask -import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask -import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask -import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.di.CryptoDatabase import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.cache.ClearCacheTask import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.membership.RoomMembers @@ -81,6 +79,7 @@ import org.matrix.olm.OlmManager import timber.log.Timber import java.util.* import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject import kotlin.coroutines.EmptyCoroutineContext /** @@ -93,7 +92,10 @@ import kotlin.coroutines.EmptyCoroutineContext * CryptoService maintains all necessary keys and their sharing with other devices required for the crypto. * Specially, it tracks all room membership changes events in order to do keys updates. */ -internal class CryptoManager( +@SessionScope +internal class CryptoManager @Inject constructor( + // Olm Manager + private val olmManager: OlmManager, // The credentials, private val credentials: Credentials, private val myDeviceInfoHolder: MyDeviceInfoHolder, @@ -119,8 +121,6 @@ internal class CryptoManager( private val incomingRoomKeyRequestManager: IncomingRoomKeyRequestManager, // private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, - // Olm Manager - private val olmManager: OlmManager, // Actions private val setDeviceVerificationAction: SetDeviceVerificationAction, private val megolmSessionDataImporter: MegolmSessionDataImporter, @@ -135,7 +135,7 @@ internal class CryptoManager( private val setDeviceNameTask: SetDeviceNameTask, private val uploadKeysTask: UploadKeysTask, private val loadRoomMembersTask: LoadRoomMembersTask, - private val clearCryptoDataTask: ClearCacheTask, + @CryptoDatabase private val clearCryptoDataTask: ClearCacheTask, private val monarchy: Monarchy, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val taskExecutor: TaskExecutor diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt index 495bd6b1..89baf1c6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt @@ -17,49 +17,41 @@ package im.vector.matrix.android.internal.crypto import android.content.Context +import dagger.Binds +import dagger.Module +import dagger.Provides import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.CryptoService -import im.vector.matrix.android.internal.crypto.actions.* -import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory -import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory -import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory -import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.tasks.* -import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule import im.vector.matrix.android.internal.crypto.store.db.hash import im.vector.matrix.android.internal.crypto.tasks.* -import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService -import im.vector.matrix.android.internal.session.DefaultSession +import im.vector.matrix.android.internal.di.CryptoDatabase +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.cache.ClearCacheTask import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask import io.realm.RealmConfiguration -import org.koin.dsl.module.module -import org.matrix.olm.OlmManager import retrofit2.Retrofit import java.io.File -internal class CryptoModule { +@Module +internal abstract class CryptoModule { - val definition = module(override = true) { - - /* ========================================================================================== - * Crypto Main - * ========================================================================================== */ + @Module + companion object { // Realm configuration, named to avoid clash with main cache realm configuration - scope(DefaultSession.SCOPE, name = "CryptoRealmConfiguration") { - val context: Context = get() - - val credentials: Credentials = get() - - RealmConfiguration.Builder() + @JvmStatic + @Provides + @CryptoDatabase + @SessionScope + fun providesRealmConfiguration(context: Context, credentials: Credentials): RealmConfiguration { + return RealmConfiguration.Builder() .directory(File(context.filesDir, credentials.userId.hash())) .name("crypto_store.realm") .modules(RealmCryptoStoreModule()) @@ -68,270 +60,115 @@ internal class CryptoModule { .build() } - // CryptoStore - scope(DefaultSession.SCOPE) { - RealmCryptoStore(false /* TODO*/, - get("CryptoRealmConfiguration"), - get()) as IMXCryptoStore - } - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(CryptoApi::class.java) - } - - // CryptoService - scope(DefaultSession.SCOPE) { - get() as CryptoService - } - - // - scope(DefaultSession.SCOPE) { - OutgoingRoomKeyRequestManager(get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - IncomingRoomKeyRequestManager(get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - RoomDecryptorProvider(get(), get()) - } - - scope(DefaultSession.SCOPE) { - // Ensure OlmManager is loaded first - get() - MXOlmDevice(get()) - } - - // ObjectSigner - scope(DefaultSession.SCOPE) { - ObjectSigner(get(), get()) - } - - // OneTimeKeysUploader - scope(DefaultSession.SCOPE) { - OneTimeKeysUploader(get(), get(), get(), get()) - } - - // Actions - scope(DefaultSession.SCOPE) { - SetDeviceVerificationAction(get(), get(), get()) - } - - // Device info - scope(DefaultSession.SCOPE) { - MyDeviceInfoHolder(get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - EnsureOlmSessionsForDevicesAction(get(), get()) - } - - scope(DefaultSession.SCOPE) { - EnsureOlmSessionsForUsersAction(get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - MegolmSessionDataImporter(get(), get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - MessageEncrypter(get(), get()) + @JvmStatic + @Provides + @CryptoDatabase + fun providesClearCacheTask(@CryptoDatabase + realmConfiguration: RealmConfiguration): ClearCacheTask { + return RealmClearCacheTask(realmConfiguration) } - scope(DefaultSession.SCOPE) { - WarnOnUnknownDeviceRepository() + @JvmStatic + @Provides + fun providesCryptoStore(@CryptoDatabase + realmConfiguration: RealmConfiguration, credentials: Credentials): IMXCryptoStore { + return RealmCryptoStore(false /* TODO*/, + realmConfiguration, + credentials) } - // Factories - scope(DefaultSession.SCOPE) { - MXMegolmDecryptionFactory( - get(), get(), get(), get(), get(), get(), get(), get(), get() - ) + @JvmStatic + @Provides + @SessionScope + fun providesCryptoAPI(retrofit: Retrofit): CryptoApi { + return retrofit.create(CryptoApi::class.java) } - scope(DefaultSession.SCOPE) { - MXMegolmEncryptionFactory( - get(), get(), get(), get(), get(), get(), get(), get(), get(), get() - ) + @JvmStatic + @Provides + @SessionScope + fun providesRoomKeysAPI(retrofit: Retrofit): RoomKeysApi { + return retrofit.create(RoomKeysApi::class.java) } - scope(DefaultSession.SCOPE) { - MXOlmDecryptionFactory( - get(), get() - ) - } - - scope(DefaultSession.SCOPE) { - MXOlmEncryptionFactory( - get(), get(), get(), get(), get(), get() - ) - } - - // CryptoManager - scope(DefaultSession.SCOPE) { - CryptoManager( - credentials = get(), - myDeviceInfoHolder = get(), - cryptoStore = get(), - olmDevice = get(), - cryptoConfig = get(), - deviceListManager = get(), - keysBackup = get(), - objectSigner = get(), - oneTimeKeysUploader = get(), - roomDecryptorProvider = get(), - sasVerificationService = get(), - incomingRoomKeyRequestManager = get(), - outgoingRoomKeyRequestManager = get(), - olmManager = get(), - setDeviceVerificationAction = get(), - megolmSessionDataImporter = get(), - warnOnUnknownDevicesRepository = get(), - megolmEncryptionFactory = get(), - olmEncryptionFactory = get(), - deleteDeviceTask = get(), - deleteDeviceWithUserPasswordTask = get(), - // Tasks - getDevicesTask = get(), - setDeviceNameTask = get(), - uploadKeysTask = get(), - loadRoomMembersTask = get(), - clearCryptoDataTask = get("ClearTaskCryptoCache"), - monarchy = get(), - coroutineDispatchers = get(), - taskExecutor = get() - ) - } - - // Olm manager - single { - // load the crypto libs. - OlmManager() - } - - - // Crypto config - scope(DefaultSession.SCOPE) { - MXCryptoConfig() - } - - // Device list - scope(DefaultSession.SCOPE) { - DeviceListManager(get(), get(), get(), get(), get()) - } - - // Crypto tasks - scope(DefaultSession.SCOPE) { - DefaultClaimOneTimeKeysForUsersDevice(get()) as ClaimOneTimeKeysForUsersDeviceTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteDeviceTask(get()) as DeleteDeviceTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteDeviceWithUserPasswordTask(get(), get()) as DeleteDeviceWithUserPasswordTask - } - scope(DefaultSession.SCOPE) { - DefaultDownloadKeysForUsers(get()) as DownloadKeysForUsersTask - } - scope(DefaultSession.SCOPE) { - DefaultGetDevicesTask(get()) as GetDevicesTask - } - scope(DefaultSession.SCOPE) { - DefaultGetKeyChangesTask(get()) as GetKeyChangesTask - } - scope(DefaultSession.SCOPE) { - DefaultSendToDeviceTask(get()) as SendToDeviceTask - } - scope(DefaultSession.SCOPE) { - DefaultSetDeviceNameTask(get()) as SetDeviceNameTask - } - scope(DefaultSession.SCOPE) { - DefaultUploadKeysTask(get()) as UploadKeysTask - } - - scope(DefaultSession.SCOPE, name = "ClearTaskCryptoCache") { - RealmClearCacheTask(get("CryptoRealmConfiguration")) as ClearCacheTask - } - - /* ========================================================================================== - * Keys backup - * ========================================================================================== */ - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(RoomKeysApi::class.java) - } - - scope(DefaultSession.SCOPE) { - KeysBackup( - // Credentials - get(), - // CryptoStore - get(), - get(), - get(), - get(), - // Task - get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), - // Task executor - get(), - get()) - } - - // Key backup tasks - scope(DefaultSession.SCOPE) { - DefaultCreateKeysBackupVersionTask(get()) as CreateKeysBackupVersionTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteBackupTask(get()) as DeleteBackupTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteRoomSessionDataTask(get()) as DeleteRoomSessionDataTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteRoomSessionsDataTask(get()) as DeleteRoomSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultDeleteSessionsDataTask(get()) as DeleteSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultGetKeysBackupLastVersionTask(get()) as GetKeysBackupLastVersionTask - } - scope(DefaultSession.SCOPE) { - DefaultGetKeysBackupVersionTask(get()) as GetKeysBackupVersionTask - } - scope(DefaultSession.SCOPE) { - DefaultGetRoomSessionDataTask(get()) as GetRoomSessionDataTask - } - scope(DefaultSession.SCOPE) { - DefaultGetRoomSessionsDataTask(get()) as GetRoomSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultGetSessionsDataTask(get()) as GetSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultStoreRoomSessionDataTask(get()) as StoreRoomSessionDataTask - } - scope(DefaultSession.SCOPE) { - DefaultStoreRoomSessionsDataTask(get()) as StoreRoomSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultStoreSessionsDataTask(get()) as StoreSessionsDataTask - } - scope(DefaultSession.SCOPE) { - DefaultUpdateKeysBackupVersionTask(get()) as UpdateKeysBackupVersionTask - } - - /* ========================================================================================== - * SAS Verification - * ========================================================================================== */ - - scope(DefaultSession.SCOPE) { - DefaultSasVerificationService(get(), get(), get(), get(), get(), get(), get(), get()) + @JvmStatic + @Provides + @SessionScope + fun providesCryptoConfig(): MXCryptoConfig { + return MXCryptoConfig() } } + + @Binds + abstract fun bindCryptoService(cryptoManager: CryptoManager): CryptoService + + @Binds + abstract fun bindDeleteDeviceTask(deleteDeviceTask: DefaultDeleteDeviceTask): DeleteDeviceTask + + @Binds + abstract fun bindGetDevicesTask(getDevicesTask: DefaultGetDevicesTask): GetDevicesTask + + @Binds + abstract fun bindSetDeviceNameTask(getDevicesTask: DefaultSetDeviceNameTask): SetDeviceNameTask + + @Binds + abstract fun bindUploadKeysTask(getDevicesTask: DefaultUploadKeysTask): UploadKeysTask + + @Binds + abstract fun bindDownloadKeysForUsersTask(downloadKeysForUsers: DefaultDownloadKeysForUsers): DownloadKeysForUsersTask + + @Binds + abstract fun bindCreateKeysBackupVersionTask(createKeysBackupVersionTask: DefaultCreateKeysBackupVersionTask): CreateKeysBackupVersionTask + + @Binds + abstract fun bindDeleteBackupTask(deleteBackupTask: DefaultDeleteBackupTask): DeleteBackupTask + + @Binds + abstract fun bindDeleteRoomSessionDataTask(deleteRoomSessionDataTask: DefaultDeleteRoomSessionDataTask): DeleteRoomSessionDataTask + + @Binds + abstract fun bindDeleteRoomSessionsDataTask(deleteRoomSessionDataTask: DefaultDeleteRoomSessionsDataTask): DeleteRoomSessionsDataTask + + @Binds + abstract fun bindDeleteSessionsDataTask(deleteRoomSessionDataTask: DefaultDeleteSessionsDataTask): DeleteSessionsDataTask + + @Binds + abstract fun bindGetKeysBackupLastVersionTask(getKeysBackupLastVersionTask: DefaultGetKeysBackupLastVersionTask): GetKeysBackupLastVersionTask + + @Binds + abstract fun bindGetKeysBackupVersionTask(getKeysBackupVersionTask: DefaultGetKeysBackupVersionTask): GetKeysBackupVersionTask + + @Binds + abstract fun bindGetRoomSessionDataTask(getRoomSessionDataTask: DefaultGetRoomSessionDataTask): GetRoomSessionDataTask + + @Binds + abstract fun bindGetRoomSessionsDataTask(getRoomSessionDataTask: DefaultGetRoomSessionsDataTask): GetRoomSessionsDataTask + + @Binds + abstract fun bindGetSessionsDataTask(getRoomSessionDataTask: DefaultGetSessionsDataTask): GetSessionsDataTask + + @Binds + abstract fun bindStoreRoomSessionDataTask(storeRoomSessionDataTask: DefaultStoreRoomSessionDataTask): StoreRoomSessionDataTask + + @Binds + abstract fun bindStoreRoomSessionsDataTask(storeRoomSessionDataTask: DefaultStoreRoomSessionsDataTask): StoreRoomSessionsDataTask + + @Binds + abstract fun bindStoreSessionsDataTask(storeRoomSessionDataTask: DefaultStoreSessionsDataTask): StoreSessionsDataTask + + @Binds + abstract fun bindUpdateKeysBackupVersionTask(updateKeysBackupVersionTask: DefaultUpdateKeysBackupVersionTask): UpdateKeysBackupVersionTask + + @Binds + abstract fun bindSendToDeviceTask(sendToDeviceTask: DefaultSendToDeviceTask): SendToDeviceTask + + @Binds + abstract fun bindClaimOneTimeKeysForUsersDeviceTask(claimOneTimeKeysForUsersDevice: DefaultClaimOneTimeKeysForUsersDevice): ClaimOneTimeKeysForUsersDeviceTask + + @Binds + abstract fun bindDeleteDeviceWithUserPasswordTask(deleteDeviceWithUserPasswordTask: DefaultDeleteDeviceWithUserPasswordTask): DeleteDeviceWithUserPasswordTask + + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt index ffda9507..5d471d5a 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -26,16 +26,19 @@ import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.SyncTokenStore import timber.log.Timber import java.util.* +import javax.inject.Inject // Legacy name: MXDeviceList -internal class DeviceListManager(private val cryptoStore: IMXCryptoStore, - private val olmDevice: MXOlmDevice, - private val syncTokenStore: SyncTokenStore, - private val credentials: Credentials, - private val downloadKeysForUsersTask: DownloadKeysForUsersTask) { +@SessionScope +internal class DeviceListManager @Inject constructor(private val cryptoStore: IMXCryptoStore, + private val olmDevice: MXOlmDevice, + private val syncTokenStore: SyncTokenStore, + private val credentials: Credentials, + private val downloadKeysForUsersTask: DownloadKeysForUsersTask) { // HS not ready for retry private val notReadyToRetryHS = HashSet() @@ -410,7 +413,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore, if (!isVerified) { Timber.e("## validateDeviceKeys() : Unable to verify signature on device " + userId + ":" - + deviceKeys.deviceId + " with error " + errorMessage) + + deviceKeys.deviceId + " with error " + errorMessage) return false } @@ -421,8 +424,8 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore, // // Should we warn the user about it somehow? Timber.e("## validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" - + deviceKeys.deviceId + " has changed : " - + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) + + deviceKeys.deviceId + " has changed : " + + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) Timber.e("## validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys") Timber.e("## validateDeviceKeys() : " + previouslyStoredDeviceKeys.keys + " -> " + deviceKeys.keys) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index 37d76340..7901ca1a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -23,11 +23,14 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import java.util.* +import javax.inject.Inject import kotlin.collections.ArrayList -internal class IncomingRoomKeyRequestManager( +@SessionScope +internal class IncomingRoomKeyRequestManager @Inject constructor( private val credentials: Credentials, private val cryptoStore: IMXCryptoStore, private val roomDecryptorProvider: RoomDecryptorProvider) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 12381a5b..243ddea6 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -26,15 +26,23 @@ import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrap import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.convertFromUTF8 import im.vector.matrix.android.internal.util.convertToUTF8 -import org.matrix.olm.* +import org.matrix.olm.OlmAccount +import org.matrix.olm.OlmInboundGroupSession +import org.matrix.olm.OlmMessage +import org.matrix.olm.OlmOutboundGroupSession +import org.matrix.olm.OlmSession +import org.matrix.olm.OlmUtility import timber.log.Timber import java.net.URLEncoder import java.util.* +import javax.inject.Inject // The libolm wrapper. -internal class MXOlmDevice( +@SessionScope +internal class MXOlmDevice @Inject constructor( /** * The store where crypto data is saved. */ @@ -670,7 +678,7 @@ internal class MXOlmDevice( val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) Timber.e("## decryptGroupMessage() : $reason") throw MXDecryptionException(MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, reason)) + MXCryptoError.UNABLE_TO_DECRYPT, reason)) } inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) @@ -703,7 +711,7 @@ internal class MXOlmDevice( val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## decryptGroupMessage() : $reason") throw MXDecryptionException(MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, reason)) + MXCryptoError.UNABLE_TO_DECRYPT, reason)) } } else { Timber.e("## decryptGroupMessage() : Cannot retrieve inbound group session $sessionId") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt index 80e03f5d..42239832 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt @@ -20,9 +20,12 @@ import android.text.TextUtils import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import java.util.* +import javax.inject.Inject -internal class MyDeviceInfoHolder( +@SessionScope +internal class MyDeviceInfoHolder @Inject constructor( // The credentials, credentials: Credentials, // the crypto store diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt index 78359abc..8ca33ea4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt @@ -17,9 +17,11 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.session.SessionScope import java.util.* +import javax.inject.Inject -internal class ObjectSigner(private val credentials: Credentials, +internal class ObjectSigner @Inject constructor(private val credentials: Credentials, private val olmDevice: MXOlmDevice) { /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index 20c6800e..27bd7e2a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -23,11 +23,14 @@ import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.session.SessionScope import org.matrix.olm.OlmAccount import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class OneTimeKeysUploader( +@SessionScope +internal class OneTimeKeysUploader @Inject constructor( private val credentials: Credentials, private val olmDevice: MXOlmDevice, private val objectSigner: ObjectSigner, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt index 4c68e905..a07b136c 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt @@ -26,12 +26,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareCancellat import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class OutgoingRoomKeyRequestManager( +@SessionScope +internal class OutgoingRoomKeyRequestManager @Inject constructor( private val cryptoStore: IMXCryptoStore, private val sendToDeviceTask: SendToDeviceTask, private val taskExecutor: TaskExecutor) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt index e75885a3..6e30484d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt @@ -20,10 +20,13 @@ import android.text.TextUtils import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class RoomDecryptorProvider( +@SessionScope +internal class RoomDecryptorProvider @Inject constructor( private val olmDecryptionFactory: MXOlmDecryptionFactory, private val megolmDecryptionFactory: MXMegolmDecryptionFactory ) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index da357c6a..36a5b5cd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -24,11 +24,13 @@ import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDevice, - private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { +internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val olmDevice: MXOlmDevice, + private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { suspend fun handle(devicesByUser: Map>): Try> { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index 6b7e28be..f6817e14 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -24,10 +24,12 @@ import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevice, +internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val olmDevice: MXOlmDevice, private val cryptoStore: IMXCryptoStore, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt index 433114df..918bdd73 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -26,12 +26,14 @@ import im.vector.matrix.android.internal.crypto.RoomDecryptorProvider import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber +import javax.inject.Inject -internal class MegolmSessionDataImporter(private val olmDevice: MXOlmDevice, - private val roomDecryptorProvider: RoomDecryptorProvider, - private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, - private val cryptoStore: IMXCryptoStore) { +internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice, + private val roomDecryptorProvider: RoomDecryptorProvider, + private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, + private val cryptoStore: IMXCryptoStore) { /** * Import a list of megolm session keys. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt index bbd23dad..4703237e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt @@ -23,12 +23,14 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.convertToUTF8 import timber.log.Timber import java.util.* +import javax.inject.Inject -internal class MessageEncrypter(private val credentials: Credentials, - private val olmDevice: MXOlmDevice) { +internal class MessageEncrypter @Inject constructor(private val credentials: Credentials, + private val olmDevice: MXOlmDevice) { /** * Encrypt an event payload for a list of devices. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt index b76f905b..dfc54ffe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt @@ -19,9 +19,11 @@ package im.vector.matrix.android.internal.crypto.actions import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber +import javax.inject.Inject -internal class SetDeviceVerificationAction(private val cryptoStore: IMXCryptoStore, +internal class SetDeviceVerificationAction @Inject constructor(private val cryptoStore: IMXCryptoStore, private val credentials: Credentials, private val keysBackup: KeysBackup) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index 1f2a3758..43c485df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -24,9 +24,11 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import javax.inject.Inject -internal class MXMegolmDecryptionFactory(private val credentials: Credentials, +internal class MXMegolmDecryptionFactory @Inject constructor(private val credentials: Credentials, private val olmDevice: MXOlmDevice, private val deviceListManager: DeviceListManager, private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt index e7daf9ad..2807863e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -25,9 +25,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor +import javax.inject.Inject -internal class MXMegolmEncryptionFactory( +internal class MXMegolmEncryptionFactory @Inject constructor( private val olmDevice: MXOlmDevice, private val keysBackup: KeysBackup, private val cryptoStore: IMXCryptoStore, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt index 5f18fe3e..a8b4b1a0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt @@ -18,9 +18,11 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.session.SessionScope +import javax.inject.Inject -internal class MXOlmDecryptionFactory(private val olmDevice: MXOlmDevice, - private val credentials: Credentials) { +internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice, + private val credentials: Credentials) { fun create(): MXOlmDecryption { return MXOlmDecryption( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt index 30c66a3e..99ab8eda 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt @@ -21,14 +21,16 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import javax.inject.Inject -internal class MXOlmEncryptionFactory(private val olmDevice: MXOlmDevice, - private val cryptoStore: IMXCryptoStore, - private val messageEncrypter: MessageEncrypter, - private val deviceListManager: DeviceListManager, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) { +internal class MXOlmEncryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice, + private val cryptoStore: IMXCryptoStore, + private val messageEncrypter: MessageEncrypter, + private val deviceListManager: DeviceListManager, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) { fun create(roomId: String): MXOlmEncryption { return MXOlmEncryption( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index 60e19e46..bd52c3d4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -51,6 +51,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskThread @@ -66,13 +67,16 @@ import org.matrix.olm.OlmPkMessage import timber.log.Timber import java.security.InvalidParameterException import java.util.* +import javax.inject.Inject import kotlin.collections.HashMap /** * A KeysBackup class instance manage incremental backup of e2e keys (megolm keys) * to the user's homeserver. */ -internal class KeysBackup( + +@SessionScope +internal class KeysBackup @Inject constructor( private val credentials: Credentials, private val cryptoStore: IMXCryptoStore, private val olmDevice: MXOlmDevice, @@ -448,7 +452,8 @@ internal class KeysBackup( val myUserId = credentials.userId // Get current signatures, or create an empty set - val myUserSignatures = (authData.signatures!![myUserId]?.toMutableMap() ?: HashMap()) + val myUserSignatures = (authData.signatures!![myUserId]?.toMutableMap() + ?: HashMap()) if (trust) { // Add current device signature @@ -1309,7 +1314,8 @@ internal class KeysBackup( "algorithm" to sessionData!!.algorithm, "sender_key" to sessionData.senderKey, "sender_claimed_keys" to sessionData.senderClaimedKeys, - "forwarding_curve25519_key_chain" to (sessionData.forwardingCurve25519KeyChain ?: ArrayList()), + "forwarding_curve25519_key_chain" to (sessionData.forwardingCurve25519KeyChain + ?: ArrayList()), "session_key" to sessionData.sessionKey) var encryptedSessionBackupData: OlmPkMessage? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt index e63201bd..f0c5037f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt @@ -22,11 +22,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeys import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface CreateKeysBackupVersionTask : Task - -internal class DefaultCreateKeysBackupVersionTask(private val roomKeysApi: RoomKeysApi) +internal class DefaultCreateKeysBackupVersionTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : CreateKeysBackupVersionTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt index 34d183a1..a1ad84ab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteBackupTask : Task { @@ -28,8 +30,7 @@ internal interface DeleteBackupTask : Task { ) } - -internal class DefaultDeleteBackupTask(private val roomKeysApi: RoomKeysApi) +internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : DeleteBackupTask { override suspend fun execute(params: DeleteBackupTask.Params): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt index e80e160b..c24d314c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteRoomSessionDataTask : Task { data class Params( @@ -29,8 +31,7 @@ internal interface DeleteRoomSessionDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt index 25e930f4..7476e2d2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteRoomSessionsDataTask : Task { data class Params( @@ -28,8 +30,7 @@ internal interface DeleteRoomSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt index 1580849a..14030775 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteSessionsDataTask : Task { data class Params( @@ -27,8 +29,7 @@ internal interface DeleteSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt index eb5d8899..48f405a0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt @@ -21,11 +21,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetKeysBackupLastVersionTask : Task - -internal class DefaultGetKeysBackupLastVersionTask(private val roomKeysApi: RoomKeysApi) +internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : GetKeysBackupLastVersionTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt index 209c8136..dfaeafa1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt @@ -21,11 +21,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetKeysBackupVersionTask : Task - -internal class DefaultGetKeysBackupVersionTask(private val roomKeysApi: RoomKeysApi) +internal class DefaultGetKeysBackupVersionTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : GetKeysBackupVersionTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt index 849e37eb..253a2a5a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt @@ -20,7 +20,9 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetRoomSessionDataTask : Task { data class Params( @@ -30,8 +32,7 @@ internal interface GetRoomSessionDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt index e67e2bc3..c5a6d19a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt @@ -20,7 +20,9 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetRoomSessionsDataTask : Task { @@ -30,8 +32,7 @@ internal interface GetRoomSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt index b2f3005b..837e3eb8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt @@ -20,7 +20,9 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetSessionsDataTask : Task { data class Params( @@ -28,8 +30,7 @@ internal interface GetSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt index e7452b0e..748dd106 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt @@ -21,7 +21,9 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface StoreRoomSessionDataTask : Task { data class Params( @@ -32,8 +34,7 @@ internal interface StoreRoomSessionDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt index 29ab818d..1799df33 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt @@ -21,7 +21,9 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface StoreRoomSessionsDataTask : Task { data class Params( @@ -31,8 +33,7 @@ internal interface StoreRoomSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt index c5b39d2d..977f8114 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt @@ -21,7 +21,9 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface StoreSessionsDataTask : Task { data class Params( @@ -30,8 +32,7 @@ internal interface StoreSessionsDataTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt index e2605bfb..74c39485 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt @@ -21,6 +21,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface UpdateKeysBackupVersionTask : Task { data class Params( @@ -29,8 +30,7 @@ internal interface UpdateKeysBackupVersionTask : Task> { data class Params( @@ -34,7 +36,7 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task> { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt index 6bdb72d8..0f93e213 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt @@ -25,7 +25,9 @@ import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteDeviceTask : Task { data class Params( @@ -33,7 +35,7 @@ internal interface DeleteDeviceTask : Task { ) } -internal class DefaultDeleteDeviceTask(private val cryptoApi: CryptoApi) +internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : DeleteDeviceTask { override suspend fun execute(params: DeleteDeviceTask.Params): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt index 7b6d1dfe..2476f242 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceAuth import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface DeleteDeviceWithUserPasswordTask : Task { data class Params( @@ -33,8 +34,8 @@ internal interface DeleteDeviceWithUserPasswordTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt index a8da9eab..00ea7178 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -22,8 +22,10 @@ import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* +import javax.inject.Inject internal interface DownloadKeysForUsersTask : Task { data class Params( @@ -33,7 +35,7 @@ internal interface DownloadKeysForUsersTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt index 7d634ab5..3779a102 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt @@ -20,11 +20,13 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetDevicesTask : Task -internal class DefaultGetDevicesTask(private val cryptoApi: CryptoApi) +internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi) : GetDevicesTask { override suspend fun execute(params: Unit): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt index 937607d9..21eabc22 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt @@ -20,7 +20,9 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetKeyChangesTask : Task { data class Params( @@ -31,7 +33,7 @@ internal interface GetKeyChangesTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt index 89417b08..27559f74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt @@ -21,8 +21,10 @@ import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* +import javax.inject.Inject internal interface SendToDeviceTask : Task { data class Params( @@ -35,7 +37,7 @@ internal interface SendToDeviceTask : Task { ) } -internal class DefaultSendToDeviceTask(private val cryptoApi: CryptoApi) +internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : SendToDeviceTask { override suspend fun execute(params: SendToDeviceTask.Params): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt index 22daf517..04613ddd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt @@ -21,7 +21,9 @@ import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface SetDeviceNameTask : Task { data class Params( @@ -32,7 +34,7 @@ internal interface SetDeviceNameTask : Task { ) } -internal class DefaultSetDeviceNameTask(private val cryptoApi: CryptoApi) +internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi) : SetDeviceNameTask { override suspend fun execute(params: SetDeviceNameTask.Params): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt index 067f8ff7..96c3a558 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt @@ -23,8 +23,10 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.convertToUTF8 +import javax.inject.Inject internal interface UploadKeysTask : Task { data class Params( @@ -36,7 +38,7 @@ internal interface UploadKeysTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index d13f13c4..ebf3294a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -35,6 +35,7 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.* import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers @@ -42,6 +43,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber import java.util.* +import javax.inject.Inject import kotlin.collections.HashMap /** @@ -49,14 +51,16 @@ import kotlin.collections.HashMap * Short codes interactive verification is a more user friendly way of verifying devices * that is still maintaining a good level of security (alternative to the 43-character strings compare method). */ -internal class DefaultSasVerificationService(private val credentials: Credentials, - private val cryptoStore: IMXCryptoStore, - private val myDeviceInfoHolder: MyDeviceInfoHolder, - private val deviceListManager: DeviceListManager, - private val setDeviceVerificationAction: SetDeviceVerificationAction, - private val sendToDeviceTask: SendToDeviceTask, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val taskExecutor: TaskExecutor) + +@SessionScope +internal class DefaultSasVerificationService @Inject constructor(private val credentials: Credentials, + private val cryptoStore: IMXCryptoStore, + private val myDeviceInfoHolder: MyDeviceInfoHolder, + private val deviceListManager: DeviceListManager, + private val setDeviceVerificationAction: SetDeviceVerificationAction, + private val sendToDeviceTask: SendToDeviceTask, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val taskExecutor: TaskExecutor) : VerificationTransaction.Listener, SasVerificationService { private val uiHandler = Handler(Looper.getMainLooper()) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/AuthQualifiers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/AuthQualifiers.kt new file mode 100644 index 00000000..e89d8b8e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/AuthQualifiers.kt @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class Authenticated + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class Unauthenticated \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixKoinHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/DbQualifiers.kt similarity index 64% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixKoinHolder.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/DbQualifiers.kt index e3d2047f..ac9a30a3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixKoinHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/DbQualifiers.kt @@ -16,22 +16,16 @@ package im.vector.matrix.android.internal.di -import org.koin.core.Koin -import org.koin.core.KoinContext -import org.koin.standalone.KoinComponent +import javax.inject.Qualifier -internal object MatrixKoinHolder { +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class AuthDatabase - val instance: Koin by lazy { - Koin.create() - } +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class SessionDatabase -} - -internal interface MatrixKoinComponent : KoinComponent { - - override fun getKoin(): KoinContext { - return MatrixKoinHolder.instance.koinContext - } - -} \ No newline at end of file +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class CryptoDatabase \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt new file mode 100644 index 00000000..ad03bbca --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.di + +import android.content.Context +import android.content.res.Resources +import com.squareup.moshi.Moshi +import dagger.BindsInstance +import dagger.Component +import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.auth.Authenticator +import im.vector.matrix.android.internal.SessionManager +import im.vector.matrix.android.internal.auth.AuthModule +import im.vector.matrix.android.internal.auth.SessionParamsStore +import im.vector.matrix.android.internal.network.NetworkConnectivityChecker +import im.vector.matrix.android.internal.network.RetrofitFactory +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.util.BackgroundDetectionObserver +import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import okhttp3.OkHttpClient +import org.matrix.olm.OlmManager +import retrofit2.Retrofit + + +@Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class]) +@MatrixScope +internal interface MatrixComponent { + + fun matrixCoroutineDispatchers(): MatrixCoroutineDispatchers + + fun moshi(): Moshi + + @Unauthenticated + fun okHttpClient(): OkHttpClient + + fun authenticator(): Authenticator + + fun context(): Context + + fun resources(): Resources + + fun olmManager(): OlmManager + + fun taskExecutor(): TaskExecutor + + fun sessionParamsStore(): SessionParamsStore + + fun networkConnectivityChecker(): NetworkConnectivityChecker + + fun backgroundDetectionObserver(): BackgroundDetectionObserver + + fun sessionManager(): SessionManager + + fun inject(matrix: Matrix) + + @Component.Factory + interface Factory { + fun create(@BindsInstance context: Context): MatrixComponent + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 84f6786b..875dd5f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -17,47 +17,44 @@ package im.vector.matrix.android.internal.di import android.content.Context +import android.content.res.Resources import android.os.Handler import android.os.HandlerThread -import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.util.BackgroundDetectionObserver +import dagger.Module +import dagger.Provides import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import im.vector.matrix.android.internal.util.StringProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.android.asCoroutineDispatcher -import org.koin.dsl.module.module +import org.matrix.olm.OlmManager +@Module +internal object MatrixModule { -class MatrixModule(private val context: Context) { - - val definition = module { - - single { - context - } - - single { - val THREAD_CRYPTO_NAME = "Crypto_Thread" - val handlerThread = HandlerThread(THREAD_CRYPTO_NAME) - handlerThread.start() - - MatrixCoroutineDispatchers(io = Dispatchers.IO, - computation = Dispatchers.IO, - main = Dispatchers.Main, - crypto = Handler(handlerThread.looper).asCoroutineDispatcher("crypto") - ) - } - - single { - TaskExecutor(get()) - } - single { - StringProvider(context.resources) - } - - single { - BackgroundDetectionObserver() - } + @JvmStatic + @Provides + fun providesMatrixCoroutineDispatchers(): MatrixCoroutineDispatchers { + val THREAD_CRYPTO_NAME = "Crypto_Thread" + val handlerThread = HandlerThread(THREAD_CRYPTO_NAME) + handlerThread.start() + return MatrixCoroutineDispatchers(io = Dispatchers.IO, + computation = Dispatchers.IO, + main = Dispatchers.Main, + crypto = Handler(handlerThread.looper).asCoroutineDispatcher("crypto") + ) } + + @JvmStatic + @Provides + fun providesResources(context: Context): Resources { + return context.resources + } + + @JvmStatic + @Provides + @MatrixScope + fun providesOlmManager(): OlmManager { + return OlmManager() + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixScope.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixScope.java new file mode 100644 index 00000000..30c9138e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixScope.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.di; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Scope +@Documented +@Retention(RUNTIME) +public @interface MatrixScope {} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt index d41f2cf8..2f9e8c69 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt @@ -17,85 +17,80 @@ package im.vector.matrix.android.internal.di import com.facebook.stetho.okhttp3.StethoInterceptor +import com.squareup.moshi.Moshi +import dagger.Module +import dagger.Provides import im.vector.matrix.android.BuildConfig -import im.vector.matrix.android.internal.network.* +import im.vector.matrix.android.internal.network.AccessTokenInterceptor +import im.vector.matrix.android.internal.network.UnitConverterFactory +import im.vector.matrix.android.internal.network.UserAgentInterceptor import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor import im.vector.matrix.android.internal.network.interceptors.FormattedJsonHttpLogger import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import okreplay.OkReplayInterceptor -import org.koin.dsl.module.module import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory import java.util.concurrent.TimeUnit -class NetworkModule { +@Module +internal object NetworkModule { - val definition = module { + @Provides + @JvmStatic + fun providesHttpLogingInterceptor(): HttpLoggingInterceptor { + val logger = FormattedJsonHttpLogger() + val interceptor = HttpLoggingInterceptor(logger) + interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL + return interceptor + } - single { - UserAgentHolder(get()) - } + @Provides + @JvmStatic + fun providesOkReplayInterceptor(): OkReplayInterceptor { + return OkReplayInterceptor() + } - single { - UserAgentInterceptor(get()) - } + @Provides + @JvmStatic + fun providesStethoInterceptor(): StethoInterceptor { + return StethoInterceptor() + } - single { - AccessTokenInterceptor(get()) - } + @Provides + @JvmStatic + fun providesCurlLoggingInterceptor(): CurlLoggingInterceptor { + return CurlLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT) + } - single { - val logger = FormattedJsonHttpLogger() - val interceptor = HttpLoggingInterceptor(logger) - interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL - interceptor - } - - single { - CurlLoggingInterceptor() - } - - single { - OkReplayInterceptor() - } - - single { - StethoInterceptor() - } - - single { - OkHttpClient.Builder() - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) - .addNetworkInterceptor(get()) - .addInterceptor(get()) - .addInterceptor(get()) - .addInterceptor(get()) - .apply { - if (BuildConfig.LOG_PRIVATE_DATA) { - addInterceptor(get()) - } + @MatrixScope + @Provides + @JvmStatic + @Unauthenticated + fun providesOkHttpClient(stethoInterceptor: StethoInterceptor, + userAgentInterceptor: UserAgentInterceptor, + httpLoggingInterceptor: HttpLoggingInterceptor, + curlLoggingInterceptor: CurlLoggingInterceptor, + okReplayInterceptor: OkReplayInterceptor): OkHttpClient { + return OkHttpClient.Builder() + .connectTimeout(1, TimeUnit.MINUTES) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .addNetworkInterceptor(stethoInterceptor) + .addInterceptor(userAgentInterceptor) + .addInterceptor(httpLoggingInterceptor) + .apply { + if (BuildConfig.LOG_PRIVATE_DATA) { + addInterceptor(curlLoggingInterceptor) } - .addInterceptor(get()) - .build() - } - - single { - MoshiProvider.providesMoshi() - } - - single { - NetworkConnectivityChecker(get()) - } - - factory { - Retrofit.Builder() - .client(get()) - .addConverterFactory(UnitConverterFactory) - .addConverterFactory(MoshiConverterFactory.create(get())) - } + } + .addInterceptor(okReplayInterceptor) + .build() + } + @Provides + @JvmStatic + fun providesMoshi(): Moshi { + return MoshiProvider.providesMoshi() } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/AccessTokenInterceptor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/AccessTokenInterceptor.kt index 77d2ec92..70b16a4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/AccessTokenInterceptor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/AccessTokenInterceptor.kt @@ -16,20 +16,18 @@ package im.vector.matrix.android.internal.network -import im.vector.matrix.android.internal.auth.SessionParamsStore +import im.vector.matrix.android.api.auth.data.Credentials import okhttp3.Interceptor import okhttp3.Response +import javax.inject.Inject -internal class AccessTokenInterceptor(private val sessionParamsStore: SessionParamsStore) : Interceptor { +internal class AccessTokenInterceptor @Inject constructor(private val credentials: Credentials) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { var request = chain.request() val newRequestBuilder = request.newBuilder() // Add the access token to all requests if it is set - val sessionParams = sessionParamsStore.get() - sessionParams?.let { - newRequestBuilder.addHeader(HttpHeaders.Authorization, "Bearer " + it.credentials.accessToken) - } + newRequestBuilder.addHeader(HttpHeaders.Authorization, "Bearer " + credentials.accessToken) request = newRequestBuilder.build() return chain.proceed(request) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt index ef83e47e..9502cb28 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt @@ -20,8 +20,11 @@ import android.content.Context import com.novoda.merlin.Merlin import com.novoda.merlin.MerlinsBeard import com.novoda.merlin.registerable.connection.Connectable +import im.vector.matrix.android.internal.di.MatrixScope +import javax.inject.Inject -internal class NetworkConnectivityChecker(context: Context) { +@MatrixScope +internal class NetworkConnectivityChecker @Inject constructor(context: Context) { private val merlin = Merlin.Builder().withConnectableCallbacks().build(context) private val merlinsBeard = MerlinsBeard.from(context) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt new file mode 100644 index 00000000..1338c36e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt @@ -0,0 +1,39 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.network + +import com.squareup.moshi.Moshi +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import javax.inject.Inject + +class RetrofitFactory @Inject constructor(private val moshi: Moshi) { + + fun create(okHttpClient: OkHttpClient, baseUrl: String): Retrofit { + return Retrofit.Builder() + .baseUrl(baseUrl) + .client(okHttpClient) + .addConverterFactory(UnitConverterFactory) + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .build() + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt index 7917f960..306b8a91 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentHolder.kt @@ -19,9 +19,12 @@ package im.vector.matrix.android.internal.network import android.content.Context import android.text.TextUtils import im.vector.matrix.android.BuildConfig +import im.vector.matrix.android.internal.di.MatrixScope import timber.log.Timber +import javax.inject.Inject -internal class UserAgentHolder(val context: Context) { +@MatrixScope +internal class UserAgentHolder @Inject constructor(val context: Context) { var userAgent: String = "" private set diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentInterceptor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentInterceptor.kt index 151ba0c3..44e3ddd1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentInterceptor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/UserAgentInterceptor.kt @@ -18,8 +18,9 @@ package im.vector.matrix.android.internal.network import okhttp3.Interceptor import okhttp3.Response +import javax.inject.Inject -internal class UserAgentInterceptor(private val userAgentHolder: UserAgentHolder) : Interceptor { +internal class UserAgentInterceptor @Inject constructor(private val userAgentHolder: UserAgentHolder) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { var request = chain.request() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 2c42b870..e77cfd3c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -23,132 +23,83 @@ import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.SessionParams -import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.pushrules.PushRuleService -import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.api.session.content.ContentUrlResolver -import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService -import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener -import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService -import im.vector.matrix.android.api.session.events.model.Content -import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.group.Group +import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.group.GroupService -import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.PushersService -import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomDirectoryService import im.vector.matrix.android.api.session.room.RoomService -import im.vector.matrix.android.api.session.room.model.RoomSummary -import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams -import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams -import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse -import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.user.UserService -import im.vector.matrix.android.api.session.user.model.User -import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.MatrixCallbackDelegate import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.crypto.CryptoModule -import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult -import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult -import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap -import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse -import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.database.LiveEntityObserver -import im.vector.matrix.android.internal.di.MatrixKoinComponent -import im.vector.matrix.android.internal.di.MatrixKoinHolder -import im.vector.matrix.android.internal.session.content.ContentModule -import im.vector.matrix.android.internal.session.group.GroupModule -import im.vector.matrix.android.internal.session.notification.BingRuleWatcher -import im.vector.matrix.android.internal.session.room.RoomModule -import im.vector.matrix.android.internal.session.signout.SignOutModule -import im.vector.matrix.android.internal.session.sync.SyncModule import im.vector.matrix.android.internal.session.sync.job.SyncThread import im.vector.matrix.android.internal.session.sync.job.SyncWorker -import im.vector.matrix.android.internal.session.user.UserModule -import org.koin.core.scope.Scope -import org.koin.standalone.inject import timber.log.Timber -import java.util.* +import javax.inject.Inject +@SessionScope +internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams, + private val context: Context, + private val liveEntityObservers: Set<@JvmSuppressWildcards LiveEntityObserver>, + private val monarchy: Monarchy, + private val sessionListeners: SessionListeners, + private val roomService: RoomService, + private val roomDirectoryService: RoomDirectoryService, + private val groupService: GroupService, + private val userService: UserService, + private val filterService: FilterService, + private val cacheService: CacheService, + private val signOutService: SignOutService, + private val pushRuleService: PushRuleService, + private val pushersService: PushersService, + private val cryptoService: CryptoManager, + private val syncThread: SyncThread, + private val contentUrlResolver: ContentUrlResolver, + private val contentUploadProgressTracker: ContentUploadStateTracker) + : Session, + RoomService by roomService, + RoomDirectoryService by roomDirectoryService, + GroupService by groupService, + UserService by userService, + CryptoService by cryptoService, + CacheService by cacheService, + SignOutService by signOutService, + FilterService by filterService, + PushRuleService by pushRuleService, + PushersService by pushersService { -internal class DefaultSession(override val sessionParams: SessionParams) : Session, MatrixKoinComponent { - - companion object { - const val SCOPE: String = "session" - } - - private lateinit var scope: Scope - - private val monarchy by inject() - private val liveEntityUpdaters by inject>() - private val sessionListeners by inject() - private val roomService by inject() - private val roomDirectoryService by inject() - private val groupService by inject() - private val userService by inject() - private val filterService by inject() - private val cacheService by inject() - private val signOutService by inject() - private val cryptoService by inject() - private val syncThread by inject() - private val contentUrlResolver by inject() - private val contentUploadProgressTracker by inject() - private val pushRuleService by inject() - private val pushersService by inject() private var isOpen = false - private val bingRuleWatcher by inject() @MainThread override fun open() { assertMainThread() assert(!isOpen) isOpen = true - val sessionModule = SessionModule(sessionParams).definition - val syncModule = SyncModule().definition - val roomModule = RoomModule().definition - val groupModule = GroupModule().definition - val signOutModule = SignOutModule().definition - val userModule = UserModule().definition - val contentModule = ContentModule().definition - val cryptoModule = CryptoModule().definition - MatrixKoinHolder.instance.loadModules(listOf(sessionModule, - syncModule, - roomModule, - groupModule, - userModule, - signOutModule, - contentModule, - cryptoModule)) - scope = getKoin().getOrCreateScope(SCOPE) if (!monarchy.isMonarchyThreadOpen) { monarchy.openManually() } - liveEntityUpdaters.forEach { it.start() } - bingRuleWatcher.start() + liveEntityObservers.forEach { it.start() } } override fun requireBackgroundSync() { - SyncWorker.requireBackgroundSync() + SyncWorker.requireBackgroundSync(context, sessionParams.credentials.userId) } override fun startAutomaticBackgroundSync(repeatDelay: Long) { - SyncWorker.automaticallyBackgroundSync(0, repeatDelay) + SyncWorker.automaticallyBackgroundSync(context, sessionParams.credentials.userId, 0, repeatDelay) } override fun stopAnyBackgroundSync() { - SyncWorker.stopAnyBackgroundSync() + SyncWorker.stopAnyBackgroundSync(context) } @MainThread @@ -172,13 +123,11 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi override fun close() { assertMainThread() assert(isOpen) - liveEntityUpdaters.forEach { it.dispose() } + liveEntityObservers.forEach { it.dispose() } cryptoService.close() - bingRuleWatcher.dispose() if (monarchy.isMonarchyThreadOpen) { monarchy.closeManually() } - scope.close() isOpen = false } @@ -222,13 +171,9 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi }) } - override fun contentUrlResolver(): ContentUrlResolver { - return contentUrlResolver - } + override fun contentUrlResolver() = contentUrlResolver - override fun contentUploadProgressTracker(): ContentUploadStateTracker { - return contentUploadProgressTracker - } + override fun contentUploadProgressTracker() = contentUploadProgressTracker override fun addListener(listener: Session.Listener) { sessionListeners.addListener(listener) @@ -238,235 +183,6 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi sessionListeners.removeListener(listener) } - // ROOM SERVICE - - override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback) { - assert(isOpen) - return roomService.createRoom(createRoomParams, callback) - } - - override fun getRoom(roomId: String): Room? { - assert(isOpen) - return roomService.getRoom(roomId) - } - - - override fun liveRoomSummaries(): LiveData> { - assert(isOpen) - return roomService.liveRoomSummaries() - } - - // ROOM DIRECTORY SERVICE - - override fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, callback: MatrixCallback): Cancelable { - assert(isOpen) - return roomDirectoryService.getPublicRooms(server, publicRoomsParams, callback) - } - - override fun joinRoom(roomId: String, callback: MatrixCallback) { - assert(isOpen) - return roomDirectoryService.joinRoom(roomId, callback) - } - - override fun getThirdPartyProtocol(callback: MatrixCallback>) { - assert(isOpen) - return roomDirectoryService.getThirdPartyProtocol(callback) - } - - // GROUP SERVICE - - override fun getGroup(groupId: String): Group? { - assert(isOpen) - return groupService.getGroup(groupId) - } - - override fun liveGroupSummaries(): LiveData> { - assert(isOpen) - return groupService.liveGroupSummaries() - } - - override fun setFilter(filterPreset: FilterService.FilterPreset) { - assert(isOpen) - return filterService.setFilter(filterPreset) - } - - override fun clearCache(callback: MatrixCallback) { - assert(isOpen) - // syncThread.pause() - cacheService.clearCache(object : MatrixCallbackDelegate(callback) { - override fun onSuccess(data: Unit) { - // Restart the sync - // syncThread.restart() - - super.onSuccess(data) - } - }) - } - - // USER SERVICE - - override fun getUser(userId: String): User? { - assert(isOpen) - return userService.getUser(userId) - } - - override fun observeUser(userId: String): LiveData { - assert(isOpen) - return userService.observeUser(userId) - } - - // CRYPTO SERVICE - - override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) { - cryptoService.setDeviceName(deviceId, deviceName, callback) - } - - override fun deleteDevice(deviceId: String, callback: MatrixCallback) { - cryptoService.deleteDevice(deviceId, callback) - } - - override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback) { - cryptoService.deleteDeviceWithUserPassword(deviceId, authSession, password, callback) - } - - override fun getCryptoVersion(context: Context, longFormat: Boolean): String { - return cryptoService.getCryptoVersion(context, longFormat) - } - - override fun isCryptoEnabled(): Boolean { - return cryptoService.isCryptoEnabled() - } - - override fun getSasVerificationService(): SasVerificationService { - return cryptoService.getSasVerificationService() - } - - override fun getKeysBackupService(): KeysBackupService { - return cryptoService.getKeysBackupService() - } - - override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean { - return cryptoService.isRoomBlacklistUnverifiedDevices(roomId) - } - - override fun setWarnOnUnknownDevices(warn: Boolean) { - cryptoService.setWarnOnUnknownDevices(warn) - } - - override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) { - cryptoService.setDeviceVerification(verificationStatus, deviceId, userId) - } - - override fun getUserDevices(userId: String): MutableList { - return cryptoService.getUserDevices(userId) - } - - override fun setDevicesKnown(devices: List, callback: MatrixCallback?) { - cryptoService.setDevicesKnown(devices, callback) - } - - override fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? { - return cryptoService.deviceWithIdentityKey(senderKey, algorithm) - } - - override fun getMyDevice(): MXDeviceInfo { - return cryptoService.getMyDevice() - } - - override fun getDevicesList(callback: MatrixCallback) { - cryptoService.getDevicesList(callback) - } - - override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int { - return cryptoService.inboundGroupSessionsCount(onlyBackedUp) - } - - override fun getGlobalBlacklistUnverifiedDevices(): Boolean { - return cryptoService.getGlobalBlacklistUnverifiedDevices() - } - - override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) { - cryptoService.setGlobalBlacklistUnverifiedDevices(block) - } - - override fun setRoomUnBlacklistUnverifiedDevices(roomId: String) { - cryptoService.setRoomUnBlacklistUnverifiedDevices(roomId) - } - - override fun getDeviceTrackingStatus(userId: String): Int { - return cryptoService.getDeviceTrackingStatus(userId) - } - - override fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?, callback: MatrixCallback) { - cryptoService.importRoomKeys(roomKeysAsArray, password, progressListener, callback) - } - - override fun exportRoomKeys(password: String, callback: MatrixCallback) { - cryptoService.exportRoomKeys(password, callback) - } - - override fun setRoomBlacklistUnverifiedDevices(roomId: String) { - cryptoService.setRoomBlacklistUnverifiedDevices(roomId) - } - - override fun isRoomEncrypted(roomId: String): Boolean { - return cryptoService.isRoomEncrypted(roomId) - } - - override fun encryptEventContent(eventContent: Content, eventType: String, roomId: String, callback: MatrixCallback) { - cryptoService.encryptEventContent(eventContent, eventType, roomId, callback) - } - - override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? { - return cryptoService.getDeviceInfo(userId, deviceId) - } - - override fun reRequestRoomKeyForEvent(event: Event) { - cryptoService.reRequestRoomKeyForEvent(event) - } - - override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) { - cryptoService.cancelRoomKeyRequest(requestBody) - } - - override fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { - cryptoService.addRoomKeysRequestListener(listener) - } - - override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { - return cryptoService.decryptEvent(event, timeline) - } - - override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) { - return cryptoService.decryptEventAsync(event, timeline, callback) - } - - override fun getEncryptionAlgorithm(roomId: String): String? { - return cryptoService.getEncryptionAlgorithm(roomId) - } - - override fun shouldEncryptForInvitedMembers(roomId: String): Boolean { - return cryptoService.shouldEncryptForInvitedMembers(roomId) - } - - override fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) { - cryptoService.downloadKeys(userIds, forceDownload, callback) - } - - override fun clearCryptoCache(callback: MatrixCallback) { - cryptoService.clearCryptoCache(callback) - } - - // PUSH RULE SERVICE - - override fun addPushRuleListener(listener: PushRuleService.PushRuleListener) { - pushRuleService.addPushRuleListener(listener) - } - - override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) { - pushRuleService.removePushRuleListener(listener) - } - // Private methods ***************************************************************************** private fun assertMainThread() { @@ -475,49 +191,4 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi } } - override fun refreshPushers() { - pushersService.refreshPushers() - } - - override fun addHttpPusher( - pushkey: String, - appId: String, - profileTag: String, - lang: String, - appDisplayName: String, - deviceDisplayName: String, - url: String, - append: Boolean, - withEventIdOnly: Boolean): UUID { - return pushersService - .addHttpPusher( - pushkey, appId, profileTag, lang, appDisplayName, deviceDisplayName, url, append, withEventIdOnly - ) - } - - override fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback) { - pushersService.removeHttpPusher(pushkey, appId, callback) - } - - override fun livePushers(): LiveData> { - return pushersService.livePushers() - } - - override fun pushers(): List { - return pushersService.pushers() - } - - override fun getPushRules(scope: String): List { - return pushRuleService.getPushRules(scope) - } - - override fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback) { - pushRuleService.updatePushRuleEnableStatus(kind, pushRule, enabled, callback) - } - - override fun fetchPushRules(scope: String) { - pushRuleService.fetchPushRules(scope) - } - - } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt new file mode 100644 index 00000000..fc23b780 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session + +import dagger.BindsInstance +import dagger.Component +import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.internal.crypto.CryptoModule +import im.vector.matrix.android.internal.di.MatrixComponent +import im.vector.matrix.android.internal.network.NetworkConnectivityChecker +import im.vector.matrix.android.internal.session.cache.CacheModule +import im.vector.matrix.android.internal.session.content.ContentModule +import im.vector.matrix.android.internal.session.content.UploadContentWorker +import im.vector.matrix.android.internal.session.filter.FilterModule +import im.vector.matrix.android.internal.session.group.GetGroupDataWorker +import im.vector.matrix.android.internal.session.group.GroupModule +import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker +import im.vector.matrix.android.internal.session.pushers.PushersModule +import im.vector.matrix.android.internal.session.room.RoomModule +import im.vector.matrix.android.internal.session.room.relation.SendRelationWorker +import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker +import im.vector.matrix.android.internal.session.room.send.RedactEventWorker +import im.vector.matrix.android.internal.session.room.send.SendEventWorker +import im.vector.matrix.android.internal.session.signout.SignOutModule +import im.vector.matrix.android.internal.session.sync.SyncModule +import im.vector.matrix.android.internal.session.sync.SyncTask +import im.vector.matrix.android.internal.session.sync.SyncTokenStore +import im.vector.matrix.android.internal.session.sync.job.SyncWorker +import im.vector.matrix.android.internal.session.user.UserModule +import im.vector.matrix.android.internal.task.TaskExecutor + +@Component(dependencies = [MatrixComponent::class], + modules = [ + SessionModule::class, + RoomModule::class, + SyncModule::class, + SignOutModule::class, + GroupModule::class, + UserModule::class, + FilterModule::class, + GroupModule::class, + ContentModule::class, + CacheModule::class, + CryptoModule::class, + PushersModule::class + ] +) +@SessionScope +internal interface SessionComponent { + + fun session(): Session + + fun syncTask(): SyncTask + + fun syncTokenStore(): SyncTokenStore + + fun networkConnectivityChecker(): NetworkConnectivityChecker + + fun taskExecutor(): TaskExecutor + + fun inject(sendEventWorker: SendEventWorker) + + fun inject(sendEventWorker: SendRelationWorker) + + fun inject(encryptEventWorker: EncryptEventWorker) + + fun inject(redactEventWorker: RedactEventWorker) + + fun inject(getGroupDataWorker: GetGroupDataWorker) + + fun inject(uploadContentWorker: UploadContentWorker) + + fun inject(syncWorker: SyncWorker) + + fun inject(addHttpPusherWorker: AddHttpPusherWorker) + + @Component.Factory + interface Factory { + fun create( + matrixComponent: MatrixComponent, + @BindsInstance sessionParams: SessionParams): SessionComponent + } + + +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt index 4fcb16e8..51b73547 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt @@ -17,8 +17,9 @@ package im.vector.matrix.android.internal.session import im.vector.matrix.android.api.session.Session +import javax.inject.Inject -internal class SessionListeners { +internal class SessionListeners @Inject constructor(){ private val listeners = ArrayList() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 1ea4803a..10d4abc9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -18,64 +18,60 @@ package im.vector.matrix.android.internal.session import android.content.Context import com.zhuinden.monarchy.Monarchy +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoSet +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.SessionParams -import im.vector.matrix.android.api.pushrules.PushRuleService -import im.vector.matrix.android.api.session.cache.CacheService -import im.vector.matrix.android.api.session.group.GroupService -import im.vector.matrix.android.api.session.pushers.PushersService -import im.vector.matrix.android.api.session.room.RoomDirectoryService -import im.vector.matrix.android.api.session.room.RoomService -import im.vector.matrix.android.api.session.signout.SignOutService -import im.vector.matrix.android.api.session.sync.FilterService -import im.vector.matrix.android.api.session.user.UserService +import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.database.model.SessionRealmModule -import im.vector.matrix.android.internal.session.cache.ClearCacheTask -import im.vector.matrix.android.internal.session.cache.RealmCacheService -import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask -import im.vector.matrix.android.internal.session.filter.* -import im.vector.matrix.android.internal.session.group.DefaultGroupService +import im.vector.matrix.android.internal.di.Authenticated +import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.di.Unauthenticated +import im.vector.matrix.android.internal.network.AccessTokenInterceptor +import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.notification.BingRuleWatcher -import im.vector.matrix.android.internal.session.notification.DefaultProcessEventForPushTask -import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService -import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask -import im.vector.matrix.android.internal.session.pushers.* -import im.vector.matrix.android.internal.session.room.* -import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask -import im.vector.matrix.android.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask -import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask -import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyProtocolsTask -import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver -import im.vector.matrix.android.internal.session.room.membership.RoomMemberDisplayNameResolver +import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater import im.vector.matrix.android.internal.session.room.prune.EventsPruner -import im.vector.matrix.android.internal.session.signout.DefaultSignOutService -import im.vector.matrix.android.internal.session.user.DefaultUserService import im.vector.matrix.android.internal.session.user.UserEntityUpdater import im.vector.matrix.android.internal.util.md5 import io.realm.RealmConfiguration -import org.koin.dsl.module.module +import okhttp3.OkHttpClient import retrofit2.Retrofit import java.io.File -internal class SessionModule(private val sessionParams: SessionParams) { +@Module +internal abstract class SessionModule { - val definition = module(override = true) { + @Module + companion object { - scope(DefaultSession.SCOPE) { - sessionParams + @JvmStatic + @Provides + fun providesHomeServerConnectionConfig(sessionParams: SessionParams): HomeServerConnectionConfig { + return sessionParams.homeServerConnectionConfig } - scope(DefaultSession.SCOPE) { - sessionParams.credentials + + @JvmStatic + @Provides + fun providesCredentials(sessionParams: SessionParams): Credentials { + return sessionParams.credentials } - scope(DefaultSession.SCOPE, name = "SessionRealmConfiguration") { - val context = get() + @JvmStatic + @Provides + @SessionDatabase + @SessionScope + fun providesRealmConfiguration(sessionParams: SessionParams, context: Context): RealmConfiguration { val childPath = sessionParams.credentials.userId.md5() val directory = File(context.filesDir, childPath) - RealmConfiguration.Builder() + return RealmConfiguration.Builder() .directory(directory) .name("disk_store.realm") .modules(SessionRealmModule()) @@ -83,147 +79,60 @@ internal class SessionModule(private val sessionParams: SessionParams) { .build() } - scope(DefaultSession.SCOPE) { - Monarchy.Builder() - .setRealmConfiguration(get("SessionRealmConfiguration")) + @JvmStatic + @Provides + @SessionScope + fun providesMonarchy(@SessionDatabase + realmConfiguration: RealmConfiguration): Monarchy { + return Monarchy.Builder() + .setRealmConfiguration(realmConfiguration) .build() } - scope(DefaultSession.SCOPE) { - val retrofitBuilder = get() - retrofitBuilder - .baseUrl(sessionParams.homeServerConnectionConfig.homeServerUri.toString()) + @JvmStatic + @Provides + @SessionScope + @Authenticated + fun providesOkHttpClient(@Unauthenticated okHttpClient: OkHttpClient, + accessTokenInterceptor: AccessTokenInterceptor): OkHttpClient { + return okHttpClient.newBuilder() + .addInterceptor(accessTokenInterceptor) .build() } - scope(DefaultSession.SCOPE) { - RoomMemberDisplayNameResolver() + @JvmStatic + @Provides + @SessionScope + fun providesRetrofit(@Authenticated okHttpClient: OkHttpClient, + sessionParams: SessionParams, + retrofitFactory: RetrofitFactory): Retrofit { + return retrofitFactory + .create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUri.toString()) } - - scope(DefaultSession.SCOPE) { - RoomDisplayNameResolver(get(), get(), get(), sessionParams.credentials) - } - - scope(DefaultSession.SCOPE) { - RoomAvatarResolver(get(), get()) - } - - scope(DefaultSession.SCOPE) { - RoomSummaryUpdater(get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - DefaultRoomService(get(), get(), get(), get()) as RoomService - } - - scope(DefaultSession.SCOPE) { - DefaultGetPublicRoomTask(get()) as GetPublicRoomTask - } - - scope(DefaultSession.SCOPE) { - DefaultGetThirdPartyProtocolsTask(get()) as GetThirdPartyProtocolsTask - } - - scope(DefaultSession.SCOPE) { - DefaultRoomDirectoryService(get(), get(), get(), get()) as RoomDirectoryService - } - - scope(DefaultSession.SCOPE) { - DefaultGroupService(get()) as GroupService - } - - scope(DefaultSession.SCOPE) { - DefaultSignOutService(get(), get()) as SignOutService - } - - scope(DefaultSession.SCOPE) { - RealmCacheService(get("ClearTaskMainCache"), get()) as CacheService - } - - // Give a name, because we have a clear task for crypto store as well - scope(DefaultSession.SCOPE, name = "ClearTaskMainCache") { - RealmClearCacheTask(get("SessionRealmConfiguration")) as ClearCacheTask - } - - scope(DefaultSession.SCOPE) { - DefaultUserService(get()) as UserService - } - - scope(DefaultSession.SCOPE) { - SessionListeners() - } - - scope(DefaultSession.SCOPE) { - DefaultFilterRepository(get("SessionRealmConfiguration")) as FilterRepository - } - - scope(DefaultSession.SCOPE) { - DefaultSaveFilterTask(get(), get(), get()) as SaveFilterTask - } - - scope(DefaultSession.SCOPE) { - DefaultFilterService(get(), get(), get()) as FilterService - } - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(FilterApi::class.java) - } - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(PushRulesApi::class.java) - } - - scope(DefaultSession.SCOPE) { - get() as PushRuleService - } - - scope(DefaultSession.SCOPE) { - DefaultPushRuleService(get(), get(), get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - DefaultGetPushRulesTask(get()) as GetPushRulesTask - } - - scope(DefaultSession.SCOPE) { - DefaultUpdatePushRuleEnableStatusTask(get()) as UpdatePushRuleEnableStatusTask - } - - scope(DefaultSession.SCOPE) { - DefaultProcessEventForPushTask(get()) as ProcessEventForPushTask - } - - scope(DefaultSession.SCOPE) { - BingRuleWatcher(get(), get(), get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - val groupSummaryUpdater = GroupSummaryUpdater(get()) - val userEntityUpdater = UserEntityUpdater(get(), get(), get()) - val aggregationUpdater = EventRelationsAggregationUpdater(get(), get(), get(), get()) - //Event pruner must be the last one, because it will clear contents - val eventsPruner = EventsPruner(get(), get(), get(), get()) - listOf(groupSummaryUpdater, userEntityUpdater, aggregationUpdater, eventsPruner) - } - - scope(DefaultSession.SCOPE) { - get().create(PushersAPI::class.java) - } - - scope(DefaultSession.SCOPE) { - DefaultGetPusherTask(get()) as GetPushersTask - } - scope(DefaultSession.SCOPE) { - DefaultRemovePusherTask(get(), get()) as RemovePusherTask - } - - scope(DefaultSession.SCOPE) { - DefaultPusherService(get(), get(), get(), get(), get()) as PushersService - } - } + @Binds + abstract fun bindSession(session: DefaultSession): Session + + @Binds + @IntoSet + abstract fun bindGroupSummaryUpdater(groupSummaryUpdater: GroupSummaryUpdater): LiveEntityObserver + + @Binds + @IntoSet + abstract fun bindEventsPruner(eventsPruner: EventsPruner): LiveEntityObserver + + @Binds + @IntoSet + abstract fun bindEventRelationsAggregationUpdater(groupSummaryUpdater: EventRelationsAggregationUpdater): LiveEntityObserver + + @Binds + @IntoSet + abstract fun bindBingRuleWatcher(bingRuleWatcher: BingRuleWatcher): LiveEntityObserver + + @Binds + @IntoSet + abstract fun bindUserEntityUpdater(groupSummaryUpdater: UserEntityUpdater): LiveEntityObserver + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionScope.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionScope.java new file mode 100644 index 00000000..52842641 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionScope.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Scope +@Documented +@Retention(RUNTIME) +public @interface SessionScope {} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt new file mode 100644 index 00000000..8da6dab6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt @@ -0,0 +1,45 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.cache + +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.cache.CacheService +import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.session.SessionScope +import io.realm.RealmConfiguration + +@Module +internal abstract class CacheModule { + + @Module + companion object { + @JvmStatic + @Provides + @SessionDatabase + fun providesClearCacheTask(@SessionDatabase realmConfiguration: RealmConfiguration): ClearCacheTask { + return RealmClearCacheTask(realmConfiguration) + } + } + + @Binds + abstract fun bindCacheService(cacheService: DefaultCacheService): CacheService + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmClearCacheTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt similarity index 82% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmClearCacheTask.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt index f1e927f0..0dd39c10 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmClearCacheTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt @@ -17,13 +17,16 @@ package im.vector.matrix.android.internal.session.cache import arrow.core.Try +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm import io.realm.RealmConfiguration +import javax.inject.Inject +import javax.inject.Named internal interface ClearCacheTask : Task -internal class RealmClearCacheTask(val realmConfiguration: RealmConfiguration) : ClearCacheTask { +internal class RealmClearCacheTask @Inject constructor(private val realmConfiguration: RealmConfiguration) : ClearCacheTask { override suspend fun execute(params: Unit): Try { return Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmCacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt similarity index 74% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmCacheService.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt index 7af01b7e..34c5c5be 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/RealmCacheService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt @@ -18,11 +18,14 @@ package im.vector.matrix.android.internal.session.cache import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.cache.CacheService +import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject -internal class RealmCacheService(private val clearCacheTask: ClearCacheTask, - private val taskExecutor: TaskExecutor) : CacheService { +internal class DefaultCacheService @Inject constructor(@SessionDatabase private val clearCacheTask: ClearCacheTask, + private val taskExecutor: TaskExecutor) : CacheService { override fun clearCache(callback: MatrixCallback) { clearCacheTask diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentModule.kt index 7fb9b757..1faf489d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentModule.kt @@ -16,34 +16,17 @@ package im.vector.matrix.android.internal.session.content -import im.vector.matrix.android.api.auth.data.SessionParams +import dagger.Binds +import dagger.Module import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.api.session.content.ContentUrlResolver -import im.vector.matrix.android.internal.session.DefaultSession -import org.koin.dsl.module.module -internal class ContentModule { - - val definition = module(override = true) { - - scope(DefaultSession.SCOPE) { - DefaultContentUploadStateTracker() - } - - scope(DefaultSession.SCOPE) { - get() as ContentUploadStateTracker - } - - scope(DefaultSession.SCOPE) { - FileUploader(get(), get()) - } - - scope(DefaultSession.SCOPE) { - val sessionParams = get() - DefaultContentUrlResolver(sessionParams.homeServerConnectionConfig) as ContentUrlResolver - } - - } +@Module +internal abstract class ContentModule { + @Binds + abstract fun bindContentUploadStateTracker(contentUploadStateTracker: DefaultContentUploadStateTracker): ContentUploadStateTracker + @Binds + abstract fun bindContentUrlResolver(contentUrlResolver: DefaultContentUrlResolver): ContentUrlResolver } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt index a5dd16fb..fbd7983d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt @@ -19,8 +19,11 @@ package im.vector.matrix.android.internal.session.content import android.os.Handler import android.os.Looper import im.vector.matrix.android.api.session.content.ContentUploadStateTracker +import im.vector.matrix.android.internal.session.SessionScope +import javax.inject.Inject -internal class DefaultContentUploadStateTracker : ContentUploadStateTracker { +@SessionScope +internal class DefaultContentUploadStateTracker @Inject constructor() : ContentUploadStateTracker { private val mainHandler = Handler(Looper.getMainLooper()) private val states = mutableMapOf() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt index 0fad40d0..55508b11 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt @@ -18,12 +18,13 @@ package im.vector.matrix.android.internal.session.content import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.session.content.ContentUrlResolver +import javax.inject.Inject private const val MATRIX_CONTENT_URI_SCHEME = "mxc://" private const val URI_PREFIX_CONTENT_API = "_matrix/media/v1/" -internal class DefaultContentUrlResolver(private val homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver { +internal class DefaultContentUrlResolver @Inject constructor(private val homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver { companion object { fun getUploadUrl(homeServerConnectionConfig: HomeServerConnectionConfig): String { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt index e8133070..61052084 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt @@ -18,20 +18,22 @@ package im.vector.matrix.android.internal.session.content import arrow.core.Try import arrow.core.Try.Companion.raise +import com.squareup.moshi.Moshi import im.vector.matrix.android.api.auth.data.SessionParams -import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.di.Authenticated import im.vector.matrix.android.internal.network.ProgressRequestBody import okhttp3.* import java.io.File import java.io.IOException +import javax.inject.Inject -internal class FileUploader(private val okHttpClient: OkHttpClient, - sessionParams: SessionParams) { +internal class FileUploader @Inject constructor(@Authenticated + private val okHttpClient: OkHttpClient, + private val sessionParams: SessionParams, + private val moshi: Moshi) { private val uploadUrl = DefaultContentUrlResolver.getUploadUrl(sessionParams.homeServerConnectionConfig) - - private val moshi = MoshiProvider.providesMoshi() private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt index bdfd9395..4c3235e1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -25,31 +25,35 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.* -import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.network.ProgressRequestBody import im.vector.matrix.android.internal.session.room.send.SendEventWorker -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent import timber.log.Timber import java.io.File +import javax.inject.Inject -internal class UploadContentWorker(context: Context, params: WorkerParameters) - : CoroutineWorker(context, params), MatrixKoinComponent { - - private val fileUploader by inject() - private val contentUploadProgressTracker by inject() +internal class UploadContentWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val roomId: String, val event: Event, val attachment: ContentAttachmentData - ) + ) : SessionWorkerParams + + @Inject lateinit var fileUploader: FileUploader + @Inject lateinit var contentUploadStateTracker: DefaultContentUploadStateTracker override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) - ?: return Result.success() + ?: return Result.success() + + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) val eventId = params.event.eventId ?: return Result.success() val attachment = params.attachment @@ -69,7 +73,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) val progressListener = object : ProgressRequestBody.Listener { override fun onProgress(current: Long, total: Long) { - contentUploadProgressTracker.setProgress(eventId, current, total) + contentUploadStateTracker.setProgress(eventId, current, total) } } return fileUploader @@ -90,16 +94,16 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) } private fun handleFailure(params: Params): Result { - contentUploadProgressTracker.setFailure(params.event.eventId!!) + contentUploadStateTracker.setFailure(params.event.eventId!!) return Result.success() } private fun handleSuccess(params: Params, attachmentUrl: String, thumbnailUrl: String?): Result { - contentUploadProgressTracker.setFailure(params.event.eventId!!) + contentUploadStateTracker.setSuccess(params.event.eventId!!) val event = updateEvent(params.event, attachmentUrl, thumbnailUrl) - val sendParams = SendEventWorker.Params(params.roomId, event) + val sendParams = SendEventWorker.Params(params.userId, params.roomId, event) return Result.success(WorkerParamsFactory.toData(sendParams)) } @@ -131,6 +135,5 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) return copy(url = url) } - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt index 32d9541a..c46da8b3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterRepository.kt @@ -19,11 +19,15 @@ package im.vector.matrix.android.internal.session.filter import im.vector.matrix.android.internal.database.model.FilterEntity import im.vector.matrix.android.internal.database.model.FilterEntityFields import im.vector.matrix.android.internal.database.query.getFilter +import im.vector.matrix.android.internal.di.SessionDatabase import io.realm.Realm import io.realm.RealmConfiguration import io.realm.kotlin.where +import javax.inject.Inject -internal class DefaultFilterRepository(val realmConfiguration: RealmConfiguration) : FilterRepository { +internal class DefaultFilterRepository @Inject constructor( + @SessionDatabase private val realmConfiguration: RealmConfiguration +) : FilterRepository { override fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean { val result: Boolean diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt index 94c3f1cf..445e416b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultFilterService.kt @@ -19,10 +19,11 @@ package im.vector.matrix.android.internal.session.filter import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject -internal class DefaultFilterService(private val filterRepository: FilterRepository, - private val saveFilterTask: SaveFilterTask, - private val taskExecutor: TaskExecutor) : FilterService { +internal class DefaultFilterService @Inject constructor(private val filterRepository: FilterRepository, + private val saveFilterTask: SaveFilterTask, + private val taskExecutor: TaskExecutor) : FilterService { // TODO Pass a list of support events instead override fun setFilter(filterPreset: FilterService.FilterPreset) { @@ -30,7 +31,7 @@ internal class DefaultFilterService(private val filterRepository: FilterReposito FilterService.FilterPreset.RiotFilter -> { FilterFactory.createRiotFilterBody() } - FilterService.FilterPreset.NoFilter -> { + FilterService.FilterPreset.NoFilter -> { FilterFactory.createDefaultFilterBody() } } @@ -39,7 +40,7 @@ internal class DefaultFilterService(private val filterRepository: FilterReposito FilterService.FilterPreset.RiotFilter -> { FilterFactory.createRiotRoomFilter() } - FilterService.FilterPreset.NoFilter -> { + FilterService.FilterPreset.NoFilter -> { FilterFactory.createDefaultRoomFilter() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt index ab826805..5fa5d0a2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt @@ -20,6 +20,7 @@ import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject /** @@ -33,9 +34,9 @@ internal interface SaveFilterTask : Task { } -internal class DefaultSaveFilterTask(private val sessionParams: SessionParams, - private val filterAPI: FilterApi, - private val filterRepository: FilterRepository +internal class DefaultSaveFilterTask @Inject constructor(private val sessionParams: SessionParams, + private val filterAPI: FilterApi, + private val filterRepository: FilterRepository ) : SaveFilterTask { override suspend fun execute(params: SaveFilterTask.Params): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt index a10f654f..092038ee 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterApi.kt @@ -42,5 +42,6 @@ internal interface FilterApi { * @return Filter */ @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter/{filterId}") - fun getFilterById(@Path("userId") userId: String, @Path("filterId") filterId: String): Call + fun getFilterById(@Path("userId") userId: String, @Path("filterId") + filterId: String): Call } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterModule.kt new file mode 100644 index 00000000..a43e19bd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterModule.kt @@ -0,0 +1,51 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.filter + +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.sync.FilterService +import im.vector.matrix.android.internal.session.SessionScope +import retrofit2.Retrofit + +@Module +internal abstract class FilterModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesFilterApi(retrofit: Retrofit): FilterApi { + return retrofit.create(FilterApi::class.java) + } + } + + @Binds + abstract fun bindFilterRepository(filterRepository: DefaultFilterRepository): FilterRepository + + @Binds + abstract fun bindFilterService(filterService: DefaultFilterService): FilterService + + @Binds + abstract fun bindSaveFilterTask(saveFilterTask_Factory: DefaultSaveFilterTask): SaveFilterTask + + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index 3d547c53..ed8c11bc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.session.group.model.GroupUsers import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.kotlin.createObject +import javax.inject.Inject internal interface GetGroupDataTask : Task { @@ -37,8 +38,7 @@ internal interface GetGroupDataTask : Task { } - -internal class DefaultGetGroupDataTask( +internal class DefaultGetGroupDataTask @Inject constructor( private val groupAPI: GroupAPI, private val monarchy: Monarchy ) : GetGroupDataTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt index 5c0f88de..ed0552da 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt @@ -25,8 +25,10 @@ import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope +import javax.inject.Inject -internal class DefaultGroupService(private val monarchy: Monarchy) : GroupService { +internal class DefaultGroupService @Inject constructor(private val monarchy: Monarchy) : GroupService { override fun getGroup(groupId: String): Group? { return null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index 34960493..4b3d6453 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -18,29 +18,34 @@ package im.vector.matrix.android.internal.session.group import android.content.Context import androidx.work.CoroutineWorker -import androidx.work.Worker import androidx.work.WorkerParameters import arrow.core.Try +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject import com.squareup.moshi.JsonClass -import im.vector.matrix.android.internal.di.MatrixKoinComponent -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject +import im.vector.matrix.android.internal.worker.DelegateWorkerFactory +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent +import javax.inject.Inject -internal class GetGroupDataWorker(context: Context, - workerParameters: WorkerParameters -) : CoroutineWorker(context, workerParameters), MatrixKoinComponent { +internal class GetGroupDataWorker (context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val groupIds: List - ) + ): SessionWorkerParams - private val getGroupDataTask by inject() + @Inject lateinit var getGroupDataTask: GetGroupDataTask override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val results = params.groupIds.map { groupId -> fetchGroupData(groupId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt index fe63ef90..cdb698b0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt @@ -16,22 +16,29 @@ package im.vector.matrix.android.internal.session.group -import im.vector.matrix.android.internal.session.DefaultSession -import org.koin.dsl.module.module +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.group.GroupService +import im.vector.matrix.android.internal.session.SessionScope import retrofit2.Retrofit -class GroupModule { +@Module +internal abstract class GroupModule { - val definition = module(override = true) { - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(GroupAPI::class.java) + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesGroupAPI(retrofit: Retrofit): GroupAPI { + return retrofit.create(GroupAPI::class.java) } - - scope(DefaultSession.SCOPE) { - DefaultGetGroupDataTask(get(), get()) as GetGroupDataTask - } - } + + @Binds + abstract fun bindGetGroupDataTask(getGroupDataTask: DefaultGetGroupDataTask): GetGroupDataTask + + @Binds + abstract fun bindGroupService(groupService: DefaultGroupService): GroupService } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt index 826c53fc..43bad2bc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt @@ -16,21 +16,25 @@ package im.vector.matrix.android.internal.session.group +import android.content.Context import androidx.work.Constraints import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.util.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import javax.inject.Inject private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" -internal class GroupSummaryUpdater(monarchy: Monarchy -) : RealmLiveEntityObserver(monarchy) { +internal class GroupSummaryUpdater @Inject constructor(private val context: Context, + private val credentials: Credentials, + monarchy: Monarchy) : RealmLiveEntityObserver(monarchy) { override val query = Monarchy.Query { GroupEntity.where(it) } @@ -40,7 +44,7 @@ internal class GroupSummaryUpdater(monarchy: Monarchy override fun processChanges(inserted: List, updated: List, deleted: List) { val newGroupIds = inserted.map { it.groupId } - val getGroupDataWorkerParams = GetGroupDataWorker.Params(newGroupIds) + val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, newGroupIds) val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams) val sendWork = OneTimeWorkRequestBuilder() @@ -48,7 +52,7 @@ internal class GroupSummaryUpdater(monarchy: Monarchy .setConstraints(workConstraints) .build() - WorkManager.getInstance() + WorkManager.getInstance(context) .beginUniqueWork(GET_GROUP_DATA_WORKER, ExistingWorkPolicy.APPEND, sendWork) .enqueue() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/BingRuleWatcher.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/BingRuleWatcher.kt index cf99c205..e07af970 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/BingRuleWatcher.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/BingRuleWatcher.kt @@ -24,13 +24,14 @@ import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.types import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject -internal class BingRuleWatcher(monarchy: Monarchy, - private val task: ProcessEventForPushTask, - private val defaultPushRuleService: DefaultPushRuleService, - private val sessionParams: SessionParams, - private val taskExecutor: TaskExecutor) : +internal class BingRuleWatcher @Inject constructor(monarchy: Monarchy, + private val task: ProcessEventForPushTask, + private val defaultPushRuleService: DefaultPushRuleService, + private val sessionParams: SessionParams, + private val taskExecutor: TaskExecutor) : RealmLiveEntityObserver(monarchy) { override val query = Monarchy.Query { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt index c85f0b44..e8ef2f7d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt @@ -27,14 +27,16 @@ import im.vector.matrix.android.internal.database.mapper.PushRulesMapper import im.vector.matrix.android.internal.database.model.PushRulesEntity import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask import im.vector.matrix.android.internal.session.pushers.UpdatePushRuleEnableStatusTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber +import javax.inject.Inject - -internal class DefaultPushRuleService( +@SessionScope +internal class DefaultPushRuleService @Inject constructor( private val sessionParams: SessionParams, private val pushRulesTask: GetPushRulesTask, private val updatePushRuleEnableStatusTask: UpdatePushRuleEnableStatusTask, @@ -42,10 +44,8 @@ internal class DefaultPushRuleService( private val monarchy: Monarchy ) : PushRuleService { - private var listeners = ArrayList() - override fun fetchPushRules(scope: String) { pushRulesTask .configureWith(Unit) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt index 7db4daa3..d6d09c86 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt @@ -1,11 +1,14 @@ package im.vector.matrix.android.internal.session.notification import arrow.core.Try +import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.session.pushers.DefaultConditionResolver import im.vector.matrix.android.internal.task.Task import timber.log.Timber +import javax.inject.Inject internal interface ProcessEventForPushTask : Task { data class Params( @@ -14,8 +17,10 @@ internal interface ProcessEventForPushTask : Task): PushRule? { - val conditionResolver = DefaultConditionResolver(event) - rules.filter { it.enabled } - .forEach { rule -> - val isFullfilled = rule.conditions?.map { - it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false - }?.fold(true/*A rule with no conditions always matches*/, { acc, next -> - //All conditions must hold true for an event in order to apply the action for the event. - acc && next - }) ?: false + val conditionResolver = DefaultConditionResolver(event, roomService, sessionParams) + rules.filter { it.enabled }.forEach { rule -> + val isFullfilled = rule.conditions?.map { + it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false + }?.fold(true/*A rule with no conditions always matches*/, { acc, next -> + //All conditions must hold true for an event in order to apply the action for the event. + acc && next + }) ?: false - if (isFullfilled) { - return rule - } - } + if (isFullfilled) { + return rule + } + } return null } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt index 1a3577ee..6ac59607 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt @@ -25,14 +25,13 @@ import im.vector.matrix.android.api.session.pushers.PusherState import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject - -class AddHttpPusherWorker(context: Context, params: WorkerParameters) - : CoroutineWorker(context, params), MatrixKoinComponent { +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent +import javax.inject.Inject +internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) + : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( @@ -40,14 +39,17 @@ class AddHttpPusherWorker(context: Context, params: WorkerParameters) val userId: String ) - private val pushersAPI by inject() - private val monarchy by inject() + @Inject lateinit var pushersAPI: PushersAPI + @Inject lateinit var monarchy: Monarchy override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val pusher = params.pusher if (pusher.pushKey.isBlank()) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt index e85f4347..a05cfb6c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultConditionResolver.kt @@ -19,15 +19,12 @@ import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.* import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.RoomService -import im.vector.matrix.android.internal.di.MatrixKoinComponent -import org.koin.standalone.inject import timber.log.Timber -internal class DefaultConditionResolver(val event: Event) : ConditionResolver, MatrixKoinComponent { +internal class DefaultConditionResolver(private val event: Event, + private val roomService: RoomService, + private val sessionParams: SessionParams) : ConditionResolver { - private val roomService by inject() - - private val sessionParams by inject() override fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean { return eventMatchCondition.isSatisfied(event) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt index 2444f7b0..103bdb71 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt @@ -15,8 +15,13 @@ */ package im.vector.matrix.android.internal.session.pushers +import android.content.Context import androidx.lifecycle.LiveData -import androidx.work.* +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.SessionParams @@ -30,12 +35,14 @@ import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.util.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.WorkerParamsFactory import java.util.* import java.util.concurrent.TimeUnit +import javax.inject.Inject -internal class DefaultPusherService( +internal class DefaultPusherService @Inject constructor( + private val context: Context, private val monarchy: Monarchy, private val sessionParam: SessionParams, private val getPusherTask: GetPushersTask, @@ -92,7 +99,7 @@ internal class DefaultPusherService( .setInputData(WorkerParamsFactory.toData(params)) .setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS) .build() - WorkManager.getInstance().enqueue(request) + WorkManager.getInstance(context).enqueue(request) return request.id } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt index b2c04842..46af3b63 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt @@ -19,11 +19,12 @@ import arrow.core.Try import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetPushRulesTask : Task -internal class DefaultGetPushRulesTask(private val pushRulesApi: PushRulesApi) : GetPushRulesTask { +internal class DefaultGetPushRulesTask @Inject constructor(private val pushRulesApi: PushRulesApi) : GetPushRulesTask { override suspend fun execute(params: Unit): Try { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt index 8587ab85..05b36fb5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt @@ -18,10 +18,11 @@ package im.vector.matrix.android.internal.session.pushers import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetPushersTask : Task -internal class DefaultGetPusherTask(private val pushersAPI: PushersAPI) : GetPushersTask { +internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI) : GetPushersTask { override suspend fun execute(params: Unit): Try { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushRulesApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushRulesApi.kt index eed08622..f191b21d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushRulesApi.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushRulesApi.kt @@ -15,8 +15,8 @@ */ package im.vector.matrix.android.internal.session.pushers -import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse +import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.internal.network.NetworkConstants import retrofit2.Call import retrofit2.http.* diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushersModule.kt new file mode 100644 index 00000000..85276252 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/PushersModule.kt @@ -0,0 +1,76 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.pushers + +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.pushrules.ConditionResolver +import im.vector.matrix.android.api.pushrules.PushRuleService +import im.vector.matrix.android.api.session.pushers.PushersService +import im.vector.matrix.android.internal.session.notification.DefaultProcessEventForPushTask +import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService +import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask +import retrofit2.Retrofit + +@Module +internal abstract class PushersModule { + + @Module + companion object { + + @JvmStatic + @Provides + fun providesPushersAPI(retrofit: Retrofit): PushersAPI { + return retrofit.create(PushersAPI::class.java) + } + + @JvmStatic + @Provides + fun providesPushRulesApi(retrofit: Retrofit): PushRulesApi { + return retrofit.create(PushRulesApi::class.java) + } + + } + + @Binds + abstract fun bindPusherService(pusherService: DefaultPusherService): PushersService + + @Binds + abstract fun bindConditionResolver(conditionResolver: DefaultConditionResolver): ConditionResolver + + @Binds + abstract fun bindGetPushersTask(getPusherTask: DefaultGetPusherTask): GetPushersTask + + @Binds + abstract fun bindGetPushRulesTask(getPushRulesTask: DefaultGetPushRulesTask): GetPushRulesTask + + @Binds + abstract fun bindRemovePusherTask(removePusherTask: DefaultRemovePusherTask): RemovePusherTask + + @Binds + abstract fun bindUpdatePushRuleEnableStatusTask(updatePushRuleEnableStatusTask: DefaultUpdatePushRuleEnableStatusTask): UpdatePushRuleEnableStatusTask + + @Binds + abstract fun bindPushRuleService(pushRuleService: DefaultPushRuleService): PushRuleService + + @Binds + abstract fun bindProcessEventForPushTask(processEventForPushTask: DefaultProcessEventForPushTask): ProcessEventForPushTask + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt index 3548afb5..8f32930c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt @@ -10,6 +10,7 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionSync +import javax.inject.Inject internal interface RemovePusherTask : Task { data class Params(val userId: String, @@ -17,7 +18,7 @@ internal interface RemovePusherTask : Task { val pushAppId: String) } -internal class DefaultRemovePusherTask( +internal class DefaultRemovePusherTask @Inject constructor( private val pushersAPI: PushersAPI, private val monarchy: Monarchy ) : RemovePusherTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt index 2deedcc9..9af67a6b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt @@ -19,6 +19,7 @@ import arrow.core.Try import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface UpdatePushRuleEnableStatusTask : Task { @@ -27,7 +28,8 @@ internal interface UpdatePushRuleEnableStatusTask : Task { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index 3715e370..3e2958d5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -34,17 +34,17 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.fetchCopied +import javax.inject.Inject -internal class DefaultRoom( - override val roomId: String, - private val monarchy: Monarchy, - private val timelineService: TimelineService, - private val sendService: SendService, - private val stateService: StateService, - private val readService: ReadService, - private val cryptoService: CryptoService, - private val relationService: RelationService, - private val roomMembersService: MembershipService +internal class DefaultRoom @Inject constructor(override val roomId: String, + private val monarchy: Monarchy, + private val timelineService: TimelineService, + private val sendService: SendService, + private val stateService: StateService, + private val readService: ReadService, + private val cryptoService: CryptoService, + private val relationService: RelationService, + private val roomMembersService: MembershipService ) : Room, TimelineService by timelineService, SendService by sendService, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt index 1a434dd6..764d4816 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt @@ -27,11 +27,12 @@ import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyPro import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject -internal class DefaultRoomDirectoryService(private val getPublicRoomTask: GetPublicRoomTask, - private val joinRoomTask: JoinRoomTask, - private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, - private val taskExecutor: TaskExecutor) : RoomDirectoryService { +internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask, + private val joinRoomTask: JoinRoomTask, + private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, + private val taskExecutor: TaskExecutor) : RoomDirectoryService { override fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index 18de69c4..cd729c93 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -28,15 +28,17 @@ import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.fetchManaged +import javax.inject.Inject -internal class DefaultRoomService(private val monarchy: Monarchy, - private val createRoomTask: CreateRoomTask, - private val roomFactory: RoomFactory, - private val taskExecutor: TaskExecutor) : RoomService { +internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy, + private val createRoomTask: CreateRoomTask, + private val roomFactory: RoomFactory, + private val taskExecutor: TaskExecutor) : RoomService { override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback) { createRoomTask @@ -47,7 +49,7 @@ internal class DefaultRoomService(private val monarchy: Monarchy, override fun getRoom(roomId: String): Room? { monarchy.fetchManaged { RoomEntity.where(it, roomId).findFirst() } ?: return null - return roomFactory.instantiate(roomId) + return roomFactory.create(roomId) } override fun liveRoomSummaries(): LiveData> { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt index e52f5e09..147726b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt @@ -20,12 +20,15 @@ import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import io.realm.Realm +import javax.inject.Inject /** * Fetches annotations (reactions, edits...) associated to a given eventEntity from the data layer. */ -internal class EventRelationExtractor { + +internal class EventRelationExtractor @Inject constructor() { fun extractFrom(event: EventEntity, realm: Realm = event.realm): EventAnnotationsSummary? { return EventAnnotationsSummaryEntity.where(realm, event.eventId).findFirst()?.asDomain() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt index 0067434d..401cb096 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt @@ -26,10 +26,12 @@ import im.vector.matrix.android.internal.database.mapper.EventMapper import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.Realm import timber.log.Timber +import javax.inject.Inject internal interface EventRelationsAggregationTask : Task { @@ -42,7 +44,7 @@ internal interface EventRelationsAggregationTask : Task(monarchy) { override val query = Monarchy.Query { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt index c26f0596..3ed2fa43 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt @@ -27,10 +27,12 @@ import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import javax.inject.Inject -internal class RoomAvatarResolver(private val monarchy: Monarchy, - private val credentials: Credentials) { +internal class RoomAvatarResolver @Inject constructor(private val monarchy: Monarchy, + private val credentials: Credentials) { /** * Compute the room avatar url diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index 42f2299e..cd7be990 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -16,8 +16,9 @@ package im.vector.matrix.android.internal.session.room +import android.content.Context import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService @@ -30,6 +31,7 @@ import im.vector.matrix.android.internal.session.room.read.DefaultReadService import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask +import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.state.DefaultStateService @@ -39,32 +41,35 @@ import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEvent import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory import im.vector.matrix.android.internal.task.TaskExecutor +import javax.inject.Inject -internal class RoomFactory(private val monarchy: Monarchy, - private val eventFactory: LocalEchoEventFactory, - private val taskExecutor: TaskExecutor, - private val loadRoomMembersTask: LoadRoomMembersTask, - private val inviteTask: InviteTask, - private val sendStateTask: SendStateTask, - private val paginationTask: PaginationTask, - private val contextOfEventTask: GetContextOfEventTask, - private val setReadMarkersTask: SetReadMarkersTask, - private val cryptoService: CryptoService, - private val findReactionEventForUndoTask: FindReactionEventForUndoTask, - private val joinRoomTask: JoinRoomTask, - private val leaveRoomTask: LeaveRoomTask, - private val sessionParams: SessionParams) { +internal class RoomFactory @Inject constructor(private val context: Context, + private val credentials: Credentials, + private val monarchy: Monarchy, + private val eventFactory: LocalEchoEventFactory, + private val taskExecutor: TaskExecutor, + private val loadRoomMembersTask: LoadRoomMembersTask, + private val inviteTask: InviteTask, + private val sendStateTask: SendStateTask, + private val paginationTask: PaginationTask, + private val contextOfEventTask: GetContextOfEventTask, + private val setReadMarkersTask: SetReadMarkersTask, + private val cryptoService: CryptoService, + private val findReactionEventForUndoTask: FindReactionEventForUndoTask, + private val updateQuickReactionTask: UpdateQuickReactionTask, + private val joinRoomTask: JoinRoomTask, + private val leaveRoomTask: LeaveRoomTask) { - fun instantiate(roomId: String): Room { + fun create(roomId: String): Room { val roomMemberExtractor = SenderRoomMemberExtractor(roomId) val relationExtractor = EventRelationExtractor() val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, relationExtractor, cryptoService) val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, paginationTask) - val sendService = DefaultSendService(roomId, eventFactory, cryptoService, monarchy) + val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy) val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask) val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask) - val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, sessionParams) - val reactionService = DefaultRelationService(roomId, eventFactory, findReactionEventForUndoTask, monarchy, taskExecutor) + val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials) + val relationService = DefaultRelationService(context, credentials, roomId, eventFactory, findReactionEventForUndoTask, monarchy, taskExecutor) return DefaultRoom( roomId, @@ -74,7 +79,7 @@ internal class RoomFactory(private val monarchy: Monarchy, stateService, readService, cryptoService, - reactionService, + relationService, roomMembersService ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 1cefe088..9cf6ac69 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -16,10 +16,26 @@ package im.vector.matrix.android.internal.session.room -import im.vector.matrix.android.internal.session.DefaultSession +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.room.RoomDirectoryService +import im.vector.matrix.android.api.session.room.RoomService +import im.vector.matrix.android.api.session.room.members.MembershipService +import im.vector.matrix.android.api.session.room.model.relation.RelationService +import im.vector.matrix.android.api.session.room.read.ReadService +import im.vector.matrix.android.api.session.room.send.SendService +import im.vector.matrix.android.api.session.room.state.StateService +import im.vector.matrix.android.api.session.room.timeline.TimelineService +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask +import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask +import im.vector.matrix.android.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask +import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask +import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyProtocolsTask import im.vector.matrix.android.internal.session.room.membership.DefaultLoadRoomMembersTask +import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.membership.joining.DefaultInviteTask import im.vector.matrix.android.internal.session.room.membership.joining.DefaultJoinRoomTask @@ -29,91 +45,97 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.Default import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask import im.vector.matrix.android.internal.session.room.prune.DefaultPruneEventTask import im.vector.matrix.android.internal.session.room.prune.PruneEventTask +import im.vector.matrix.android.internal.session.room.read.DefaultReadService import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask -import im.vector.matrix.android.internal.session.room.relation.DefaultFindReactionEventForUndoTask -import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask -import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory -import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater +import im.vector.matrix.android.internal.session.room.relation.* +import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask +import im.vector.matrix.android.internal.session.room.state.DefaultStateService import im.vector.matrix.android.internal.session.room.state.SendStateTask import im.vector.matrix.android.internal.session.room.timeline.* -import org.koin.dsl.module.module import retrofit2.Retrofit +@Module +internal abstract class RoomModule { -class RoomModule { - - val definition = module(override = true) { - - scope(DefaultSession.SCOPE) { - val retrofit: Retrofit = get() - retrofit.create(RoomAPI::class.java) + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesRoomAPI(retrofit: Retrofit): RoomAPI { + return retrofit.create(RoomAPI::class.java) } - - scope(DefaultSession.SCOPE) { - DefaultLoadRoomMembersTask(get(), get(), get(), get()) as LoadRoomMembersTask - } - - scope(DefaultSession.SCOPE) { - TokenChunkEventPersistor(get()) - } - - scope(DefaultSession.SCOPE) { - DefaultPaginationTask(get(), get(), get()) as PaginationTask - } - - scope(DefaultSession.SCOPE) { - DefaultGetContextOfEventTask(get(), get(), get()) as GetContextOfEventTask - } - - scope(DefaultSession.SCOPE) { - DefaultSetReadMarkersTask(get(), get(), get()) as SetReadMarkersTask - } - - scope(DefaultSession.SCOPE) { - LocalEchoEventFactory(get(), get()) - } - - scope(DefaultSession.SCOPE) { - LocalEchoUpdater(get()) - } - - scope(DefaultSession.SCOPE) { - RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) - } - - scope(DefaultSession.SCOPE) { - DefaultCreateRoomTask(get(), get("SessionRealmConfiguration")) as CreateRoomTask - } - - scope(DefaultSession.SCOPE) { - DefaultInviteTask(get()) as InviteTask - } - - scope(DefaultSession.SCOPE) { - DefaultJoinRoomTask(get()) as JoinRoomTask - } - - scope(DefaultSession.SCOPE) { - DefaultLeaveRoomTask(get()) as LeaveRoomTask - } - - scope(DefaultSession.SCOPE) { - DefaultSendStateTask(get()) as SendStateTask - } - - scope(DefaultSession.SCOPE) { - DefaultFindReactionEventForUndoTask(get()) as FindReactionEventForUndoTask - } - - scope(DefaultSession.SCOPE) { - DefaultPruneEventTask(get()) as PruneEventTask - } - - scope(DefaultSession.SCOPE) { - DefaultEventRelationsAggregationTask(get()) as EventRelationsAggregationTask - } - } + + @Binds + abstract fun bindRoomService(roomService: DefaultRoomService): RoomService + + @Binds + abstract fun bindRoomDirectoryService(roomDirectoryService: DefaultRoomDirectoryService): RoomDirectoryService + + @Binds + abstract fun bindEventRelationsAggregationTask(eventRelationsAggregationTask: DefaultEventRelationsAggregationTask): EventRelationsAggregationTask + + @Binds + abstract fun bindCreateRoomTask(createRoomTask: DefaultCreateRoomTask): CreateRoomTask + + @Binds + abstract fun bindGetPublicRoomTask(getPublicRoomTask: DefaultGetPublicRoomTask): GetPublicRoomTask + + @Binds + abstract fun bindGetThirdPartyProtocolsTask(getThirdPartyProtocolsTask: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask + + @Binds + abstract fun bindInviteTask(inviteTask: DefaultInviteTask): InviteTask + + @Binds + abstract fun bindJoinRoomTask(joinRoomTask: DefaultJoinRoomTask): JoinRoomTask + + @Binds + abstract fun bindLeaveRoomTask(leaveRoomTask: DefaultLeaveRoomTask): LeaveRoomTask + + @Binds + abstract fun bindMembershipService(membershipService: DefaultMembershipService): MembershipService + + @Binds + abstract fun bindLoadRoomMembersTask(loadRoomMembersTask: DefaultLoadRoomMembersTask): LoadRoomMembersTask + + @Binds + abstract fun bindPruneEventTask(pruneEventTask: DefaultPruneEventTask): PruneEventTask + + @Binds + abstract fun bindReadService(readService: DefaultReadService): ReadService + + @Binds + abstract fun bindSetReadMarkersTask(setReadMarkersTask: DefaultSetReadMarkersTask): SetReadMarkersTask + + @Binds + abstract fun bindRelationService(relationService: DefaultRelationService): RelationService + + @Binds + abstract fun bindFindReactionEventForUndoTask(findReactionEventForUndoTask: DefaultFindReactionEventForUndoTask): FindReactionEventForUndoTask + + @Binds + abstract fun bindUpdateQuickReactionTask(updateQuickReactionTask: DefaultUpdateQuickReactionTask): UpdateQuickReactionTask + + @Binds + abstract fun bindSendStateTask(sendStateTask: DefaultSendStateTask): SendStateTask + + @Binds + abstract fun bindSendService(sendService: DefaultSendService): SendService + + @Binds + abstract fun bindStateService(stateService: DefaultStateService): StateService + + @Binds + abstract fun bindGetContextOfEventTask(getContextOfEventTask: DefaultGetContextOfEventTask): GetContextOfEventTask + + @Binds + abstract fun bindPaginationTask(paginationTask: DefaultPaginationTask): PaginationTask + + @Binds + abstract fun bindTimelineService(timelineService: DefaultTimelineService): TimelineService + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 7bf08b70..54de9467 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -29,16 +29,18 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications import io.realm.Realm import io.realm.kotlin.createObject +import javax.inject.Inject -internal class RoomSummaryUpdater(private val credentials: Credentials, - private val roomDisplayNameResolver: RoomDisplayNameResolver, - private val roomAvatarResolver: RoomAvatarResolver) { +internal class RoomSummaryUpdater @Inject constructor(private val credentials: Credentials, + private val roomDisplayNameResolver: RoomDisplayNameResolver, + private val roomAvatarResolver: RoomAvatarResolver) { fun update(realm: Realm, roomId: String, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index 9ff5f511..73d9b6f2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -20,18 +20,20 @@ import arrow.core.Try import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse import im.vector.matrix.android.internal.database.RealmQueryLatch +import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntityFields import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import io.realm.RealmConfiguration +import javax.inject.Inject internal interface CreateRoomTask : Task - -internal class DefaultCreateRoomTask(private val roomAPI: RoomAPI, - private val realmConfiguration: RealmConfiguration) : CreateRoomTask { +internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: RoomAPI, + @SessionDatabase private val realmConfiguration: RealmConfiguration) : CreateRoomTask { override suspend fun execute(params: CreateRoomParams): Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt index 4b71b871..b2cc1a74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt @@ -20,8 +20,10 @@ import arrow.core.Try import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetPublicRoomTask : Task { data class Params( @@ -30,7 +32,7 @@ internal interface GetPublicRoomTask : Task { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt index a50ec367..cd53c92d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt @@ -19,12 +19,14 @@ package im.vector.matrix.android.internal.session.room.directory import arrow.core.Try import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetThirdPartyProtocolsTask : Task> -internal class DefaultGetThirdPartyProtocolsTask(private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { +internal class DefaultGetThirdPartyProtocolsTask @Inject constructor (private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { override suspend fun execute(params: Unit): Try> { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index a3c2fc13..06835374 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -33,14 +33,15 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRo import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.fetchCopied +import javax.inject.Inject -internal class DefaultMembershipService(private val roomId: String, - private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor, - private val loadRoomMembersTask: LoadRoomMembersTask, - private val inviteTask: InviteTask, - private val joinTask: JoinRoomTask, - private val leaveRoomTask: LeaveRoomTask +internal class DefaultMembershipService @Inject constructor(private val roomId: String, + private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, + private val loadRoomMembersTask: LoadRoomMembersTask, + private val inviteTask: InviteTask, + private val joinTask: JoinRoomTask, + private val leaveRoomTask: LeaveRoomTask ) : MembershipService { override fun loadRoomMembersIfNeeded(): Cancelable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index 44a050db..4dbb60b8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -23,12 +23,14 @@ import im.vector.matrix.android.internal.database.helper.addStateEvents import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.kotlin.createObject +import javax.inject.Inject internal interface LoadRoomMembersTask : Task { @@ -38,7 +40,7 @@ internal interface LoadRoomMembersTask : Task): String? { val currentMember = members[userId] diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt index c8944bac..209e5f38 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt @@ -31,8 +31,10 @@ import im.vector.matrix.android.internal.database.query.where import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery +import timber.log.Timber +import javax.inject.Inject -internal class SenderRoomMemberExtractor(private val roomId: String) { +internal class SenderRoomMemberExtractor @Inject constructor(private val roomId: String) { fun extractFrom(event: EventEntity, realm: Realm = event.realm): RoomMember? { val sender = event.sender ?: return null @@ -45,9 +47,8 @@ internal class SenderRoomMemberExtractor(private val roomId: String) { event.stateIndex <= 0 -> baseQuery(chunkEntity.events, sender, unlinked).next(from = event.stateIndex)?.prevContent else -> baseQuery(chunkEntity.events, sender, unlinked).prev(since = event.stateIndex)?.content } - val fallbackContent = content - ?: baseQuery(roomEntity.untimelinedStateEvents, sender, unlinked).prev(since = event.stateIndex)?.content + ?: baseQuery(roomEntity.untimelinedStateEvents, sender, unlinked).prev(since = event.stateIndex)?.content return ContentMapper.map(fallbackContent).toModel() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt index 3610cbe0..5f656c01 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt @@ -18,8 +18,10 @@ package im.vector.matrix.android.internal.session.room.membership.joining import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface InviteTask : Task { @@ -29,7 +31,7 @@ internal interface InviteTask : Task { ) } -internal class DefaultInviteTask(private val roomAPI: RoomAPI) : InviteTask { +internal class DefaultInviteTask @Inject constructor(private val roomAPI: RoomAPI) : InviteTask { override suspend fun execute(params: InviteTask.Params): Try { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt index 3fe2d139..96454cbf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt @@ -18,8 +18,10 @@ package im.vector.matrix.android.internal.session.room.membership.joining import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface JoinRoomTask : Task { data class Params( @@ -27,7 +29,7 @@ internal interface JoinRoomTask : Task { ) } -internal class DefaultJoinRoomTask(private val roomAPI: RoomAPI) : JoinRoomTask { +internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: RoomAPI) : JoinRoomTask { override suspend fun execute(params: JoinRoomTask.Params): Try { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt index 9eb1728f..fe055e99 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt @@ -18,8 +18,10 @@ package im.vector.matrix.android.internal.session.room.membership.leaving import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface LeaveRoomTask : Task { data class Params( @@ -27,7 +29,7 @@ internal interface LeaveRoomTask : Task { ) } -internal class DefaultLeaveRoomTask(private val roomAPI: RoomAPI) : LeaveRoomTask { +internal class DefaultLeaveRoomTask @Inject constructor(private val roomAPI: RoomAPI) : LeaveRoomTask { override suspend fun execute(params: LeaveRoomTask.Params): Try { return executeRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt index 4d3bc6fd..456cbde7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt @@ -23,26 +23,27 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber +import javax.inject.Inject /** * Listens to the database for the insertion of any redaction event. * As it will actually delete the content, it should be called last in the list of listener. */ -internal class EventsPruner(monarchy: Monarchy, - private val credentials: Credentials, - private val pruneEventTask: PruneEventTask, - private val taskExecutor: TaskExecutor) : +internal class EventsPruner @Inject constructor(monarchy: Monarchy, + private val credentials: Credentials, + private val pruneEventTask: PruneEventTask, + private val taskExecutor: TaskExecutor) : RealmLiveEntityObserver(monarchy) { override val query = Monarchy.Query { EventEntity.where(it, type = EventType.REDACTION) } override fun processChanges(inserted: List, updated: List, deleted: List) { Timber.v("Event pruner called with ${inserted.size} insertions") - val redactionEvents = inserted - .mapNotNull { it.asDomain() } + val redactionEvents = inserted.map { it.asDomain() } val params = PruneEventTask.Params( redactionEvents, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt index ef8557ae..56b40f2d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt @@ -26,10 +26,12 @@ import im.vector.matrix.android.internal.database.mapper.EventMapper import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.Realm import timber.log.Timber +import javax.inject.Inject internal interface PruneEventTask : Task { @@ -41,8 +43,7 @@ internal interface PruneEventTask : Task { } -internal class DefaultPruneEventTask( - private val monarchy: Monarchy) : PruneEventTask { +internal class DefaultPruneEventTask @Inject constructor(private val monarchy: Monarchy) : PruneEventTask { override suspend fun execute(params: PruneEventTask.Params): Try { return monarchy.tryTransactionSync { realm -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt index 46e08482..3edfed82 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.room.read import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.room.read.ReadService import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.EventEntity @@ -30,12 +30,13 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.fetchCopied +import javax.inject.Inject -internal class DefaultReadService(private val roomId: String, - private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor, - private val setReadMarkersTask: SetReadMarkersTask, - private val sessionParams: SessionParams) : ReadService { +internal class DefaultReadService @Inject constructor(private val roomId: String, + private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, + private val setReadMarkersTask: SetReadMarkersTask, + private val credentials: Credentials) : ReadService { override fun markAllAsRead(callback: MatrixCallback) { val latestEvent = getLatestEvent() @@ -60,7 +61,7 @@ internal class DefaultReadService(private val roomId: String, override fun isEventRead(eventId: String): Boolean { var isEventRead = false monarchy.doWithRealm { - val readReceipt = ReadReceiptEntity.where(it, roomId, sessionParams.credentials.userId).findFirst() + val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst() ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) ?: return@doWithRealm diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt index 0592077e..2146fdf0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt @@ -29,9 +29,11 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.tryTransactionAsync +import javax.inject.Inject internal interface SetReadMarkersTask : Task { @@ -45,7 +47,7 @@ internal interface SetReadMarkersTask : Task { private const val READ_MARKER = "m.fully_read" private const val READ_RECEIPT = "m.read" -internal class DefaultSetReadMarkersTask(private val roomAPI: RoomAPI, +internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI: RoomAPI, private val credentials: Credentials, private val monarchy: Monarchy ) : SetReadMarkersTask { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index f2527d49..9b07bb28 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -15,10 +15,12 @@ */ package im.vector.matrix.android.internal.session.room.relation +import android.content.Context import androidx.lifecycle.LiveData import androidx.work.OneTimeWorkRequest import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.message.MessageType @@ -38,16 +40,18 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEvent import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.WorkerParamsFactory import im.vector.matrix.android.internal.util.tryTransactionAsync +import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber +import javax.inject.Inject - -internal class DefaultRelationService(private val roomId: String, - private val eventFactory: LocalEchoEventFactory, - private val findReactionEventForUndoTask: FindReactionEventForUndoTask, - private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor) +internal class DefaultRelationService @Inject constructor(private val context: Context, + private val credentials: Credentials, + private val roomId: String, + private val eventFactory: LocalEchoEventFactory, + private val findReactionEventForUndoTask: FindReactionEventForUndoTask, + private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor) : RelationService { @@ -57,14 +61,13 @@ internal class DefaultRelationService(private val roomId: String, saveLocalEcho(it) } val sendRelationWork = createSendRelationWork(event) - TimelineSendEventWorkCommon.postWork(roomId, sendRelationWork) - return CancelableWork(sendRelationWork.id) + TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork) + return CancelableWork(context, sendRelationWork.id) } private fun createSendRelationWork(event: Event): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params( - roomId, event) + val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return TimelineSendEventWorkCommon.createWork(sendWorkData) @@ -94,7 +97,7 @@ internal class DefaultRelationService(private val roomId: String, } val redactWork = createRedactEventWork(redactEvent, toRedact, null) - TimelineSendEventWorkCommon.postWork(roomId, redactWork) + TimelineSendEventWorkCommon.postWork(context, roomId, redactWork) } } @@ -103,16 +106,10 @@ internal class DefaultRelationService(private val roomId: String, } - - - private fun buildWorkIdentifier(identifier: String): String { - return "${roomId}_$identifier" - } - //TODO duplicate with send service? private fun createRedactEventWork(localEvent: Event, eventId: String, reason: String?): OneTimeWorkRequest { - val sendContentWorkerParams = RedactEventWorker.Params(localEvent.eventId!!, + val sendContentWorkerParams = RedactEventWorker.Params(credentials.userId, localEvent.eventId!!, roomId, eventId, reason) val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return TimelineSendEventWorkCommon.createWork(redactWorkData) @@ -122,14 +119,14 @@ internal class DefaultRelationService(private val roomId: String, val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, MessageType.MSGTYPE_TEXT, compatibilityBodyText).also { saveLocalEcho(it) } - val sendContentWorkerParams = SendEventWorker.Params(roomId, event) + val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) //TODO use relation API? val workRequest = TimelineSendEventWorkCommon.createWork(sendWorkData) - TimelineSendEventWorkCommon.postWork(roomId, workRequest) - return CancelableWork(workRequest.id) + TimelineSendEventWorkCommon.postWork(context, roomId, workRequest) + return CancelableWork(context, workRequest.id) } @@ -138,13 +135,13 @@ internal class DefaultRelationService(private val roomId: String, val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText)?.also { saveLocalEcho(it) } ?: return null - val sendContentWorkerParams = SendEventWorker.Params(roomId, event) + val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) val workRequest = TimelineSendEventWorkCommon.createWork(sendWorkData) - TimelineSendEventWorkCommon.postWork(roomId, workRequest) - return CancelableWork(workRequest.id) + TimelineSendEventWorkCommon.postWork(context, roomId, workRequest) + return CancelableWork(context, workRequest.id) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt index 07b48bd0..8841819e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt @@ -21,8 +21,10 @@ import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryE import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm +import javax.inject.Inject internal interface FindReactionEventForUndoTask : Task { @@ -40,7 +42,7 @@ internal interface FindReactionEventForUndoTask : Task { return Try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt index 824c85b8..16e5f52f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.session.room.relation import android.content.Context import androidx.work.CoroutineWorker -import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.failure.Failure @@ -25,30 +24,33 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.relation.ReactionContent import im.vector.matrix.android.api.session.room.model.relation.ReactionInfo -import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.send.SendResponse -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject - -class SendRelationWorker(context: Context, params: WorkerParameters) - : CoroutineWorker(context, params), MatrixKoinComponent { +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent +import javax.inject.Inject +internal class SendRelationWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val roomId: String, val event: Event, val relationType: String? = null - ) + ) : SessionWorkerParams - private val roomAPI by inject() + @Inject lateinit var roomAPI: RoomAPI override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val localEvent = params.event if (localEvent.eventId == null) { return Result.failure() @@ -71,7 +73,7 @@ class SendRelationWorker(context: Context, params: WorkerParameters) return result.fold({ when (it) { is Failure.NetworkConnection -> Result.retry() - else -> { + else -> { //TODO mark as failed to send? //always return success, or the chain will be stuck for ever! Result.success() @@ -79,4 +81,5 @@ class SendRelationWorker(context: Context, params: WorkerParameters) } }, { Result.success() }) } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt new file mode 100644 index 00000000..66e02ce9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.session.room.relation + +import arrow.core.Try +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity +import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields +import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.task.Task +import io.realm.Realm +import javax.inject.Inject + + +internal interface UpdateQuickReactionTask : Task { + + data class Params( + val roomId: String, + val eventId: String, + val reaction: String, + val oppositeReaction: String, + val myUserId: String + ) + + data class Result( + val reactionToAdd: String?, + val reactionToRedact: List + ) +} + +internal class DefaultUpdateQuickReactionTask @Inject constructor(private val monarchy: Monarchy) : UpdateQuickReactionTask { + override suspend fun execute(params: UpdateQuickReactionTask.Params): Try { + return Try { + var res: Pair?>? = null + monarchy.doWithRealm { realm -> + res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId) + } + UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList()) + } + } + + + private fun updateQuickReaction(realm: Realm, reaction: String, oppositeReaction: String, eventId: String, myUserId: String): Pair?> { + //the emoji reaction has been selected, we need to check if we have reacted it or not + val existingSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() + ?: return Pair(reaction, null) + + //Ok there is already reactions on this event, have we reacted to it + val aggregationForReaction = existingSummary.reactionsSummary.where() + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction) + .findFirst() + val aggregationForOppositeReaction = existingSummary.reactionsSummary.where() + .equalTo(ReactionAggregatedSummaryEntityFields.KEY, oppositeReaction) + .findFirst() + + if (aggregationForReaction == null || !aggregationForReaction.addedByMe) { + //i haven't yet reacted to it, so need to add it, but do I need to redact the opposite? + val toRedact = aggregationForOppositeReaction?.sourceEvents?.mapNotNull { + //find source event + val entity = EventEntity.where(realm, it).findFirst() + if (entity?.sender == myUserId) entity.eventId else null + } + return Pair(reaction, toRedact) + } else { + //I already added it, so i need to undo it (like a toggle) + // find all m.redaction coming from me to readact them + val toRedact = aggregationForReaction.sourceEvents.mapNotNull { + //find source event + val entity = EventEntity.where(realm, it).findFirst() + if (entity?.sender == myUserId) entity.eventId else null + } + return Pair(null, toRedact) + } + + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 62239a74..fb6f1be0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -16,8 +16,10 @@ package im.vector.matrix.android.internal.session.room.send +import android.content.Context import androidx.work.* import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.events.model.Event @@ -28,9 +30,10 @@ import im.vector.matrix.android.api.util.addTo import im.vector.matrix.android.internal.session.content.UploadContentWorker import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber import java.util.concurrent.TimeUnit +import javax.inject.Inject private const val UPLOAD_WORK = "UPLOAD_WORK" private const val BACKOFF_DELAY = 10_000L @@ -39,10 +42,12 @@ private val WORK_CONSTRAINTS = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() -internal class DefaultSendService(private val roomId: String, - private val localEchoEventFactory: LocalEchoEventFactory, - private val cryptoService: CryptoService, - private val monarchy: Monarchy) +internal class DefaultSendService @Inject constructor(private val context: Context, + private val credentials: Credentials, + private val roomId: String, + private val localEchoEventFactory: LocalEchoEventFactory, + private val cryptoService: CryptoService, + private val monarchy: Monarchy) : SendService { override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable { @@ -54,8 +59,8 @@ internal class DefaultSendService(private val roomId: String, Timber.v("Send event in encrypted room") val encryptWork = createEncryptEventWork(event) val sendWork = createSendEventWork(event) - TimelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork) - CancelableWork(encryptWork.id) + TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, sendWork) + CancelableWork(context, encryptWork.id) } else { sendEvent(event) } @@ -63,8 +68,8 @@ internal class DefaultSendService(private val roomId: String, private fun sendEvent(event: Event): Cancelable { val sendWork = createSendEventWork(event) - TimelineSendEventWorkCommon.postWork(roomId, sendWork) - return CancelableWork(sendWork.id) + TimelineSendEventWorkCommon.postWork(context, roomId, sendWork) + return CancelableWork(context, sendWork.id) } override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable { @@ -72,8 +77,8 @@ internal class DefaultSendService(private val roomId: String, saveLocalEcho(it) } val sendWork = createSendEventWork(event) - TimelineSendEventWorkCommon.postWork(roomId, sendWork) - return CancelableWork(sendWork.id) + TimelineSendEventWorkCommon.postWork(context, roomId, sendWork) + return CancelableWork(context, sendWork.id) } override fun sendMedias(attachments: List): Cancelable { @@ -87,8 +92,8 @@ internal class DefaultSendService(private val roomId: String, override fun redactEvent(event: Event, reason: String?): Cancelable { //TODO manage media/attachements? val redactWork = createRedactEventWork(event, reason) - TimelineSendEventWorkCommon.postWork(roomId, redactWork) - return CancelableWork(redactWork.id) + TimelineSendEventWorkCommon.postWork(context, roomId, redactWork) + return CancelableWork(context, redactWork.id) } override fun sendMedia(attachment: ContentAttachmentData): Cancelable { @@ -99,12 +104,12 @@ internal class DefaultSendService(private val roomId: String, val uploadWork = createUploadMediaWork(event, attachment) val sendWork = createSendEventWork(event) - WorkManager.getInstance() + WorkManager.getInstance(context) .beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) .then(sendWork) .enqueue() - return CancelableWork(sendWork.id) + return CancelableWork(context, sendWork.id) } private fun saveLocalEcho(event: Event) { @@ -117,7 +122,7 @@ internal class DefaultSendService(private val roomId: String, private fun createEncryptEventWork(event: Event): OneTimeWorkRequest { // Same parameter - val params = EncryptEventWorker.Params(roomId, event) + val params = EncryptEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(params) return OneTimeWorkRequestBuilder() @@ -128,7 +133,7 @@ internal class DefaultSendService(private val roomId: String, } private fun createSendEventWork(event: Event): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(roomId, event) + val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return TimelineSendEventWorkCommon.createWork(sendWorkData) @@ -138,13 +143,13 @@ internal class DefaultSendService(private val roomId: String, val redactEvent = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason).also { saveLocalEcho(it) } - val sendContentWorkerParams = RedactEventWorker.Params(redactEvent.eventId!!, roomId, event.eventId, reason) + val sendContentWorkerParams = RedactEventWorker.Params(credentials.userId, redactEvent.eventId!!, roomId, event.eventId, reason) val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return TimelineSendEventWorkCommon.createWork(redactWorkData) } private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData): OneTimeWorkRequest { - val uploadMediaWorkerParams = UploadContentWorker.Params(roomId, event, attachment) + val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) return OneTimeWorkRequestBuilder() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt index 8dcae0cc..359eb181 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt @@ -26,29 +26,33 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult -import im.vector.matrix.android.internal.di.MatrixKoinComponent -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent import java.util.concurrent.CountDownLatch +import javax.inject.Inject internal class EncryptEventWorker(context: Context, params: WorkerParameters) - : Worker(context, params), MatrixKoinComponent { - + : Worker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val roomId: String, val event: Event - ) + ) : SessionWorkerParams - private val crypto by inject() - private val localEchoUpdater by inject() + @Inject lateinit var crypto: CryptoService + @Inject lateinit var localEchoUpdater: LocalEchoUpdater override fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val localEvent = params.event if (localEvent.eventId == null) { return Result.success() @@ -85,7 +89,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) type = safeResult.eventType, content = safeResult.eventContent ) - val nextWorkerParams = SendEventWorker.Params(params.roomId, encryptedEvent) + val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, encryptedEvent) return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) } val safeError = error diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index c4d32190..dbdfe124 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -33,12 +33,14 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.content.ThumbnailExtractor import im.vector.matrix.android.internal.util.StringProvider import im.vector.matrix.android.internal.util.tryTransactionAsync import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import java.util.* +import javax.inject.Inject /** * Creates local echo of events for room events. @@ -49,7 +51,8 @@ import java.util.* * * The transactionID is used as loc */ -internal class LocalEchoEventFactory(private val credentials: Credentials, private val stringProvider: StringProvider) { + +internal class LocalEchoEventFactory @Inject constructor(private val credentials: Credentials, private val stringProvider: StringProvider) { fun createTextEvent(roomId: String, msgType: String, text: String, autoMarkdown: Boolean): Event { if (autoMarkdown && msgType == MessageType.MSGTYPE_TEXT) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt index 7dc69536..f4401ca0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt @@ -23,8 +23,9 @@ import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.tryTransactionAsync +import javax.inject.Inject -internal class LocalEchoUpdater(private val monarchy: Monarchy) { +internal class LocalEchoUpdater @Inject constructor(private val monarchy: Monarchy) { fun updateSendState(eventId: String, sendState: SendState) { monarchy.tryTransactionAsync { realm -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt index e51889f4..b3e2edcf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt @@ -17,33 +17,36 @@ package im.vector.matrix.android.internal.session.room.send import android.content.Context import androidx.work.CoroutineWorker -import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.failure.Failure -import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent +import javax.inject.Inject -internal class RedactEventWorker(context: Context, params: WorkerParameters) - : CoroutineWorker(context, params), MatrixKoinComponent { +internal class RedactEventWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val txID: String, val roomId: String, val eventId: String, val reason: String? - ) + ) : SessionWorkerParams - private val roomAPI by inject() + @Inject lateinit var roomAPI: RoomAPI override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val eventId = params.eventId val result = executeRequest { apiCall = roomAPI.redactEvent( @@ -56,7 +59,7 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) return result.fold({ when (it) { is Failure.NetworkConnection -> Result.retry() - else -> { + else -> { //TODO mark as failed to send? //always return success, or the chain will be stuck for ever! Result.success() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt index 3b2fd87b..6b6585ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt @@ -18,36 +18,40 @@ package im.vector.matrix.android.internal.session.room.send import android.content.Context import androidx.work.CoroutineWorker -import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.send.SendState -import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.util.WorkerParamsFactory -import org.koin.standalone.inject +import im.vector.matrix.android.internal.worker.SessionWorkerParams +import im.vector.matrix.android.internal.worker.WorkerParamsFactory +import im.vector.matrix.android.internal.worker.getSessionComponent +import javax.inject.Inject -internal class SendEventWorker(context: Context, params: WorkerParameters) - : CoroutineWorker(context, params), MatrixKoinComponent { +internal class SendEventWorker constructor(context: Context, params: WorkerParameters) + : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( + override val userId: String, val roomId: String, val event: Event - ) + ) : SessionWorkerParams - private val roomAPI by inject() - private val localEchoUpdater by inject() + @Inject lateinit var localEchoUpdater: LocalEchoUpdater + @Inject lateinit var roomAPI: RoomAPI override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() + val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() + sessionComponent.inject(this) + val event = params.event if (event.eventId == null) { return Result.success() @@ -73,4 +77,5 @@ internal class SendEventWorker(context: Context, params: WorkerParameters) } }, { Result.success() }) } + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index f1ad712e..053bf7f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -21,10 +21,11 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.state.StateService import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject -internal class DefaultStateService(private val roomId: String, - private val taskExecutor: TaskExecutor, - private val sendStateTask: SendStateTask) : StateService { +internal class DefaultStateService @Inject constructor(private val roomId: String, + private val taskExecutor: TaskExecutor, + private val sendStateTask: SendStateTask) : StateService { override fun updateTopic(topic: String, callback: MatrixCallback) { val params = SendStateTask.Params(roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt index b6d3a748..085340e6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt @@ -18,8 +18,10 @@ package im.vector.matrix.android.internal.session.room.state import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface SendStateTask : Task { data class Params( @@ -29,7 +31,7 @@ internal interface SendStateTask : Task { ) } -internal class DefaultSendStateTask(private val roomAPI: RoomAPI) : SendStateTask { +internal class DefaultSendStateTask @Inject constructor(private val roomAPI: RoomAPI) : SendStateTask { override suspend fun execute(params: SendStateTask.Params): Try { return executeRequest { apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt index 5d72ac33..a47bab62 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt @@ -18,9 +18,11 @@ package im.vector.matrix.android.internal.session.room.timeline import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject internal interface GetContextOfEventTask : Task { @@ -31,7 +33,7 @@ internal interface GetContextOfEventTask : Task { @@ -34,9 +36,9 @@ internal interface PaginationTask : Task { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index 828964d8..fbf22c37 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -20,21 +20,18 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineService -import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.database.model.ReadReceiptEntity -import im.vector.matrix.android.internal.database.query.find -import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.fetchCopyMap +import javax.inject.Inject -internal class DefaultTimelineService(private val roomId: String, - private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor, - private val timelineEventFactory: TimelineEventFactory, - private val contextOfEventTask: GetContextOfEventTask, - private val paginationTask: PaginationTask +internal class DefaultTimelineService @Inject constructor(private val roomId: String, + private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, + private val timelineEventFactory: TimelineEventFactory, + private val contextOfEventTask: GetContextOfEventTask, + private val paginationTask: PaginationTask ) : TimelineService { override fun createTimeline(eventId: String?, allowedTypes: List?): Timeline { @@ -42,11 +39,12 @@ internal class DefaultTimelineService(private val roomId: String, } override fun getTimeLineEvent(eventId: String): TimelineEvent? { - return monarchy.fetchCopyMap({ - EventEntity.where(it, eventId = eventId).findFirst() - }, { entity, realm -> - timelineEventFactory.create(entity, realm) - }) + return monarchy + .fetchCopyMap({ + EventEntity.where(it, eventId = eventId).findFirst() + }, { entity, realm -> + timelineEventFactory.create(entity, realm) + }) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt index ae575d68..5ce58e02 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt @@ -20,9 +20,11 @@ import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI +import javax.inject.Inject -internal class GetEventTask(private val roomAPI: RoomAPI +internal class GetEventTask @Inject constructor(private val roomAPI: RoomAPI ) : Task { internal data class Params( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index fe3bdcd4..c2f9827d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -31,12 +31,13 @@ import im.vector.matrix.android.internal.session.room.membership.SenderRoomMembe import io.realm.Realm import timber.log.Timber import java.util.* +import javax.inject.Inject /** * This class is responsible for building [TimelineEvent] returned by a [Timeline] through [TimelineService] * It handles decryption, extracting additional data around an event as sender data and relation. */ -internal class TimelineEventFactory( +internal class TimelineEventFactory @Inject constructor( private val roomMemberExtractor: SenderRoomMemberExtractor, private val relationExtractor: EventRelationExtractor, private val cryptoService: CryptoService) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt index b47fad21..ca2007a1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt @@ -15,6 +15,7 @@ */ package im.vector.matrix.android.internal.session.room.timeline +import android.content.Context import androidx.work.* import java.util.concurrent.TimeUnit @@ -35,13 +36,13 @@ private val WORK_CONSTRAINTS = Constraints.Builder() */ internal object TimelineSendEventWorkCommon { - fun postSequentialWorks(roomId: String, vararg workRequests: OneTimeWorkRequest) { + fun postSequentialWorks(context: Context, roomId: String, vararg workRequests: OneTimeWorkRequest) { when { workRequests.isEmpty() -> return - workRequests.size == 1 -> postWork(roomId, workRequests.first()) + workRequests.size == 1 -> postWork(context, roomId, workRequests.first()) else -> { val firstWork = workRequests.first() - var continuation = WorkManager.getInstance() + var continuation = WorkManager.getInstance(context) .beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, firstWork) for (i in 1 until workRequests.size) { val workRequest = workRequests[i] @@ -52,8 +53,8 @@ internal object TimelineSendEventWorkCommon { } } - fun postWork(roomId: String, workRequest: OneTimeWorkRequest) { - WorkManager.getInstance() + fun postWork(context: Context, roomId: String, workRequest: OneTimeWorkRequest) { + WorkManager.getInstance(context) .beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, workRequest) .enqueue() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt index 9c40b4aa..50881854 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -18,7 +18,12 @@ package im.vector.matrix.android.internal.session.room.timeline import arrow.core.Try import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.internal.database.helper.* +import im.vector.matrix.android.internal.database.helper.addAll +import im.vector.matrix.android.internal.database.helper.addOrUpdate +import im.vector.matrix.android.internal.database.helper.addStateEvents +import im.vector.matrix.android.internal.database.helper.deleteOnCascade +import im.vector.matrix.android.internal.database.helper.isUnlinked +import im.vector.matrix.android.internal.database.helper.merge import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.create @@ -28,11 +33,12 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.kotlin.createObject import timber.log.Timber +import javax.inject.Inject /** * Insert Chunk in DB, and eventually merge with existing chunk event */ -internal class TokenChunkEventPersistor(private val monarchy: Monarchy) { +internal class TokenChunkEventPersistor @Inject constructor(private val monarchy: Monarchy) { /** *
@@ -111,7 +117,7 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
                     Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction")
 
                     val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                            ?: realm.createObject(roomId)
+                                     ?: realm.createObject(roomId)
 
                     val nextToken: String?
                     val prevToken: String?
@@ -134,7 +140,7 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
                     } else {
                         nextChunk?.apply { this.prevToken = prevToken }
                     }
-                            ?: ChunkEntity.create(realm, prevToken, nextToken)
+                                       ?: ChunkEntity.create(realm, prevToken, nextToken)
 
                     if (receivedChunk.events.isEmpty() && receivedChunk.end == receivedChunk.start) {
                         Timber.v("Reach end of $roomId")
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt
index ff399900..e8011b8c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt
@@ -20,9 +20,10 @@ import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.signout.SignOutService
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.configureWith
+import javax.inject.Inject
 
-internal class DefaultSignOutService(private val signOutTask: SignOutTask,
-                                     private val taskExecutor: TaskExecutor) : SignOutService {
+internal class DefaultSignOutService @Inject constructor(private val signOutTask: SignOutTask,
+                                                         private val taskExecutor: TaskExecutor) : SignOutService {
 
     override fun signOut(callback: MatrixCallback) {
         signOutTask
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutModule.kt
index 98098b1a..c123dd01 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutModule.kt
@@ -16,22 +16,30 @@
 
 package im.vector.matrix.android.internal.session.signout
 
-import im.vector.matrix.android.internal.session.DefaultSession
-import org.koin.dsl.module.module
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import im.vector.matrix.android.api.session.signout.SignOutService
+import im.vector.matrix.android.internal.session.SessionScope
 import retrofit2.Retrofit
 
-class SignOutModule {
+@Module
+internal abstract class SignOutModule {
 
-    val definition = module(override = true) {
-
-        scope(DefaultSession.SCOPE) {
-            val retrofit: Retrofit = get()
-            retrofit.create(SignOutAPI::class.java)
+    @Module
+    companion object {
+        @Provides
+        @JvmStatic
+        @SessionScope
+        fun providesSignOutAPI(retrofit: Retrofit): SignOutAPI {
+            return retrofit.create(SignOutAPI::class.java)
         }
-
-        scope(DefaultSession.SCOPE) {
-            DefaultSignOutTask(get(), get()) as SignOutTask
-        }
-
     }
+
+    @Binds
+    abstract fun bindSignOutTask(signOutTask: DefaultSignOutTask): SignOutTask
+
+    @Binds
+    abstract fun bindSignOutService(signOutService: DefaultSignOutService): SignOutService
+
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt
index 5fa09b9a..903763e4 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt
@@ -17,21 +17,29 @@
 package im.vector.matrix.android.internal.session.signout
 
 import arrow.core.Try
+import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.internal.SessionManager
 import im.vector.matrix.android.internal.auth.SessionParamsStore
 import im.vector.matrix.android.internal.network.executeRequest
 import im.vector.matrix.android.internal.task.Task
+import javax.inject.Inject
 
 internal interface SignOutTask : Task
 
-
-internal class DefaultSignOutTask(private val signOutAPI: SignOutAPI,
-                                  private val sessionParamsStore: SessionParamsStore) : SignOutTask {
+internal class DefaultSignOutTask @Inject constructor(private val credentials: Credentials,
+                                                      private val signOutAPI: SignOutAPI,
+                                                      private val sessionManager: SessionManager,
+                                                      private val sessionParamsStore: SessionParamsStore) : SignOutTask {
 
     override suspend fun execute(params: Unit): Try {
         return executeRequest {
             apiCall = signOutAPI.signOut()
         }.flatMap {
-            sessionParamsStore.delete()
+            sessionParamsStore.delete(credentials.userId)
+        }.flatMap {
+            Try {
+                sessionManager.releaseSession(credentials.userId)
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt
index 6dd2763f..8992091b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt
@@ -25,20 +25,22 @@ import im.vector.matrix.android.internal.crypto.CryptoManager
 import im.vector.matrix.android.internal.crypto.MXDecryptionException
 import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
 import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse
 import timber.log.Timber
+import javax.inject.Inject
 
 
-internal class CryptoSyncHandler(private val cryptoManager: CryptoManager,
-                                 private val sasVerificationService: DefaultSasVerificationService) {
+internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: CryptoManager,
+                                                     private val sasVerificationService: DefaultSasVerificationService) {
 
     fun handleToDevice(toDevice: ToDeviceSyncResponse) {
         toDevice.events?.forEach { event ->
             // Decrypt event if necessary
             decryptEvent(event, null)
             if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
-                    && event.mClearEvent?.content?.toModel()?.type == "m.bad.encrypted") {
+                && event.mClearEvent?.content?.toModel()?.type == "m.bad.encrypted") {
                 Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
             } else {
                 sasVerificationService.onToDeviceEvent(event)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt
index 25bea096..d7b8328c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/GroupSyncHandler.kt
@@ -20,12 +20,13 @@ import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.internal.database.model.GroupEntity
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.sync.model.GroupsSyncResponse
 import im.vector.matrix.android.internal.session.sync.model.InvitedGroupSync
 import io.realm.Realm
+import javax.inject.Inject
 
-
-internal class GroupSyncHandler(private val monarchy: Monarchy) {
+internal class GroupSyncHandler @Inject constructor(private val monarchy: Monarchy) {
 
     sealed class HandlingStrategy {
         data class JOINED(val data: Map) : HandlingStrategy()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt
index 7557f71a..930f20f2 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt
@@ -17,8 +17,10 @@
 package im.vector.matrix.android.internal.session.sync
 
 import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
+import im.vector.matrix.android.internal.session.SessionScope
 import io.realm.Realm
 import timber.log.Timber
+import javax.inject.Inject
 
 
 // the receipts dictionnaries
@@ -28,7 +30,7 @@ import timber.log.Timber
 //                    dict value ts value
 typealias ReadReceiptContent = Map>>>
 
-internal class ReadReceiptHandler {
+internal class ReadReceiptHandler @Inject constructor() {
 
     fun handle(realm: Realm, roomId: String, content: ReadReceiptContent?) {
         if (content == null) {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt
index e6d77775..4c80fbcc 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt
@@ -34,16 +34,21 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo
 import im.vector.matrix.android.internal.database.query.where
 import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
 import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
-import im.vector.matrix.android.internal.session.sync.model.*
+import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
+import im.vector.matrix.android.internal.session.sync.model.RoomSync
+import im.vector.matrix.android.internal.session.sync.model.RoomSyncAccountData
+import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral
+import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
 import io.realm.Realm
 import io.realm.kotlin.createObject
 import timber.log.Timber
+import javax.inject.Inject
 
-internal class RoomSyncHandler(private val monarchy: Monarchy,
-                               private val readReceiptHandler: ReadReceiptHandler,
-                               private val roomSummaryUpdater: RoomSummaryUpdater,
-                               private val roomTagHandler: RoomTagHandler,
-                               private val cryptoManager: CryptoManager) {
+internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarchy,
+                                                   private val readReceiptHandler: ReadReceiptHandler,
+                                                   private val roomSummaryUpdater: RoomSummaryUpdater,
+                                                   private val roomTagHandler: RoomTagHandler,
+                                                   private val cryptoManager: CryptoManager) {
 
     sealed class HandlingStrategy {
         data class JOINED(val data: Map) : HandlingStrategy()
@@ -63,9 +68,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
 
     private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
         val rooms = when (handlingStrategy) {
-            is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
+            is HandlingStrategy.JOINED  -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
             is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
-            is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(realm, it.key, it.value) }
+            is HandlingStrategy.LEFT    -> handlingStrategy.data.map { handleLeftRoom(realm, it.key, it.value) }
         }
         realm.insertOrUpdate(rooms)
     }
@@ -77,7 +82,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
         Timber.v("Handle join sync for room $roomId")
 
         val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                ?: realm.createObject(roomId)
+                         ?: realm.createObject(roomId)
 
         if (roomEntity.membership == Membership.INVITE) {
             roomEntity.chunks.deleteAllFromRealm()
@@ -148,7 +153,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
                                   InvitedRoomSync): RoomEntity {
         Timber.v("Handle invited sync for room $roomId")
         val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                ?: realm.createObject(roomId)
+                         ?: realm.createObject(roomId)
         roomEntity.membership = Membership.INVITE
         if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
             val chunkEntity = handleTimelineEvents(realm, roomId, roomSync.inviteState.events)
@@ -162,7 +167,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
                                roomId: String,
                                roomSync: RoomSync): RoomEntity {
         val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                ?: realm.createObject(roomId)
+                         ?: realm.createObject(roomId)
 
         roomEntity.membership = Membership.LEAVE
         roomEntity.chunks.deleteAllFromRealm()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt
index 90915b42..49038eed 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt
@@ -20,10 +20,12 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
 import im.vector.matrix.android.internal.database.model.RoomTagEntity
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import io.realm.Realm
 import java.util.*
+import javax.inject.Inject
 
-internal class RoomTagHandler {
+internal class RoomTagHandler @Inject constructor() {
 
     fun handle(realm: Realm, roomId: String, content: RoomTagContent?) {
         if (content == null) {
@@ -41,7 +43,7 @@ internal class RoomTagHandler {
             tags.add(tag)
         }
         val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
-                ?: RoomSummaryEntity(roomId)
+                                ?: RoomSummaryEntity(roomId)
 
         roomSummaryEntity.tags.clear()
         roomSummaryEntity.tags.addAll(tags)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt
index 5fd7f610..3d84d63e 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt
@@ -16,60 +16,28 @@
 
 package im.vector.matrix.android.internal.session.sync
 
-import im.vector.matrix.android.internal.session.DefaultSession
-import im.vector.matrix.android.internal.session.sync.job.SyncThread
-import org.koin.dsl.module.module
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import im.vector.matrix.android.internal.session.SessionScope
+import im.vector.matrix.android.internal.session.user.DefaultUpdateUserTask
+import im.vector.matrix.android.internal.session.user.UpdateUserTask
 import retrofit2.Retrofit
 
+@Module
+internal abstract class SyncModule {
 
-internal class SyncModule {
-
-    val definition = module(override = true) {
-
-        scope(DefaultSession.SCOPE) {
-            val retrofit: Retrofit = get()
-            retrofit.create(SyncAPI::class.java)
+    @Module
+    companion object {
+        @Provides
+        @JvmStatic
+        @SessionScope
+        fun providesSyncAPI(retrofit: Retrofit): SyncAPI {
+            return retrofit.create(SyncAPI::class.java)
         }
-
-        scope(DefaultSession.SCOPE) {
-            ReadReceiptHandler()
-        }
-
-        scope(DefaultSession.SCOPE) {
-            RoomTagHandler()
-        }
-
-        scope(DefaultSession.SCOPE) {
-            RoomSyncHandler(get(), get(), get(), get(), get())
-        }
-
-        scope(DefaultSession.SCOPE) {
-            GroupSyncHandler(get())
-        }
-
-        scope(DefaultSession.SCOPE) {
-            CryptoSyncHandler(get(), get())
-        }
-
-        scope(DefaultSession.SCOPE) {
-            UserAccountDataSyncHandler(get())
-        }
-
-        scope(DefaultSession.SCOPE) {
-            SyncResponseHandler(get(), get(), get(), get(), get())
-        }
-
-        scope(DefaultSession.SCOPE) {
-            DefaultSyncTask(get(), get(), get(), get()) as SyncTask
-        }
-
-        scope(DefaultSession.SCOPE) {
-            SyncTokenStore(get("SessionRealmConfiguration"))
-        }
-
-        scope(DefaultSession.SCOPE) {
-            SyncThread(get(), get(), get(), get(), get())
-        }
-
     }
+
+    @Binds
+    abstract fun bindSyncTask(syncTask: DefaultSyncTask): SyncTask
+
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
index c6305bd9..593bb924 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt
@@ -18,15 +18,17 @@ package im.vector.matrix.android.internal.session.sync
 
 import arrow.core.Try
 import im.vector.matrix.android.internal.crypto.CryptoManager
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import timber.log.Timber
+import javax.inject.Inject
 import kotlin.system.measureTimeMillis
 
-internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
-                                   private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
-                                   private val groupSyncHandler: GroupSyncHandler,
-                                   private val cryptoSyncHandler: CryptoSyncHandler,
-                                   private val cryptoManager: CryptoManager) {
+internal class SyncResponseHandler @Inject constructor(private val roomSyncHandler: RoomSyncHandler,
+                                                       private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
+                                                       private val groupSyncHandler: GroupSyncHandler,
+                                                       private val cryptoSyncHandler: CryptoSyncHandler,
+                                                       private val cryptoManager: CryptoManager) {
 
     fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try {
         return Try {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt
index 01df2128..78f949de 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt
@@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.sync
 import arrow.core.Try
 import arrow.core.failure
 import arrow.core.recoverWith
+import im.vector.matrix.android.api.auth.data.Credentials
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.matrix.android.internal.auth.SessionParamsStore
@@ -26,6 +27,7 @@ import im.vector.matrix.android.internal.network.executeRequest
 import im.vector.matrix.android.internal.session.filter.FilterRepository
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import im.vector.matrix.android.internal.task.Task
+import javax.inject.Inject
 
 internal interface SyncTask : Task {
 
@@ -33,10 +35,11 @@ internal interface SyncTask : Task {
 
 }
 
-internal class DefaultSyncTask(private val syncAPI: SyncAPI,
-                               private val filterRepository: FilterRepository,
-                               private val syncResponseHandler: SyncResponseHandler,
-                               private val sessionParamsStore: SessionParamsStore
+internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI,
+                                                   private val credentials: Credentials,
+                                                   private val filterRepository: FilterRepository,
+                                                   private val syncResponseHandler: SyncResponseHandler,
+                                                   private val sessionParamsStore: SessionParamsStore
 ) : SyncTask {
 
 
@@ -56,7 +59,7 @@ internal class DefaultSyncTask(private val syncAPI: SyncAPI,
             // Intercept 401
             if (throwable is Failure.ServerError
                     && throwable.error.code == MatrixError.UNKNOWN_TOKEN) {
-                sessionParamsStore.delete()
+                sessionParamsStore.delete(credentials.userId)
             }
 
             // Transmit the throwable
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt
index d4874178..8823f2e3 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTokenStore.kt
@@ -17,10 +17,12 @@
 package im.vector.matrix.android.internal.session.sync
 
 import im.vector.matrix.android.internal.database.model.SyncEntity
+import im.vector.matrix.android.internal.di.SessionDatabase
 import io.realm.Realm
 import io.realm.RealmConfiguration
+import javax.inject.Inject
 
-internal class SyncTokenStore(private val realmConfiguration: RealmConfiguration) {
+internal class SyncTokenStore @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {
 
     fun getLastToken(): String? {
         val realm = Realm.getInstance(realmConfiguration)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
index 65a19afc..52f66fa4 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
@@ -20,10 +20,12 @@ import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
 import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
 import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
+import javax.inject.Inject
 
-internal class UserAccountDataSyncHandler(private val monarchy: Monarchy) {
+internal class UserAccountDataSyncHandler @Inject constructor(private val monarchy: Monarchy) {
 
     fun handle(accountData: UserAccountDataSync) {
         accountData.list.forEach {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt
index 504c621a..2fa0d494 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt
@@ -19,12 +19,13 @@ import android.app.Service
 import android.content.Intent
 import android.os.Binder
 import android.os.IBinder
+import androidx.work.ListenableWorker
 import com.squareup.moshi.JsonEncodingException
+import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.matrix.android.api.util.Cancelable
-import im.vector.matrix.android.internal.di.MatrixKoinComponent
 import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
 import im.vector.matrix.android.internal.session.sync.SyncTask
 import im.vector.matrix.android.internal.session.sync.SyncTokenStore
@@ -32,10 +33,11 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.TaskThread
 import im.vector.matrix.android.internal.task.configureWith
-import org.koin.standalone.inject
+import im.vector.matrix.android.internal.worker.getSessionComponent
 import timber.log.Timber
 import java.net.SocketTimeoutException
 import java.util.*
+import javax.inject.Inject
 import kotlin.collections.ArrayList
 
 private const val DEFAULT_LONG_POOL_TIMEOUT = 10_000L
@@ -47,15 +49,15 @@ private const val BACKGROUND_LONG_POOL_TIMEOUT = 0L
  * in order to be able to perform a sync even if the app is not running.
  * The  and  must be declared in the Manifest or the app using the SDK
  */
-open class SyncService : Service(), MatrixKoinComponent {
+open class SyncService : Service() {
 
     private var mIsSelfDestroyed: Boolean = false
     private var cancelableTask: Cancelable? = null
-    private val syncTokenStore: SyncTokenStore by inject()
 
-    private val syncTask: SyncTask by inject()
-    private val networkConnectivityChecker: NetworkConnectivityChecker by inject()
-    private val taskExecutor: TaskExecutor by inject()
+    private lateinit var syncTokenStore: SyncTokenStore
+    private lateinit var syncTask: SyncTask
+    private lateinit var networkConnectivityChecker: NetworkConnectivityChecker
+    private lateinit var taskExecutor: TaskExecutor
 
     private var localBinder = LocalBinder()
 
@@ -66,9 +68,15 @@ open class SyncService : Service(), MatrixKoinComponent {
 
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         Timber.i("onStartCommand ${intent}")
-        nextBatchDelay  = 60_000L
+        nextBatchDelay = 60_000L
         timeout = 0
         intent?.let {
+            val userId = it.getStringExtra(EXTRA_USER_ID)
+            val sessionComponent =  Matrix.getInstance(applicationContext).sessionManager.getSessionComponent(userId) ?: return@let
+            syncTokenStore = sessionComponent.syncTokenStore()
+            syncTask = sessionComponent.syncTask()
+            networkConnectivityChecker = sessionComponent.networkConnectivityChecker()
+            taskExecutor = sessionComponent.taskExecutor()
             if (cancelableTask == null) {
                 timer.cancel()
                 timer = Timer()
@@ -192,7 +200,6 @@ open class SyncService : Service(), MatrixKoinComponent {
         }
 
         internal fun notifySyncFinish() {
-
             listeners.forEach {
                 try {
                     it.onSyncFinsh()
@@ -236,6 +243,8 @@ open class SyncService : Service(), MatrixKoinComponent {
 
     companion object {
 
+        const val EXTRA_USER_ID = "EXTRA_USER_ID"
+
         fun startLongPool(delay: Long) {
 
         }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncServiceOld.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncServiceOld.kt
deleted file mode 100644
index a76aa99d..00000000
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncServiceOld.kt
+++ /dev/null
@@ -1,201 +0,0 @@
-package im.vector.matrix.android.internal.session.sync.job
-
-import android.app.Service
-import android.content.Intent
-import android.os.Binder
-import android.os.IBinder
-import com.squareup.moshi.JsonEncodingException
-import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.failure.Failure
-import im.vector.matrix.android.api.failure.MatrixError
-import im.vector.matrix.android.api.util.Cancelable
-import im.vector.matrix.android.internal.di.MatrixKoinComponent
-import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
-import im.vector.matrix.android.internal.session.sync.SyncTask
-import im.vector.matrix.android.internal.session.sync.SyncTokenStore
-import im.vector.matrix.android.internal.session.sync.model.SyncResponse
-import im.vector.matrix.android.internal.task.TaskExecutor
-import im.vector.matrix.android.internal.task.TaskThread
-import im.vector.matrix.android.internal.task.configureWith
-import org.koin.standalone.inject
-import timber.log.Timber
-import java.net.SocketTimeoutException
-import java.util.*
-import kotlin.collections.ArrayList
-
-private const val DEFAULT_LONG_POOL_TIMEOUT = 10_000L
-private const val BACKGROUND_LONG_POOL_TIMEOUT = 0L
-
-/**
- * Can execute periodic sync task.
- * An IntentService is used in conjunction with the AlarmManager and a Broadcast Receiver
- * in order to be able to perform a sync even if the app is not running.
- * The  and  must be declared in the Manifest or the app using the SDK
- */
-open class SyncServiceOld : Service(), MatrixKoinComponent {
-
-    private var mIsSelfDestroyed: Boolean = false
-    private var cancelableTask: Cancelable? = null
-    private val syncTokenStore: SyncTokenStore by inject()
-
-    private val syncTask: SyncTask by inject()
-    private val networkConnectivityChecker: NetworkConnectivityChecker by inject()
-    private val taskExecutor: TaskExecutor by inject()
-
-    private var localBinder = LocalBinder()
-
-    val timer = Timer()
-
-    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
-        Timber.i("onStartCommand ${intent}")
-        intent?.let {
-            if (cancelableTask == null) {
-                doSync(BACKGROUND_LONG_POOL_TIMEOUT)
-            } else {
-                //Already syncing ignore
-                Timber.i("Received a start while was already syncking... ignore")
-            }
-        }
-        //No intent just start the service, an alarm will should call with intent
-        return START_STICKY
-    }
-
-    override fun onDestroy() {
-        Timber.i("## onDestroy() : $this")
-
-        if (!mIsSelfDestroyed) {
-            Timber.w("## Destroy by the system : $this")
-        }
-
-        cancelableTask?.cancel()
-        super.onDestroy()
-    }
-
-    fun stopMe() {
-        cancelableTask?.cancel()
-        mIsSelfDestroyed = true
-        stopSelf()
-    }
-
-    fun doSync(currentLongPoolTimeoutMs: Long = DEFAULT_LONG_POOL_TIMEOUT) {
-        var nextBatch = syncTokenStore.getLastToken()
-        if (!networkConnectivityChecker.isConnected()) {
-            Timber.v("Sync is Paused. Waiting...")
-            //TODO Retry in ?
-        } else {
-            Timber.v("Execute sync request with token $nextBatch and timeout $currentLongPoolTimeoutMs")
-            val params = SyncTask.Params(nextBatch, currentLongPoolTimeoutMs)
-            cancelableTask = syncTask.configureWith(params)
-                    .callbackOn(TaskThread.CALLER)
-                    .executeOn(TaskThread.CALLER)
-                    .dispatchTo(object : MatrixCallback {
-                        override fun onSuccess(data: SyncResponse) {
-                            cancelableTask = null
-                            nextBatch = data.nextBatch
-                            syncTokenStore.saveToken(nextBatch)
-                            localBinder.notifySyncFinish()
-                        }
-
-                        override fun onFailure(failure: Throwable) {
-                            Timber.e(failure)
-                            cancelableTask = null
-                            localBinder.notifyFailure(failure)
-//                            if (failure is Failure.NetworkConnection
-//                                    && failure.cause is SocketTimeoutException) {
-//                                // Timeout are not critical
-//                                localBinder.notifyFailure()
-//                            }
-//
-//                            if (failure !is Failure.NetworkConnection
-//                                    || failure.cause is JsonEncodingException) {
-//                                // Wait 10s before retrying
-////                                Thread.sleep(RETRY_WAIT_TIME_MS)
-//                                //TODO Retry in 10S?
-//                            }
-//
-//                            if (failure is Failure.ServerError
-//                                    && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
-//                                // No token or invalid token, stop the thread
-//                                stopSelf()
-//                            }
-
-                        }
-
-                    })
-                    .executeBy(taskExecutor)
-
-            //TODO return and schedule a new one?
-//            Timber.v("Waiting for $currentLongPoolDelayMs delay before new pool...")
-//            if (currentLongPoolDelayMs > 0) Thread.sleep(currentLongPoolDelayMs)
-//            Timber.v("...Continue")
-        }
-    }
-
-    override fun onBind(intent: Intent?): IBinder {
-        return localBinder
-    }
-
-    inner class LocalBinder : Binder() {
-
-        private var listeners = ArrayList()
-
-        fun addListener(listener: SyncListener) {
-            if (!listeners.contains(listener)) {
-                listeners.add(listener)
-            }
-        }
-
-        fun removeListener(listener: SyncListener) {
-            listeners.remove(listener)
-        }
-
-        internal fun notifySyncFinish() {
-
-            listeners.forEach {
-                try {
-                    it.onSyncFinsh()
-                } catch (t: Throwable) {
-                    Timber.e("Failed to notify listener $it")
-                }
-            }
-        }
-
-        internal fun notifyNetworkNotAvailable() {
-            listeners.forEach {
-                try {
-                    it.networkNotAvailable()
-                } catch (t: Throwable) {
-                    Timber.e("Failed to notify listener $it")
-                }
-            }
-        }
-
-        internal fun notifyFailure(throwable: Throwable) {
-
-            listeners.forEach {
-                try {
-                    it.onFailed(throwable)
-                } catch (t: Throwable) {
-                    Timber.e("Failed to notify listener $it")
-                }
-            }
-
-        }
-
-        fun getService(): SyncServiceOld = this@SyncServiceOld
-
-    }
-
-    interface SyncListener {
-        fun onSyncFinsh()
-        fun networkNotAvailable()
-        fun onFailed(throwable: Throwable)
-    }
-
-    companion object {
-
-        fun startLongPool(delay: Long) {
-
-        }
-    }
-}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
index abb27a8a..ba4c009a 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt
@@ -25,6 +25,7 @@ import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.matrix.android.api.session.sync.SyncState
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.sync.SyncTask
 import im.vector.matrix.android.internal.session.sync.SyncTokenStore
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
@@ -35,16 +36,17 @@ import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
 import timber.log.Timber
 import java.net.SocketTimeoutException
 import java.util.concurrent.CountDownLatch
+import javax.inject.Inject
 
 private const val RETRY_WAIT_TIME_MS = 10_000L
 private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
 private const val DEFAULT_LONG_POOL_DELAY = 0L
 
-internal class SyncThread(private val syncTask: SyncTask,
-                          private val networkConnectivityChecker: NetworkConnectivityChecker,
-                          private val syncTokenStore: SyncTokenStore,
-                          private val backgroundDetectionObserver: BackgroundDetectionObserver,
-                          private val taskExecutor: TaskExecutor
+internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
+                                              private val networkConnectivityChecker: NetworkConnectivityChecker,
+                                              private val syncTokenStore: SyncTokenStore,
+                                              private val backgroundDetectionObserver: BackgroundDetectionObserver,
+                                              private val taskExecutor: TaskExecutor
 ) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
 
     private var state: SyncState = SyncState.IDLE
@@ -113,7 +115,7 @@ internal class SyncThread(private val syncTask: SyncTask,
 
                             override fun onFailure(failure: Throwable) {
                                 if (failure is Failure.NetworkConnection
-                                        && failure.cause is SocketTimeoutException) {
+                                    && failure.cause is SocketTimeoutException) {
                                     // Timeout are not critical
                                     Timber.v("Timeout")
                                 } else {
@@ -121,13 +123,13 @@ internal class SyncThread(private val syncTask: SyncTask,
                                 }
 
                                 if (failure !is Failure.NetworkConnection
-                                        || failure.cause is JsonEncodingException) {
+                                    || failure.cause is JsonEncodingException) {
                                     // Wait 10s before retrying
                                     sleep(RETRY_WAIT_TIME_MS)
                                 }
 
                                 if (failure is Failure.ServerError
-                                        && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
+                                    && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
                                     // No token or invalid token, stop the thread
                                     updateStateTo(SyncState.KILLING)
                                 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt
index dd5b91f6..6b2af8fc 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt
@@ -21,42 +21,44 @@ import com.squareup.moshi.JsonClass
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.failure.MatrixError
 import im.vector.matrix.android.internal.auth.SessionParamsStore
-import im.vector.matrix.android.internal.di.MatrixKoinComponent
 import im.vector.matrix.android.internal.network.executeRequest
 import im.vector.matrix.android.internal.session.filter.FilterRepository
 import im.vector.matrix.android.internal.session.sync.SyncAPI
 import im.vector.matrix.android.internal.session.sync.SyncResponseHandler
 import im.vector.matrix.android.internal.session.sync.SyncTokenStore
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
-import im.vector.matrix.android.internal.util.WorkerParamsFactory
-import org.koin.standalone.inject
+import im.vector.matrix.android.internal.worker.WorkerParamsFactory
+import im.vector.matrix.android.internal.worker.getSessionComponent
 import timber.log.Timber
 import java.util.concurrent.TimeUnit
+import javax.inject.Inject
 
 
 private const val DEFAULT_LONG_POOL_TIMEOUT = 0L
 
 internal class SyncWorker(context: Context,
-                 workerParameters: WorkerParameters
-) : CoroutineWorker(context, workerParameters), MatrixKoinComponent {
+                          workerParameters: WorkerParameters
+) : CoroutineWorker(context, workerParameters) {
 
     @JsonClass(generateAdapter = true)
     internal data class Params(
+            val userId: String,
             val timeout: Long = DEFAULT_LONG_POOL_TIMEOUT,
             val automaticallyRetry: Boolean = false
     )
 
-    private val syncAPI by inject()
-    private val filterRepository by inject()
-    private val syncResponseHandler by inject()
-    private val sessionParamsStore by inject()
-    private val syncTokenStore by inject()
+    @Inject lateinit var syncAPI: SyncAPI
+    @Inject lateinit var filterRepository: FilterRepository
+    @Inject lateinit var syncResponseHandler: SyncResponseHandler
+    @Inject lateinit var sessionParamsStore: SessionParamsStore
+    @Inject lateinit var syncTokenStore: SyncTokenStore
 
 
     override suspend fun doWork(): Result {
         Timber.i("Sync work starting")
-        val params = WorkerParamsFactory.fromData(inputData)
-                ?: Params()
+        val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success()
+        val sessionComponent = getSessionComponent(params.userId) ?: return Result.success()
+        sessionComponent.inject(this)
 
         val requestParams = HashMap()
         requestParams["timeout"] = params.timeout.toString()
@@ -70,7 +72,7 @@ internal class SyncWorker(context: Context,
                 {
                     if (it is Failure.ServerError
                             && it.error.code == MatrixError.UNKNOWN_TOKEN) {
-                        sessionParamsStore.delete()
+                        sessionParamsStore.delete(params.userId)
                         Result.failure()
                     } else {
                         Timber.i("Sync work failed $it")
@@ -89,8 +91,8 @@ internal class SyncWorker(context: Context,
     }
 
     companion object {
-        fun requireBackgroundSync(serverTimeout: Long = 0) {
-            val data = WorkerParamsFactory.toData(Params(serverTimeout, false))
+        fun requireBackgroundSync(context: Context, userId: String, serverTimeout: Long = 0) {
+            val data = WorkerParamsFactory.toData(Params(userId, serverTimeout, false))
             val workRequest = OneTimeWorkRequestBuilder()
                     .setInputData(data)
                     .setConstraints(Constraints.Builder()
@@ -98,11 +100,11 @@ internal class SyncWorker(context: Context,
                             .build())
                     .setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
                     .build()
-            WorkManager.getInstance().enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
+            WorkManager.getInstance(context).enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
         }
 
-        fun automaticallyBackgroundSync(serverTimeout: Long = 0, delay: Long = 30_000) {
-            val data = WorkerParamsFactory.toData(Params(serverTimeout, true))
+        fun automaticallyBackgroundSync(context: Context, userId: String, serverTimeout: Long = 0, delay: Long = 30_000) {
+            val data = WorkerParamsFactory.toData(Params(userId, serverTimeout, true))
             val workRequest = OneTimeWorkRequestBuilder()
                     .setInputData(data)
                     .setConstraints(Constraints.Builder()
@@ -110,11 +112,11 @@ internal class SyncWorker(context: Context,
                             .build())
                     .setBackoffCriteria(BackoffPolicy.LINEAR, delay, TimeUnit.MILLISECONDS)
                     .build()
-            WorkManager.getInstance().enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
+            WorkManager.getInstance(context).enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
         }
 
-        fun stopAnyBackgroundSync() {
-            WorkManager.getInstance().cancelUniqueWork("BG_SYNCP")
+        fun stopAnyBackgroundSync(context: Context) {
+            WorkManager.getInstance(context).cancelUniqueWork("BG_SYNCP")
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
index e3f68f06..cb1bcdce 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
@@ -27,9 +27,11 @@ import im.vector.matrix.android.internal.database.RealmLiveData
 import im.vector.matrix.android.internal.database.mapper.asDomain
 import im.vector.matrix.android.internal.database.model.UserEntity
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.util.fetchCopied
+import javax.inject.Inject
 
-internal class DefaultUserService(private val monarchy: Monarchy) : UserService {
+internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy) : UserService {
 
     override fun getUser(userId: String): User? {
         val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UpdateUserTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UpdateUserTask.kt
index 3230ecf0..eb331e41 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UpdateUserTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UpdateUserTask.kt
@@ -23,9 +23,11 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
 import im.vector.matrix.android.internal.database.model.EventEntity
 import im.vector.matrix.android.internal.database.model.UserEntity
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.session.room.membership.RoomMembers
 import im.vector.matrix.android.internal.task.Task
 import im.vector.matrix.android.internal.util.tryTransactionSync
+import javax.inject.Inject
 
 internal interface UpdateUserTask : Task {
 
@@ -33,7 +35,7 @@ internal interface UpdateUserTask : Task {
 
 }
 
-internal class DefaultUpdateUserTask(private val monarchy: Monarchy) : UpdateUserTask {
+internal class DefaultUpdateUserTask @Inject constructor(private val monarchy: Monarchy) : UpdateUserTask {
 
     override suspend fun execute(params: UpdateUserTask.Params): Try {
         return monarchy.tryTransactionSync { realm ->
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityUpdater.kt
index b09fa911..df9dc277 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityUpdater.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityUpdater.kt
@@ -24,14 +24,16 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
 import im.vector.matrix.android.internal.database.model.EventEntity
 import im.vector.matrix.android.internal.database.model.EventEntityFields
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.SessionScope
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.TaskThread
 import im.vector.matrix.android.internal.task.configureWith
 import io.realm.Sort
+import javax.inject.Inject
 
-internal class UserEntityUpdater(monarchy: Monarchy,
-                                 private val updateUserTask: UpdateUserTask,
-                                 private val taskExecutor: TaskExecutor)
+internal class UserEntityUpdater @Inject constructor(monarchy: Monarchy,
+                                                     private val updateUserTask: UpdateUserTask,
+                                                     private val taskExecutor: TaskExecutor)
     : RealmLiveEntityObserver(monarchy) {
 
     override val query = Monarchy.Query {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
index a82fddc8..b3af7e45 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
@@ -1,31 +1,34 @@
 /*
- * Copyright 2019 New Vector Ltd
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
  *
- * 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.matrix.android.internal.session.user
 
-import im.vector.matrix.android.internal.session.DefaultSession
-import org.koin.dsl.module.module
+import dagger.Binds
+import dagger.Module
+import im.vector.matrix.android.api.session.user.UserService
 
-internal class UserModule {
+@Module
+internal abstract class UserModule {
 
-    val definition = module(override = true) {
+    @Binds
+    abstract fun bindUserService(userService: DefaultUserService): UserService
 
-        scope(DefaultSession.SCOPE) {
-            DefaultUpdateUserTask(get()) as UpdateUserTask
-        }
+    @Binds
+    abstract fun bindUpdateUserTask(updateUserTask: DefaultUpdateUserTask): UpdateUserTask
 
-    }
-}
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt
index 153bd97c..02ce07b0 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt
@@ -21,7 +21,7 @@ import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.util.Cancelable
 
 internal fun  Task.configureWith(params: PARAMS): ConfigurableTask {
-    return ConfigurableTask(this, params)
+    return ConfigurableTask (this, params)
 }
 
 internal data class ConfigurableTask(
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt
index 263c381d..c46b86bd 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt
@@ -16,6 +16,7 @@
 
 package im.vector.matrix.android.internal.task
 
+
 import arrow.core.Try
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.extensions.foldToCallback
@@ -27,9 +28,10 @@ import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import timber.log.Timber
+import javax.inject.Inject
 import kotlin.coroutines.EmptyCoroutineContext
 
-internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDispatchers) {
+internal class TaskExecutor @Inject constructor(private val coroutineDispatchers: MatrixCoroutineDispatchers) {
 
     fun  execute(task: ConfigurableTask): Cancelable {
 
@@ -40,9 +42,10 @@ internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDis
                     task.execute(task.params)
                 }
             }
-            resultOrFailure.onError {
-                Timber.d(it, "Task failed")
-            }
+            resultOrFailure
+                    .onError {
+                        Timber.d(it, "Task failed")
+                    }
                     .foldToCallback(task.callback)
         }
         return CancelableCoroutine(job)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/BackgroundDetectionObserver.kt
index 5f85804f..a426bdd0 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/BackgroundDetectionObserver.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/BackgroundDetectionObserver.kt
@@ -19,12 +19,15 @@ package im.vector.matrix.android.internal.util
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleObserver
 import androidx.lifecycle.OnLifecycleEvent
+import im.vector.matrix.android.internal.di.MatrixScope
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * To be attached to ProcessLifecycleOwner lifecycle
  */
-internal class BackgroundDetectionObserver : LifecycleObserver {
+@MatrixScope
+internal class BackgroundDetectionObserver @Inject constructor() : LifecycleObserver {
 
     var isIsBackground: Boolean = false
         private set
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt
index f144ed5c..ebd20ec6 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt
@@ -16,14 +16,16 @@
 
 package im.vector.matrix.android.internal.util
 
+import android.content.Context
 import androidx.work.WorkManager
 import im.vector.matrix.android.api.util.Cancelable
 import java.util.*
 
-internal class CancelableWork(private val workId: UUID) : Cancelable {
+internal class CancelableWork(private val context: Context,
+                              private val workId: UUID) : Cancelable {
 
     override fun cancel() {
-        WorkManager.getInstance().cancelWorkById(workId)
+        WorkManager.getInstance(context).cancelWorkById(workId)
     }
 
 }
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt
index 02d90c2f..479412cf 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt
@@ -19,8 +19,13 @@ package im.vector.matrix.android.internal.util
 import android.content.res.Resources
 import androidx.annotation.NonNull
 import androidx.annotation.StringRes
+import dagger.Reusable
+import im.vector.matrix.android.internal.di.MatrixScope
+import javax.inject.Inject
+import javax.inject.Singleton
 
-internal class StringProvider(private val resources: Resources) {
+@Reusable
+internal class StringProvider @Inject constructor(private val resources: Resources) {
 
     /**
      * Returns a localized string from the application's package's
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/DelegateWorkerFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/DelegateWorkerFactory.kt
new file mode 100644
index 00000000..5141e06f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/DelegateWorkerFactory.kt
@@ -0,0 +1,29 @@
+/*
+ *
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package im.vector.matrix.android.internal.worker
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerParameters
+
+interface DelegateWorkerFactory {
+
+    fun create(context: Context, params: WorkerParameters): ListenableWorker
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt
new file mode 100644
index 00000000..e31e300e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt
@@ -0,0 +1,44 @@
+/*
+ *
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package im.vector.matrix.android.internal.worker
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerFactory
+import androidx.work.WorkerParameters
+import im.vector.matrix.android.internal.di.MatrixScope
+import javax.inject.Inject
+import javax.inject.Provider
+
+class MatrixWorkerFactory @Inject constructor(
+        private val workerFactories: Map, @JvmSuppressWildcards Provider>
+) : WorkerFactory() {
+
+    override fun createWorker(
+            appContext: Context,
+            workerClassName: String,
+            workerParameters: WorkerParameters
+    ): ListenableWorker? {
+        val foundEntry =
+                workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
+        val factoryProvider = foundEntry?.value
+                ?: throw IllegalArgumentException("unknown worker class name: $workerClassName")
+        return factoryProvider.get().create(appContext, workerParameters)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/SessionWorkerParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/SessionWorkerParams.kt
new file mode 100644
index 00000000..874254cc
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/SessionWorkerParams.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.worker
+
+interface SessionWorkerParams {
+    val userId: String
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/Worker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/Worker.kt
new file mode 100644
index 00000000..4a4b5e3b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/Worker.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.worker
+
+import androidx.work.ListenableWorker
+import im.vector.matrix.android.api.Matrix
+import im.vector.matrix.android.internal.session.SessionComponent
+
+internal fun ListenableWorker.getSessionComponent(userId: String): SessionComponent? {
+    return Matrix.getInstance(applicationContext).sessionManager.getSessionComponent(userId)
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerKey.kt
new file mode 100644
index 00000000..c5b0f45a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerKey.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.worker
+
+import androidx.work.ListenableWorker
+import dagger.MapKey
+import kotlin.reflect.KClass
+
+@MapKey
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class WorkerKey(val value: KClass)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/WorkerParamsFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerParamsFactory.kt
similarity index 96%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/WorkerParamsFactory.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerParamsFactory.kt
index 7c1cc72d..87d17b9c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/WorkerParamsFactory.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/WorkerParamsFactory.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.util
+package im.vector.matrix.android.internal.worker
 
 import androidx.work.Data
 import im.vector.matrix.android.internal.di.MoshiProvider
diff --git a/vector/build.gradle b/vector/build.gradle
index 8fc7f70a..ba12a14a 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -146,6 +146,7 @@ dependencies {
     def big_image_viewer_version = '1.5.6'
     def glide_version = '4.9.0'
     def moshi_version = '1.8.0'
+    def daggerVersion = '2.23.1'
 
     implementation project(":matrix-sdk-android")
     implementation project(":matrix-sdk-android-rx")
@@ -184,7 +185,7 @@ dependencies {
     implementation 'com.airbnb.android:mvrx:1.0.1'
 
     // Work
-    implementation "android.arch.work:work-runtime-ktx:1.0.0"
+    implementation "androidx.work:work-runtime-ktx:2.1.0-beta01"
 
     // Functional Programming
     implementation "io.arrow-kt:arrow-core:$arrow_version"
@@ -231,8 +232,10 @@ dependencies {
     implementation 'com.github.jaiselrahman:FilePicker:1.2.2'
 
     // DI
-    implementation "org.koin:koin-android:$koin_version"
-    implementation "org.koin:koin-android-scope:$koin_version"
+    implementation "com.google.dagger:dagger:$daggerVersion"
+    kapt "com.google.dagger:dagger-compiler:$daggerVersion"
+    compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
+    kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
 
     // gplay flavor only
     gplayImplementation('com.google.firebase:firebase-messaging:19.0.1') {
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
index 211f7337..adc2d00c 100644
--- a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
@@ -15,26 +15,29 @@
  */
 package im.vector.riotredesign.fdroid.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
+import androidx.appcompat.app.AppCompatActivity
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.settings.PreferencesManager
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
+import javax.inject.Inject
 
 /**
  * Test that the application is started on boot
  */
-class TestAutoStartBoot(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
+class TestAutoStartBoot @Inject constructor(private val context: AppCompatActivity,
+                                            private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
 
     override fun perform() {
-        if (PreferencesManager.autoStartOnBoot(fragment.context)) {
-            description = fragment.getString(R.string.settings_troubleshoot_test_service_boot_success)
+        if (PreferencesManager.autoStartOnBoot(context)) {
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
             status = TestStatus.SUCCESS
             quickFix = null
         } else {
-            description = fragment.getString(R.string.settings_troubleshoot_test_service_boot_failed)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_failed)
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
                 override fun doFix() {
-                    PreferencesManager.setAutoStartOnBoot(fragment.context, true)
+                    PreferencesManager.setAutoStartOnBoot(context, true)
                     manager?.retry()
                 }
             }
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
index 72c0b433..c124864a 100644
--- a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
@@ -17,24 +17,27 @@ package im.vector.riotredesign.fdroid.features.settings.troubleshoot
 
 import android.content.Context
 import android.net.ConnectivityManager
+import androidx.appcompat.app.AppCompatActivity
 import androidx.core.net.ConnectivityManagerCompat
-import androidx.fragment.app.Fragment
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
+import javax.inject.Inject
 
-class TestBackgroundRestrictions(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
+class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity,
+                                                     private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
 
     override fun perform() {
-        (fragment.context!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).apply {
+        (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).apply {
             // Checks if the device is on a metered network
             if (isActiveNetworkMetered) {
                 // Checks user’s Data Saver settings.
                 val restrictBackgroundStatus = ConnectivityManagerCompat.getRestrictBackgroundStatus(this)
                 when (restrictBackgroundStatus) {
-                    ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED -> {
+                    ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED     -> {
                         // Background data usage is blocked for this app. Wherever possible,
                         // the app should also use less data in the foreground.
-                        description = fragment.getString(R.string.settings_troubleshoot_test_bg_restricted_failed,
+                        description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_failed,
                                 "RESTRICT_BACKGROUND_STATUS_ENABLED")
                         status = TestStatus.FAILED
                         quickFix = null
@@ -42,15 +45,15 @@ class TestBackgroundRestrictions(val fragment: Fragment) : TroubleshootTest(R.st
                     ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED -> {
                         // The app is whitelisted. Wherever possible,
                         // the app should use less data in the foreground and background.
-                        description = fragment.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
+                        description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
                                 "RESTRICT_BACKGROUND_STATUS_WHITELISTED")
                         status = TestStatus.SUCCESS
                         quickFix = null
                     }
-                    ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED -> {
+                    ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED    -> {
                         // Data Saver is disabled. Since the device is connected to a
                         // metered network, the app should use less data wherever possible.
-                        description = fragment.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
+                        description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
                                 "RESTRICT_BACKGROUND_STATUS_DISABLED")
                         status = TestStatus.SUCCESS
                         quickFix = null
@@ -61,7 +64,7 @@ class TestBackgroundRestrictions(val fragment: Fragment) : TroubleshootTest(R.st
             } else {
                 // The device is not on a metered network.
                 // Use data as required to perform syncs, downloads, and updates.
-                description = fragment.getString(R.string.settings_troubleshoot_test_bg_restricted_success, "")
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success, "")
                 status = TestStatus.SUCCESS
                 quickFix = null
             }
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
index ec37964f..92b00c5e 100644
--- a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
@@ -24,6 +24,7 @@ import android.content.Intent
 import android.os.Build
 import android.os.PowerManager
 import androidx.core.content.ContextCompat
+import im.vector.matrix.android.internal.session.sync.job.SyncService
 import im.vector.riotredesign.fdroid.service.VectorSyncService
 import timber.log.Timber
 
@@ -39,11 +40,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
             }
         }
 
-
+        val userId = intent.getStringExtra(SyncService.EXTRA_USER_ID)
         // This method is called when the BroadcastReceiver is receiving an Intent broadcast.
         Timber.d("RestartBroadcastReceiver received intent")
         Intent(context, VectorSyncService::class.java).also {
             it.action = "SLOW"
+            it.putExtra(SyncService.EXTRA_USER_ID, userId)
             context.startService(it)
             try {
                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -57,7 +59,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
             }
         }
 
-        scheduleAlarm(context, 30_000L)
+        scheduleAlarm(context, userId, 30_000L)
 
         Timber.i("Alarm scheduled to restart service")
     }
@@ -65,9 +67,11 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
     companion object {
         private const val REQUEST_CODE = 0
 
-        fun scheduleAlarm(context: Context, delay: Long) {
+        fun scheduleAlarm(context: Context, userId: String, delay: Long) {
             //Reschedule
-            val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
+            val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
+                putExtra(SyncService.EXTRA_USER_ID, userId)
+            }
             val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
             val firstMillis = System.currentTimeMillis() + delay
             val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
index 4ed793d7..ca19d0d2 100644
--- a/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
@@ -20,12 +20,19 @@ package im.vector.riotredesign.fdroid.receiver
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
+import im.vector.riotredesign.core.di.HasVectorInjector
 import timber.log.Timber
 
 class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
 
     override fun onReceive(context: Context, intent: Intent) {
         Timber.v("## onReceive() ${intent.action}")
-        AlarmSyncBroadcastReceiver.scheduleAlarm(context, 10)
+        val appContext = context.applicationContext
+        if (appContext is HasVectorInjector) {
+            val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
+            if (activeSession != null) {
+                AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.myUserId, 10)
+            }
+        }
     }
 }
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/FcmHelper.kt b/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
index 6691f5ed..fb3c00de 100755
--- a/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
@@ -18,6 +18,7 @@ package im.vector.riotredesign.push.fcm
 
 import android.app.Activity
 import android.content.Context
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.pushers.PushersManager
 import im.vector.riotredesign.fdroid.receiver.AlarmSyncBroadcastReceiver
 import im.vector.riotredesign.features.settings.PreferencesManager
@@ -62,11 +63,11 @@ object FcmHelper {
         AlarmSyncBroadcastReceiver.cancelAlarm(context)
     }
 
-    fun onEnterBackground(context: Context, hasSession: Boolean) {
+    fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
         //We need to use alarm in this mode
-        if (PreferencesManager.areNotificationEnabledForDevice(context)
-                && hasSession) {
-            AlarmSyncBroadcastReceiver.scheduleAlarm(context, 4_000L)
+        if (PreferencesManager.areNotificationEnabledForDevice(context) && activeSessionHolder.hasActiveSession()) {
+            val currentSession = activeSessionHolder.getActiveSession()
+            AlarmSyncBroadcastReceiver.scheduleAlarm(context, currentSession.myUserId, 4_000L)
             Timber.i("Alarm scheduled to restart service")
         }
     }
diff --git a/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
index 9a5ada35..f55d9b8e 100644
--- a/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
+++ b/vector/src/fdroid/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
@@ -16,31 +16,27 @@
 package im.vector.riotredesign.push.fcm
 
 import androidx.fragment.app.Fragment
-import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.fdroid.features.settings.troubleshoot.TestAutoStartBoot
 import im.vector.riotredesign.fdroid.features.settings.troubleshoot.TestBackgroundRestrictions
 import im.vector.riotredesign.features.settings.troubleshoot.*
+import javax.inject.Inject
 
-class NotificationTroubleshootTestManagerFactory {
+class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
+                                                                     private val testAccountSettings: TestAccountSettings,
+                                                                     private val testDeviceSettings: TestDeviceSettings,
+                                                                     private val testBingRulesSettings: TestBingRulesSettings,
+                                                                     private val testAutoStartBoot: TestAutoStartBoot,
+                                                                     private val testBackgroundRestrictions: TestBackgroundRestrictions) {
 
-    companion object {
-        fun createTestManager(fragment: Fragment, session: Session?): NotificationTroubleshootTestManager {
-            val mgr = NotificationTroubleshootTestManager(fragment)
-            mgr.addTest(TestSystemSettings(fragment))
-            if (session != null) {
-                mgr.addTest(TestAccountSettings(fragment, session))
-            }
-            mgr.addTest(TestDeviceSettings(fragment))
-            if (session != null) {
-                mgr.addTest(TestBingRulesSettings(fragment, session))
-            }
-            // mgr.addTest(TestNotificationServiceRunning(fragment))
-            // mgr.addTest(TestServiceRestart(fragment))
-            mgr.addTest(TestAutoStartBoot(fragment))
-            mgr.addTest(TestBackgroundRestrictions(fragment))
-            // mgr.addTest(TestBatteryOptimization(fragment))
-            return mgr
-        }
+    fun create(fragment: Fragment): NotificationTroubleshootTestManager {
+        val mgr = NotificationTroubleshootTestManager(fragment)
+        mgr.addTest(testSystemSettings)
+        mgr.addTest(testAccountSettings)
+        mgr.addTest(testDeviceSettings)
+        mgr.addTest(testBingRulesSettings)
+        mgr.addTest(testAutoStartBoot)
+        mgr.addTest(testBackgroundRestrictions)
+        return mgr
     }
 
 }
\ No newline at end of file
diff --git a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
index ed0a7a7a..ec76fd2b 100644
--- a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
@@ -15,60 +15,58 @@
  */
 package im.vector.riotredesign.gplay.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
+import androidx.appcompat.app.AppCompatActivity
 import com.google.firebase.iid.FirebaseInstanceId
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.utils.startAddGoogleAccountIntent
 import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
 import im.vector.riotredesign.push.fcm.FcmHelper
 import timber.log.Timber
+import javax.inject.Inject
 
 /*
 * Test that app can successfully retrieve a token via firebase
  */
-class TestFirebaseToken(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
+class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity,
+                                            private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
 
     override fun perform() {
         status = TestStatus.RUNNING
-        val activity = fragment.activity
-        if (activity != null) {
-            try {
-                FirebaseInstanceId.getInstance().instanceId
-                        .addOnCompleteListener(activity) { task ->
-                            if (!task.isSuccessful) {
-                                val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage
-                                //Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
-                                if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) {
-                                    description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
-                                } else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) {
-                                    description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
-                                } else if ("ACCOUNT_MISSING".equals(errorMsg)) {
-                                    description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
-                                    quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
-                                        override fun doFix() {
-                                            startAddGoogleAccountIntent(fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
-                                        }
+        try {
+            FirebaseInstanceId.getInstance().instanceId
+                    .addOnCompleteListener(context) { task ->
+                        if (!task.isSuccessful) {
+                            val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage
+                            //Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
+                            if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) {
+                                description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
+                            } else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) {
+                                description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
+                            } else if ("ACCOUNT_MISSING".equals(errorMsg)) {
+                                description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
+                                quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
+                                    override fun doFix() {
+                                        startAddGoogleAccountIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX)
                                     }
-                                } else {
-                                    description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
                                 }
-                                status = TestStatus.FAILED
                             } else {
-                                task.result?.token?.let {
-                                    val tok = it.substring(0, Math.min(8, it.length)) + "********************"
-                                    description = fragment.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
-                                    Timber.e("Retrieved FCM token success [$it].")
-                                    FcmHelper.storeFcmToken(fragment.requireContext(),tok)
-                                }
-                                status = TestStatus.SUCCESS
+                                description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
                             }
+                            status = TestStatus.FAILED
+                        } else {
+                            task.result?.token?.let {
+                                val tok = it.substring(0, Math.min(8, it.length)) + "********************"
+                                description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
+                                Timber.e("Retrieved FCM token success [$it].")
+                                FcmHelper.storeFcmToken(context, tok)
+                            }
+                            status = TestStatus.SUCCESS
                         }
-            } catch (e: Throwable) {
-                description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed, e.localizedMessage)
-                status = TestStatus.FAILED
-            }
-        } else {
+                    }
+        } catch (e: Throwable) {
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, e.localizedMessage)
             status = TestStatus.FAILED
         }
     }
diff --git a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestPlayServices.kt
index 842f043d..f99787c6 100644
--- a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestPlayServices.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestPlayServices.kt
@@ -15,38 +15,39 @@
  */
 package im.vector.riotredesign.gplay.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
+import androidx.appcompat.app.AppCompatActivity
 import com.google.android.gms.common.ConnectionResult
 import com.google.android.gms.common.GoogleApiAvailability
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
 import timber.log.Timber
+import javax.inject.Inject
 
 /*
 * Check that the play services APK is available an up-to-date. If needed provide quick fix to install it.
  */
-class TestPlayServices(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
+class TestPlayServices @Inject constructor(private val context: AppCompatActivity,
+                                           private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
 
     override fun perform() {
         val apiAvailability = GoogleApiAvailability.getInstance()
-        val resultCode = apiAvailability.isGooglePlayServicesAvailable(fragment.context)
+        val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
         if (resultCode == ConnectionResult.SUCCESS) {
             quickFix = null
-            description = fragment.getString(R.string.settings_troubleshoot_test_play_services_success)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_play_services_success)
             status = TestStatus.SUCCESS
         } else {
             if (apiAvailability.isUserResolvableError(resultCode)) {
                 quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_play_services_quickfix) {
                     override fun doFix() {
-                        fragment.activity?.let {
-                            apiAvailability.getErrorDialog(it, resultCode, 9000 /*hey does the magic number*/).show()
-                        }
+                        apiAvailability.getErrorDialog(context, resultCode, 9000 /*hey does the magic number*/).show()
                     }
                 }
                 Timber.e("Play Services apk error $resultCode -> ${apiAvailability.getErrorString(resultCode)}.")
             }
 
-            description = fragment.getString(R.string.settings_troubleshoot_test_play_services_failed, apiAvailability.getErrorString(resultCode))
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_play_services_failed, apiAvailability.getErrorString(resultCode))
             status = TestStatus.FAILED
         }
     }
diff --git a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
index e0fd3588..4485ce90 100644
--- a/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
@@ -15,45 +15,46 @@
  */
 package im.vector.riotredesign.gplay.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
+import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Observer
 import androidx.work.WorkInfo
 import androidx.work.WorkManager
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.session.pushers.PusherState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.pushers.PushersManager
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
 import im.vector.riotredesign.push.fcm.FcmHelper
-import org.koin.android.ext.android.get
+import javax.inject.Inject
 
 /**
  * Force registration of the token to HomeServer
  */
-class TestTokenRegistration(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
+class TestTokenRegistration @Inject constructor(private val context: AppCompatActivity,
+                                                private val stringProvider: StringProvider,
+                                                private val pushersManager: PushersManager,
+                                                private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
 
     override fun perform() {
         //Check if we have a registered pusher for this token
-        val fcmToken = FcmHelper.getFcmToken(fragment.requireContext()) ?: run {
+        val fcmToken = FcmHelper.getFcmToken(context) ?: run {
             status = TestStatus.FAILED
             return
         }
-
-        val session = Matrix.getInstance().currentSession ?: run {
+        val session = activeSessionHolder.getSafeActiveSession() ?: run {
             status = TestStatus.FAILED
             return
         }
-
         val pusher = session.pushers().filter {
             it.pushKey == fcmToken && it.state == PusherState.REGISTERED
         }
-
-        if (pusher == null) {
-            description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_failed, null)
+        if (pusher.isEmpty()) {
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_failed, null)
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
                 override fun doFix() {
-                    val workId = fragment.get().registerPusherWithFcmKey(fcmToken)
-                    WorkManager.getInstance().getWorkInfoByIdLiveData(workId).observe(fragment, Observer { workInfo ->
+                    val workId = pushersManager.registerPusherWithFcmKey(fcmToken)
+                    WorkManager.getInstance().getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
                         if (workInfo != null) {
                             if (workInfo.state == WorkInfo.State.SUCCEEDED) {
                                 manager?.retry()
@@ -69,7 +70,7 @@ class TestTokenRegistration(val fragment: Fragment) : TroubleshootTest(R.string.
             status = TestStatus.FAILED
 
         } else {
-            description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_success)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_success)
             status = TestStatus.SUCCESS
         }
 
diff --git a/vector/src/gplay/java/im/vector/riotredesign/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/riotredesign/gplay/push/fcm/VectorFirebaseMessagingService.kt
index 2d0ad013..177b2346 100755
--- a/vector/src/gplay/java/im/vector/riotredesign/gplay/push/fcm/VectorFirebaseMessagingService.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/gplay/push/fcm/VectorFirebaseMessagingService.kt
@@ -26,11 +26,12 @@ import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ProcessLifecycleOwner
 import com.google.firebase.messaging.FirebaseMessagingService
 import com.google.firebase.messaging.RemoteMessage
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.riotredesign.BuildConfig
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.extensions.vectorComponent
 import im.vector.riotredesign.core.preference.BingRule
 import im.vector.riotredesign.core.pushers.PushersManager
 import im.vector.riotredesign.features.badge.BadgeProxy
@@ -40,23 +41,32 @@ import im.vector.riotredesign.features.notifications.NotificationDrawerManager
 import im.vector.riotredesign.features.notifications.SimpleNotifiableEvent
 import im.vector.riotredesign.features.settings.PreferencesManager
 import im.vector.riotredesign.push.fcm.FcmHelper
-import org.koin.android.ext.android.inject
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * Class extending FirebaseMessagingService.
  */
 class VectorFirebaseMessagingService : FirebaseMessagingService() {
 
-    private val notificationDrawerManager by inject()
-    private val pusherManager by inject()
+    private lateinit var notificationDrawerManager: NotificationDrawerManager
+    private lateinit var notifiableEventResolver: NotifiableEventResolver
+    private lateinit var pusherManager: PushersManager
+    private lateinit var activeSessionHolder: ActiveSessionHolder
 
-    private val notifiableEventResolver by inject()
     // UI handler
     private val mUIHandler by lazy {
         Handler(Looper.getMainLooper())
     }
 
+    override fun onCreate() {
+        super.onCreate()
+        notificationDrawerManager = vectorComponent().notificationDrawerManager()
+        notifiableEventResolver = vectorComponent().notifiableEventResolver()
+        pusherManager = vectorComponent().pusherManager()
+        activeSessionHolder = vectorComponent().activeSessionHolder()
+    }
+
     /**
      * Called when message is received.
      *
@@ -93,13 +103,12 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
      * you retrieve the token.
      */
     override fun onNewToken(refreshedToken: String?) {
-        if (Matrix.getInstance().currentSession == null) return
         Timber.i("onNewToken: FCM Token has been updated")
         FcmHelper.storeFcmToken(this, refreshedToken)
         if (refreshedToken == null) {
             Timber.w("onNewToken:received null token")
         } else {
-            if (PreferencesManager.areNotificationEnabledForDevice(applicationContext)) {
+            if (PreferencesManager.areNotificationEnabledForDevice(applicationContext) && activeSessionHolder.hasActiveSession()) {
                 pusherManager.registerPusherWithFcmKey(refreshedToken)
             }
         }
@@ -139,7 +148,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
             val unreadCount = data.get("unread")?.let { Integer.parseInt(it) } ?: 0
             BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
 
-            val session = safeGetCurrentSession()
+            val session = activeSessionHolder.getSafeActiveSession()
 
             if (session == null) {
                 Timber.w("## Can't sync from push, no current session")
@@ -157,21 +166,12 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
         }
     }
 
-    fun safeGetCurrentSession(): Session? {
-        try {
-            return Matrix.getInstance().currentSession
-        } catch (e: Throwable) {
-            Timber.e(e, "## Failed to get current session")
-            return null
-        }
-    }
-
     // check if the event was not yet received
     // a previous catchup might have already retrieved the notified event
     private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {
         if (null != eventId && null != roomId) {
             try {
-                val session = safeGetCurrentSession() ?: return false
+                val session = activeSessionHolder.getSafeActiveSession() ?: return false
                 val room = session.getRoom(roomId) ?: return false
                 return room.getTimeLineEvent(eventId) != null
             } catch (e: Exception) {
@@ -231,7 +231,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
                 if (notifiableEvent is NotifiableMessageEvent) {
                     if (TextUtils.isEmpty(notifiableEvent.senderName)) {
                         notifiableEvent.senderName = data["sender_display_name"]
-                                ?: data["sender"] ?: ""
+                                                     ?: data["sender"] ?: ""
                     }
                     if (TextUtils.isEmpty(notifiableEvent.roomName)) {
                         notifiableEvent.roomName = findRoomNameBestEffort(data, session) ?: ""
@@ -277,11 +277,11 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
 
         try {
             return Event(eventId = data["event_id"],
-                    senderId = data["sender"],
-                    roomId = data["room_id"],
-                    type = data.getValue("type"),
+                         senderId = data["sender"],
+                         roomId = data["room_id"],
+                         type = data.getValue("type"),
                     // TODO content = data.getValue("content"),
-                    originServerTs = System.currentTimeMillis())
+                         originServerTs = System.currentTimeMillis())
         } catch (e: Exception) {
             Timber.e(e, "buildEvent fails ")
         }
diff --git a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
index a0e2d824..ca807f36 100755
--- a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
@@ -25,6 +25,7 @@ import com.google.android.gms.common.ConnectionResult
 import com.google.android.gms.common.GoogleApiAvailability
 import com.google.firebase.iid.FirebaseInstanceId
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.pushers.PushersManager
 import timber.log.Timber
 
@@ -104,7 +105,7 @@ object FcmHelper {
         // No op
     }
 
-    fun onEnterBackground(context: Context, hasSession: Boolean) {
+    fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
         // TODO FCM fallback
     }
 }
diff --git a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
index f2bb4007..d7459b7e 100644
--- a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
+++ b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/NotificationTroubleshootTestManagerFactory.kt
@@ -16,30 +16,34 @@
 package im.vector.riotredesign.push.fcm
 
 import androidx.fragment.app.Fragment
-import im.vector.matrix.android.api.session.Session
-import im.vector.riotredesign.features.settings.troubleshoot.*
+import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
+import im.vector.riotredesign.features.settings.troubleshoot.TestAccountSettings
+import im.vector.riotredesign.features.settings.troubleshoot.TestBingRulesSettings
+import im.vector.riotredesign.features.settings.troubleshoot.TestDeviceSettings
+import im.vector.riotredesign.features.settings.troubleshoot.TestSystemSettings
 import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestFirebaseToken
 import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestPlayServices
 import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestTokenRegistration
+import javax.inject.Inject
 
-class NotificationTroubleshootTestManagerFactory {
+class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
+                                                                     private val testAccountSettings: TestAccountSettings,
+                                                                     private val testDeviceSettings: TestDeviceSettings,
+                                                                     private val testBingRulesSettings: TestBingRulesSettings,
+                                                                     private val testPlayServices: TestPlayServices,
+                                                                     private val testFirebaseToken: TestFirebaseToken,
+                                                                     private val testTokenRegistration: TestTokenRegistration) {
 
-    companion object {
-        fun createTestManager(fragment: Fragment, session: Session?): NotificationTroubleshootTestManager {
-            val mgr = NotificationTroubleshootTestManager(fragment)
-            mgr.addTest(TestSystemSettings(fragment))
-            if (session != null) {
-                mgr.addTest(TestAccountSettings(fragment, session))
-            }
-            mgr.addTest(TestDeviceSettings(fragment))
-            if (session != null) {
-                mgr.addTest(TestBingRulesSettings(fragment, session))
-            }
-            mgr.addTest(TestPlayServices(fragment))
-            mgr.addTest(TestFirebaseToken(fragment))
-            mgr.addTest(TestTokenRegistration(fragment))
-            return mgr
-        }
+    fun create(fragment: Fragment): NotificationTroubleshootTestManager {
+        val mgr = NotificationTroubleshootTestManager(fragment)
+        mgr.addTest(testSystemSettings)
+        mgr.addTest(testAccountSettings)
+        mgr.addTest(testDeviceSettings)
+        mgr.addTest(testBingRulesSettings)
+        mgr.addTest(testPlayServices)
+        mgr.addTest(testFirebaseToken)
+        mgr.addTest(testTokenRegistration)
+        return mgr
     }
 
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt b/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt
index a928d48b..6647cbea 100644
--- a/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt
@@ -3,9 +3,11 @@ package im.vector.riotredesign
 import android.graphics.Typeface
 import androidx.core.provider.FontsContractCompat
 import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
 
-
-class EmojiCompatFontProvider : FontsContractCompat.FontRequestCallback() {
+@Singleton
+class EmojiCompatFontProvider @Inject constructor(): FontsContractCompat.FontRequestCallback() {
 
     var typeface: Typeface? = null
         set(value) {
diff --git a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt
index 90ec01c9..4fc217a0 100644
--- a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt
+++ b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt
@@ -35,37 +35,42 @@ import com.github.piasy.biv.BigImageViewer
 import com.github.piasy.biv.loader.glide.GlideImageLoader
 import com.jakewharton.threetenabp.AndroidThreeTen
 import im.vector.matrix.android.api.Matrix
-import im.vector.riotredesign.core.di.AppModule
+import im.vector.matrix.android.api.MatrixConfiguration
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.di.DaggerVectorComponent
+import im.vector.riotredesign.core.di.HasVectorInjector
+import im.vector.riotredesign.core.di.VectorComponent
+import im.vector.riotredesign.core.extensions.configureAndStart
 import im.vector.riotredesign.features.configuration.VectorConfiguration
-import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
-import im.vector.riotredesign.features.home.HomeModule
 import im.vector.riotredesign.features.lifecycle.VectorActivityLifecycleCallbacks
 import im.vector.riotredesign.features.notifications.NotificationDrawerManager
 import im.vector.riotredesign.features.notifications.NotificationUtils
 import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
 import im.vector.riotredesign.features.rageshake.VectorFileLogger
 import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
-import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
 import im.vector.riotredesign.features.version.getVersion
 import im.vector.riotredesign.push.fcm.FcmHelper
-import org.koin.android.ext.android.get
-import org.koin.android.ext.android.inject
-import org.koin.log.EmptyLogger
-import org.koin.standalone.StandAloneContext.startKoin
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.*
+import javax.inject.Inject
 
-class VectorApplication : Application() {
+class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider {
 
 
     lateinit var appContext: Context
     //font thread handler
-    private var mFontThreadHandler: Handler? = null
+    @Inject lateinit var authenticator: Authenticator
+    @Inject lateinit var vectorConfiguration: VectorConfiguration
+    @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
+    @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
+    @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
+    @Inject lateinit var pushRuleTriggerListener: PushRuleTriggerListener
+    lateinit var vectorComponent: VectorComponent
+    private var fontThreadHandler: Handler? = null
 
-    val vectorConfiguration: VectorConfiguration by inject()
-
-    private val notificationDrawerManager by inject()
 
 //    var slowMode = false
 
@@ -73,50 +78,41 @@ class VectorApplication : Application() {
     override fun onCreate() {
         super.onCreate()
         appContext = this
-
-        logInfo()
-
-        VectorUncaughtExceptionHandler.activate(this)
-
+        vectorComponent = DaggerVectorComponent.factory().create(this)
+        vectorComponent.inject(this)
+        vectorUncaughtExceptionHandler.activate(this)
         // Log
         VectorFileLogger.init(this)
         Timber.plant(Timber.DebugTree(), VectorFileLogger)
-
         if (BuildConfig.DEBUG) {
             Stetho.initializeWithDefaults(this)
         }
-
+        logInfo()
         AndroidThreeTen.init(this)
         BigImageViewer.initialize(GlideImageLoader.with(applicationContext))
         EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
         EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
-        val appModule = AppModule(applicationContext).definition
-        val homeModule = HomeModule().definition
-        val roomDirectoryModule = RoomDirectoryModule().definition
-        val keysBackupModule = KeysBackupModule().definition
-        val koin = startKoin(listOf(appModule, homeModule, roomDirectoryModule, keysBackupModule), logger = EmptyLogger())
-        Matrix.getInstance().setApplicationFlavor(BuildConfig.FLAVOR_DESCRIPTION)
         registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks())
-
         val fontRequest = FontRequest(
                 "com.google.android.gms.fonts",
                 "com.google.android.gms",
                 "Noto Color Emoji Compat",
                 R.array.com_google_android_gms_fonts_certs
         )
-
-        FontsContractCompat.requestFont(this, fontRequest, koin.koinContext.get(), getFontThreadHandler())
-
+        FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
         vectorConfiguration.initConfiguration()
-
         NotificationUtils.createNotificationChannels(applicationContext)
-
+        if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
+            val lastAuthenticatedSession = authenticator.getLastAuthenticatedSession()!!
+            activeSessionHolder.setActiveSession(lastAuthenticatedSession)
+            lastAuthenticatedSession.configureAndStart(pushRuleTriggerListener)
+        }
         ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver {
 
             @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
             fun entersForeground() {
                 FcmHelper.onEnterForeground(appContext)
-                Matrix.getInstance().currentSession?.also {
+                activeSessionHolder.getSafeActiveSession()?.also {
                     it.stopAnyBackgroundSync()
                 }
             }
@@ -124,20 +120,18 @@ class VectorApplication : Application() {
             @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
             fun entersBackground() {
                 Timber.i("App entered background") // call persistInfo
-
                 notificationDrawerManager.persistInfo()
-
-                FcmHelper.onEnterBackground(appContext, Matrix.getInstance().currentSession != null)
+                FcmHelper.onEnterBackground(appContext, activeSessionHolder)
             }
         })
+    }
 
+    override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
 
-        Matrix.getInstance().currentSession?.let {
-            it.refreshPushers()
-            //bind to the sync service
-            get().startWithSession(it)
-            it.fetchPushRules()
-        }
+    override fun getWorkManagerConfiguration() = androidx.work.Configuration.Builder().build()
+
+    override fun injector(): VectorComponent {
+        return vectorComponent
     }
 
     private fun logInfo() {
@@ -165,12 +159,15 @@ class VectorApplication : Application() {
     }
 
     private fun getFontThreadHandler(): Handler {
-        if (mFontThreadHandler == null) {
-            val handlerThread = HandlerThread("fonts")
-            handlerThread.start()
-            mFontThreadHandler = Handler(handlerThread.looper)
+        return fontThreadHandler ?: createFontThreadHandler().also {
+            fontThreadHandler = it
         }
-        return mFontThreadHandler!!
+    }
+
+    private fun createFontThreadHandler(): Handler {
+        val handlerThread = HandlerThread("fonts")
+        handlerThread.start()
+        return Handler(handlerThread.looper)
     }
 
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ActiveSessionHolder.kt b/vector/src/main/java/im/vector/riotredesign/core/di/ActiveSessionHolder.kt
new file mode 100644
index 00000000..b02934d8
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ActiveSessionHolder.kt
@@ -0,0 +1,61 @@
+/*
+ *
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *  
+ */
+
+package im.vector.riotredesign.core.di
+
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.matrix.android.api.auth.data.SessionParams
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.sync.FilterService
+import java.lang.IllegalStateException
+import java.util.concurrent.atomic.AtomicReference
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ActiveSessionHolder @Inject constructor(private val authenticator: Authenticator) {
+
+    private var activeSession: AtomicReference = AtomicReference()
+
+    fun setActiveSession(session: Session){
+        activeSession.set(session)
+    }
+
+    fun clearActiveSession(){
+        activeSession.set(null)
+    }
+
+    fun hasActiveSession(): Boolean {
+        return activeSession.get() != null
+    }
+
+    fun getSafeActiveSession(): Session? {
+        return activeSession.get()
+    }
+
+    fun getActiveSession(): Session {
+        return activeSession.get() ?: throw IllegalStateException("You should authenticate before using this")
+    }
+
+    //TODO: Stop sync ?
+    fun switchToSession(sessionParams: SessionParams) {
+        val newActiveSession = authenticator.getSession(sessionParams)
+        activeSession.set(newActiveSession)
+    }
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt
deleted file mode 100644
index 85f0d3a5..00000000
--- a/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2019 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.riotredesign.core.di
-
-import android.content.Context
-import android.content.Context.MODE_PRIVATE
-import im.vector.matrix.android.api.Matrix
-import im.vector.riotredesign.EmojiCompatFontProvider
-import im.vector.riotredesign.core.error.ErrorFormatter
-import im.vector.riotredesign.core.pushers.PushersManager
-import im.vector.riotredesign.core.resources.AppNameProvider
-import im.vector.riotredesign.core.resources.LocaleProvider
-import im.vector.riotredesign.core.resources.StringArrayProvider
-import im.vector.riotredesign.core.resources.StringProvider
-import im.vector.riotredesign.features.configuration.VectorConfiguration
-import im.vector.riotredesign.features.crypto.keysrequest.KeyRequestHandler
-import im.vector.riotredesign.features.crypto.verification.IncomingVerificationRequestHandler
-import im.vector.riotredesign.features.home.HomeRoomListObservableStore
-import im.vector.riotredesign.features.home.group.SelectedGroupStore
-import im.vector.riotredesign.features.home.room.list.AlphabeticalRoomComparator
-import im.vector.riotredesign.features.home.room.list.ChronologicalRoomComparator
-import im.vector.riotredesign.features.navigation.DefaultNavigator
-import im.vector.riotredesign.features.navigation.Navigator
-import im.vector.riotredesign.features.notifications.NotifiableEventResolver
-import im.vector.riotredesign.features.notifications.NotificationDrawerManager
-import im.vector.riotredesign.features.notifications.OutdatedEventDetector
-import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
-import org.koin.dsl.module.module
-
-class AppModule(private val context: Context) {
-
-    val definition = module {
-
-        single {
-            VectorConfiguration(context)
-        }
-
-        single {
-            LocaleProvider(context.resources)
-        }
-
-        single {
-            StringProvider(context.resources)
-        }
-
-        single {
-            StringArrayProvider(context.resources)
-        }
-
-        single {
-            context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
-        }
-
-        single {
-            SelectedGroupStore()
-        }
-
-        single {
-            HomeRoomListObservableStore()
-        }
-
-        single {
-            ChronologicalRoomComparator()
-        }
-
-        single {
-            AlphabeticalRoomComparator()
-        }
-
-        single {
-            ErrorFormatter(get())
-        }
-
-        single {
-            PushRuleTriggerListener(get(), get())
-        }
-
-        single {
-            OutdatedEventDetector()
-        }
-
-        single {
-            NotificationDrawerManager(context, get())
-        }
-
-        single {
-            NotifiableEventResolver(get(), get())
-        }
-
-        factory {
-            Matrix.getInstance().currentSession!!
-        }
-
-        single {
-            KeyRequestHandler(context, get())
-        }
-
-        single {
-            IncomingVerificationRequestHandler(context, get())
-        }
-
-        factory {
-            DefaultNavigator() as Navigator
-        }
-
-        single {
-            EmojiCompatFontProvider()
-        }
-
-        single {
-            AppNameProvider(context)
-        }
-
-        single {
-            PushersManager(get(), get(), get(), get())
-        }
-    }
-}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/HasScreenInjector.kt b/vector/src/main/java/im/vector/riotredesign/core/di/HasScreenInjector.kt
new file mode 100644
index 00000000..746c0c18
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/HasScreenInjector.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+interface HasScreenInjector {
+
+    fun injector(): ScreenComponent
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/HasVectorInjector.kt b/vector/src/main/java/im/vector/riotredesign/core/di/HasVectorInjector.kt
new file mode 100644
index 00000000..e8301c73
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/HasVectorInjector.kt
@@ -0,0 +1,25 @@
+/*
+ *
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package im.vector.riotredesign.core.di
+
+interface HasVectorInjector {
+
+    fun injector(): VectorComponent
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenComponent.kt
new file mode 100644
index 00000000..f20ddc5d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenComponent.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.ViewModelProvider
+import dagger.BindsInstance
+import dagger.Component
+import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
+import im.vector.matrix.android.api.session.Session
+import im.vector.riotredesign.core.preference.UserAvatarPreference
+import im.vector.riotredesign.features.MainActivity
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyFragment
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSuccessFragment
+import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupManageActivity
+import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
+import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupStep1Fragment
+import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupStep2Fragment
+import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupStep3Fragment
+import im.vector.riotredesign.features.crypto.verification.SASVerificationIncomingFragment
+import im.vector.riotredesign.features.home.*
+import im.vector.riotredesign.features.home.group.GroupListFragment
+import im.vector.riotredesign.features.home.room.detail.RoomDetailFragment
+import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageActionsBottomSheet
+import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageMenuFragment
+import im.vector.riotredesign.features.home.room.detail.timeline.action.QuickReactionFragment
+import im.vector.riotredesign.features.home.room.detail.timeline.action.ViewReactionBottomSheet
+import im.vector.riotredesign.features.home.room.list.RoomListFragment
+import im.vector.riotredesign.features.invite.VectorInviteView
+import im.vector.riotredesign.features.login.LoginActivity
+import im.vector.riotredesign.features.media.ImageMediaViewerActivity
+import im.vector.riotredesign.features.media.VideoMediaViewerActivity
+import im.vector.riotredesign.features.rageshake.BugReportActivity
+import im.vector.riotredesign.features.rageshake.BugReporter
+import im.vector.riotredesign.features.rageshake.RageShake
+import im.vector.riotredesign.features.reactions.EmojiReactionPickerActivity
+import im.vector.riotredesign.features.roomdirectory.PublicRoomsFragment
+import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
+import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomFragment
+import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
+import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
+import im.vector.riotredesign.features.settings.VectorSettingsActivity
+import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
+import im.vector.riotredesign.features.settings.VectorSettingsNotificationsTroubleshootFragment
+import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment
+
+@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
+@ScreenScope
+interface ScreenComponent {
+
+    fun session(): Session
+
+    fun viewModelFactory(): ViewModelProvider.Factory
+
+    fun bugReporter(): BugReporter
+
+    fun rageShake(): RageShake
+
+    fun inject(activity: HomeActivity)
+
+    fun inject(roomDetailFragment: RoomDetailFragment)
+
+    fun inject(roomListFragment: RoomListFragment)
+
+    fun inject(groupListFragment: GroupListFragment)
+
+    fun inject(roomDirectoryPickerFragment: RoomDirectoryPickerFragment)
+
+    fun inject(roomPreviewNoPreviewFragment: RoomPreviewNoPreviewFragment)
+
+    fun inject(keysBackupSettingsFragment: KeysBackupSettingsFragment)
+
+    fun inject(homeDrawerFragment: HomeDrawerFragment)
+
+    fun inject(homeDetailFragment: HomeDetailFragment)
+
+    fun inject(messageActionsBottomSheet: MessageActionsBottomSheet)
+
+    fun inject(viewReactionBottomSheet: ViewReactionBottomSheet)
+
+    fun inject(messageMenuFragment: MessageMenuFragment)
+
+    fun inject(vectorSettingsActivity: VectorSettingsActivity)
+
+    fun inject(createRoomFragment: CreateRoomFragment)
+
+    fun inject(keysBackupManageActivity: KeysBackupManageActivity)
+
+    fun inject(keysBackupRestoreFromKeyFragment: KeysBackupRestoreFromKeyFragment)
+
+    fun inject(keysBackupRestoreFromPassphraseFragment: KeysBackupRestoreFromPassphraseFragment)
+
+    fun inject(keysBackupRestoreSuccessFragment: KeysBackupRestoreSuccessFragment)
+
+    fun inject(keysBackupSetupStep1Fragment: KeysBackupSetupStep1Fragment)
+
+    fun inject(keysBackupSetupStep2Fragment: KeysBackupSetupStep2Fragment)
+
+    fun inject(keysBackupSetupStep3Fragment: KeysBackupSetupStep3Fragment)
+
+    fun inject(publicRoomsFragment: PublicRoomsFragment)
+
+    fun inject(sasVerificationIncomingFragment: SASVerificationIncomingFragment)
+
+    fun inject(quickReactionFragment: QuickReactionFragment)
+
+    fun inject(emojiReactionPickerActivity: EmojiReactionPickerActivity)
+
+    fun inject(loginActivity: LoginActivity)
+
+    fun inject(mainActivity: MainActivity)
+
+    fun inject(roomDirectoryActivity: RoomDirectoryActivity)
+
+    fun inject(bugReportActivity: BugReportActivity)
+
+    fun inject(imageMediaViewerActivity: ImageMediaViewerActivity)
+
+    fun inject(vectorInviteView: VectorInviteView)
+
+    fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)
+
+    fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)
+
+    fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
+
+    fun inject(userAvatarPreference: UserAvatarPreference)
+
+    fun inject(vectorSettingsNotificationsTroubleshootFragment: VectorSettingsNotificationsTroubleshootFragment)
+
+    @Component.Factory
+    interface Factory {
+        fun create(vectorComponent: VectorComponent,
+                   @BindsInstance context: AppCompatActivity
+        ): ScreenComponent
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ScreenModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenModule.kt
new file mode 100644
index 00000000..3ea64332
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import androidx.appcompat.app.AppCompatActivity
+import dagger.Module
+import dagger.Provides
+import im.vector.riotredesign.core.glide.GlideApp
+
+@Module
+object ScreenModule {
+
+    @Provides
+    @JvmStatic
+    fun providesGlideRequests(context: AppCompatActivity) = GlideApp.with(context)
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ScreenScope.java b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenScope.java
new file mode 100644
index 00000000..d3915f7d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ScreenScope.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Scope
+@Documented
+@Retention(RUNTIME)
+public @interface ScreenScope {}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/VectorAssistedModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/VectorAssistedModule.kt
new file mode 100644
index 00000000..bbf37bfe
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/VectorAssistedModule.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+/*
+@Module(includes = [AssistedInject_VectorAssistedModule::class])
+@AssistedModule
+class VectorAssistedModule*/
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/riotredesign/core/di/VectorComponent.kt
new file mode 100644
index 00000000..ffc3a126
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/VectorComponent.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import android.content.Context
+import android.content.res.Resources
+import dagger.BindsInstance
+import dagger.Component
+import im.vector.matrix.android.api.Matrix
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.matrix.android.api.session.Session
+import im.vector.riotredesign.EmojiCompatFontProvider
+import im.vector.riotredesign.VectorApplication
+import im.vector.riotredesign.core.pushers.PushersManager
+import im.vector.riotredesign.features.configuration.VectorConfiguration
+import im.vector.riotredesign.features.crypto.keysrequest.KeyRequestHandler
+import im.vector.riotredesign.features.crypto.verification.IncomingVerificationRequestHandler
+import im.vector.riotredesign.features.home.AvatarRenderer
+import im.vector.riotredesign.features.home.HomeNavigator
+import im.vector.riotredesign.features.home.HomeRoomListObservableStore
+import im.vector.riotredesign.features.home.group.SelectedGroupStore
+import im.vector.riotredesign.features.navigation.Navigator
+import im.vector.riotredesign.features.notifications.NotifiableEventResolver
+import im.vector.riotredesign.features.notifications.NotificationBroadcastReceiver
+import im.vector.riotredesign.features.notifications.NotificationDrawerManager
+import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
+import im.vector.riotredesign.features.rageshake.BugReporter
+import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
+import javax.inject.Singleton
+
+@Component(modules = [VectorModule::class])
+@Singleton
+interface VectorComponent {
+
+    fun inject(vectorApplication: NotificationBroadcastReceiver)
+
+    fun inject(vectorApplication: VectorApplication)
+
+    fun matrix(): Matrix
+
+    fun currentSession(): Session
+
+    fun notificationDrawerManager(): NotificationDrawerManager
+
+    fun appContext(): Context
+
+    fun resources(): Resources
+
+    fun vectorConfiguration(): VectorConfiguration
+
+    fun avatarRenderer(): AvatarRenderer
+
+    fun activeSessionHolder(): ActiveSessionHolder
+
+    fun emojiCompatFontProvider(): EmojiCompatFontProvider
+
+    fun navigator(): Navigator
+
+    fun homeNavigator(): HomeNavigator
+
+    fun homeRoomListObservableStore(): HomeRoomListObservableStore
+
+    fun selectedGroupStore(): SelectedGroupStore
+
+    fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
+
+    fun incomingKeyRequestHandler(): KeyRequestHandler
+
+    fun authenticator(): Authenticator
+
+    fun bugReporter(): BugReporter
+
+    fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler
+
+    fun pushRuleTriggerListener(): PushRuleTriggerListener
+
+    fun pusherManager(): PushersManager
+
+    fun notifiableEventResolver(): NotifiableEventResolver
+
+    @Component.Factory
+    interface Factory {
+        fun create(@BindsInstance context: Context): VectorComponent
+    }
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/VectorModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/VectorModule.kt
new file mode 100644
index 00000000..b1c6c7e7
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/VectorModule.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import android.content.Context
+import android.content.Context.MODE_PRIVATE
+import android.content.SharedPreferences
+import android.content.res.Resources
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import im.vector.matrix.android.api.Matrix
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.matrix.android.api.session.Session
+import im.vector.riotredesign.features.navigation.DefaultNavigator
+import im.vector.riotredesign.features.navigation.Navigator
+
+@Module
+abstract class VectorModule {
+
+    @Module
+    companion object {
+
+        @Provides
+        @JvmStatic
+        fun providesResources(context: Context): Resources {
+            return context.resources
+        }
+
+        @Provides
+        @JvmStatic
+        fun providesSharedPreferences(context: Context): SharedPreferences {
+            return context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
+        }
+
+        @Provides
+        @JvmStatic
+        fun providesMatrix(context: Context): Matrix {
+            return Matrix.getInstance(context)
+        }
+
+        @Provides
+        @JvmStatic
+        fun providesCurrentSession(activeSessionHolder: ActiveSessionHolder): Session {
+            //TODO: handle session injection better
+            return activeSessionHolder.getActiveSession()
+        }
+
+        @Provides
+        @JvmStatic
+        fun providesAuthenticator(matrix: Matrix): Authenticator{
+            return matrix.authenticator()
+        }
+    }
+
+    @Binds
+    abstract fun bindNavigator(navigator: DefaultNavigator): Navigator
+
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/VectorViewModelFactory.kt b/vector/src/main/java/im/vector/riotredesign/core/di/VectorViewModelFactory.kt
new file mode 100644
index 00000000..eac1b52e
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/VectorViewModelFactory.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * ViewModelFactory which uses Dagger to create the instances.
+ */
+class VectorViewModelFactory @Inject constructor(
+        private val creators: @JvmSuppressWildcards Map, Provider>
+) : ViewModelProvider.Factory {
+    override fun  create(modelClass: Class): T {
+        var creator: Provider? = creators[modelClass]
+        if (creator == null) {
+            for ((key, value) in creators) {
+                if (modelClass.isAssignableFrom(key)) {
+                    creator = value
+                    break
+                }
+            }
+        }
+        if (creator == null) {
+            throw IllegalArgumentException("Unknown model class: $modelClass")
+        }
+        try {
+            @Suppress("UNCHECKED_CAST")
+            return creator.get() as T
+        } catch (e: Exception) {
+            throw RuntimeException(e)
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelKey.kt b/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelKey.kt
new file mode 100644
index 00000000..c0887423
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelKey.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import androidx.lifecycle.ViewModel
+import dagger.MapKey
+import kotlin.reflect.KClass
+
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
+@Retention(AnnotationRetention.RUNTIME)
+@MapKey
+annotation class ViewModelKey(val value: KClass)
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelModule.kt
new file mode 100644
index 00000000..df984d23
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/di/ViewModelModule.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.di
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import im.vector.riotredesign.core.platform.ConfigurationViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel_AssistedFactory
+import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
+import im.vector.riotredesign.features.crypto.verification.SasVerificationViewModel
+import im.vector.riotredesign.features.home.HomeActivityViewModel
+import im.vector.riotredesign.features.home.HomeActivityViewModel_AssistedFactory
+import im.vector.riotredesign.features.home.HomeDetailViewModel
+import im.vector.riotredesign.features.home.HomeDetailViewModel_AssistedFactory
+import im.vector.riotredesign.features.home.HomeNavigationViewModel
+import im.vector.riotredesign.features.home.group.GroupListViewModel
+import im.vector.riotredesign.features.home.group.GroupListViewModel_AssistedFactory
+import im.vector.riotredesign.features.home.room.detail.RoomDetailViewModel
+import im.vector.riotredesign.features.home.room.detail.RoomDetailViewModel_AssistedFactory
+import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewModel
+import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewModel_AssistedFactory
+import im.vector.riotredesign.features.home.room.detail.timeline.action.*
+import im.vector.riotredesign.features.home.room.list.RoomListViewModel
+import im.vector.riotredesign.features.home.room.list.RoomListViewModel_AssistedFactory
+import im.vector.riotredesign.features.reactions.EmojiChooserViewModel
+import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
+import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
+import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel_AssistedFactory
+import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomViewModel
+import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomViewModel_AssistedFactory
+import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerViewModel
+import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerViewModel_AssistedFactory
+import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewViewModel
+import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewViewModel_AssistedFactory
+import im.vector.riotredesign.features.workers.signout.SignOutViewModel
+
+@Module
+interface ViewModelModule {
+    
+
+    @Binds
+    fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(SignOutViewModel::class)
+    fun bindSignOutViewModel(viewModel: SignOutViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(EmojiChooserViewModel::class)
+    fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(SasVerificationViewModel::class)
+    fun bindSasVerificationViewModel(viewModel: SasVerificationViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
+    fun bindKeysBackupRestoreFromKeyViewModel(viewModel: KeysBackupRestoreFromKeyViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(KeysBackupRestoreSharedViewModel::class)
+    fun bindKeysBackupRestoreSharedViewModel(viewModel: KeysBackupRestoreSharedViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(KeysBackupRestoreFromPassphraseViewModel::class)
+    fun bindKeysBackupRestoreFromPassphraseViewModel(viewModel: KeysBackupRestoreFromPassphraseViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(RoomDirectoryNavigationViewModel::class)
+    fun bindRoomDirectoryNavigationViewModel(viewModel: RoomDirectoryNavigationViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(HomeNavigationViewModel::class)
+    fun bindHomeNavigationViewModel(viewModel: HomeNavigationViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(KeysBackupSetupSharedViewModel::class)
+    fun bindKeysBackupSetupSharedViewModel(viewModel: KeysBackupSetupSharedViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(ConfigurationViewModel::class)
+    fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
+
+    @Binds
+    fun bindHomeActivityViewModelFactory(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory
+
+    @Binds
+    fun bindTextComposerViewModelFactory(factory: TextComposerViewModel_AssistedFactory): TextComposerViewModel.Factory
+
+    @Binds
+    fun bindRoomDetailViewModelFactory(factory: RoomDetailViewModel_AssistedFactory): RoomDetailViewModel.Factory
+
+    @Binds
+    fun bindQuickReactionViewModelFactory(factory: QuickReactionViewModel_AssistedFactory): QuickReactionViewModel.Factory
+
+    @Binds
+    fun bindMessageActionsViewModelFactory(factory: MessageActionsViewModel_AssistedFactory): MessageActionsViewModel.Factory
+
+    @Binds
+    fun bindMessageMenuViewModelFactory(factory: MessageMenuViewModel_AssistedFactory): MessageMenuViewModel.Factory
+
+    @Binds
+    fun bindRoomListViewModelFactory(factory: RoomListViewModel_AssistedFactory): RoomListViewModel.Factory
+
+    @Binds
+    fun bindGroupListViewModelFactory(factory: GroupListViewModel_AssistedFactory): GroupListViewModel.Factory
+
+    @Binds
+    fun bindHomeDetailViewModelFactory(factory: HomeDetailViewModel_AssistedFactory): HomeDetailViewModel.Factory
+
+    @Binds
+    fun bindKeysBackupSettingsViewModelFactory(factory: KeysBackupSettingsViewModel_AssistedFactory): KeysBackupSettingsViewModel.Factory
+
+    @Binds
+    fun bindRoomDirectoryPickerViewModelFactory(factory: RoomDirectoryPickerViewModel_AssistedFactory): RoomDirectoryPickerViewModel.Factory
+
+    @Binds
+    fun bindRoomDirectoryViewModelFactory(factory: RoomDirectoryViewModel_AssistedFactory): RoomDirectoryViewModel.Factory
+
+    @Binds
+    fun bindRoomPreviewViewModelFactory(factory: RoomPreviewViewModel_AssistedFactory): RoomPreviewViewModel.Factory
+
+    @Binds
+    fun bindViewReactionViewModelFactory(factory: ViewReactionViewModel_AssistedFactory): ViewReactionViewModel.Factory
+
+    @Binds
+    fun bindCreateRoomViewModelFactory(factory: CreateRoomViewModel_AssistedFactory): CreateRoomViewModel.Factory
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/riotredesign/core/error/ErrorFormatter.kt
index 114e08b8..284bd192 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/error/ErrorFormatter.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/error/ErrorFormatter.kt
@@ -19,8 +19,9 @@ package im.vector.riotredesign.core.error
 import im.vector.matrix.android.api.failure.Failure
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.resources.StringProvider
+import javax.inject.Inject
 
-class ErrorFormatter(val stringProvider: StringProvider) {
+class ErrorFormatter @Inject constructor(val stringProvider: StringProvider) {
 
 
     fun toHumanReadable(failure: Failure): String {
diff --git a/vector/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt b/vector/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt
index 8c23d795..78796d5e 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt
@@ -33,4 +33,4 @@ fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, t
 
 fun AppCompatActivity.hideKeyboard() {
     currentFocus?.hideKeyboard()
-}
\ No newline at end of file
+}
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/KeysBackupModule.kt b/vector/src/main/java/im/vector/riotredesign/core/extensions/Context.kt
similarity index 51%
rename from vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/KeysBackupModule.kt
rename to vector/src/main/java/im/vector/riotredesign/core/extensions/Context.kt
index c9cebfc3..f9b30a47 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/KeysBackupModule.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/extensions/Context.kt
@@ -5,7 +5,7 @@
  * 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
+ * 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,
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-package im.vector.riotredesign.features.crypto.keysbackup
+package im.vector.riotredesign.core.extensions
 
-import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsRecyclerViewController
-import org.koin.dsl.module.module
-
-class KeysBackupModule {
-
-    companion object {
-        const val KEYS_BACKUP_SCOPE = "KEYS_BACKUP_SCOPE"
-    }
-
-    val definition = module(override = true) {
-
-        scope(KEYS_BACKUP_SCOPE) {
-            KeysBackupSettingsRecyclerViewController(get(), get())
-        }
+import android.content.Context
+import im.vector.riotredesign.core.di.HasVectorInjector
+import im.vector.riotredesign.core.di.VectorComponent
 
+fun Context.vectorComponent(): VectorComponent {
+    val appContext = applicationContext
+    if (appContext is HasVectorInjector) {
+        return appContext.injector()
+    } else {
+        throw IllegalStateException("Your application context doesn't implement HasVectorInjector")
     }
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/riotredesign/core/extensions/Fragment.kt
index 1467071b..e0eb4bac 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/extensions/Fragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/extensions/Fragment.kt
@@ -40,4 +40,4 @@ fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
 
 fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
     childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
-}
\ No newline at end of file
+}
diff --git a/vector/src/main/java/im/vector/riotredesign/core/extensions/Session.kt b/vector/src/main/java/im/vector/riotredesign/core/extensions/Session.kt
new file mode 100644
index 00000000..cc7ca459
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/extensions/Session.kt
@@ -0,0 +1,32 @@
+/*
+ *
+ *  * Copyright 2019 New Vector Ltd
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package im.vector.riotredesign.core.extensions
+
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.sync.FilterService
+import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
+
+fun Session.configureAndStart(pushRuleTriggerListener: PushRuleTriggerListener) {
+    open()
+    setFilter(FilterService.FilterPreset.RiotFilter)
+    startSync()
+    refreshPushers()
+    pushRuleTriggerListener.startWithSession(this)
+    fetchPushRules()
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/ConfigurationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/ConfigurationViewModel.kt
index af7d0e5d..24fa889b 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/platform/ConfigurationViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/platform/ConfigurationViewModel.kt
@@ -21,13 +21,12 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import im.vector.riotredesign.core.utils.LiveEvent
 import im.vector.riotredesign.features.configuration.VectorConfiguration
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
 import timber.log.Timber
+import javax.inject.Inject
 
-class ConfigurationViewModel : ViewModel(), KoinComponent {
-
-    private val vectorConfiguration: VectorConfiguration by inject()
+class ConfigurationViewModel @Inject constructor(
+        private val vectorConfiguration: VectorConfiguration
+) : ViewModel() {
 
     private var currentConfigurationValue: String? = null
 
@@ -47,7 +46,6 @@ class ConfigurationViewModel : ViewModel(), KoinComponent {
             if (newHash != currentConfigurationValue) {
                 Timber.v("Configuration: recreate the Activity")
                 currentConfigurationValue = newHash
-
                 _activityRestarter.postValue(LiveEvent(Unit))
             }
         }
diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/SimpleFragmentActivity.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/SimpleFragmentActivity.kt
index dfac10b5..b02b2cb8 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/platform/SimpleFragmentActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/platform/SimpleFragmentActivity.kt
@@ -15,6 +15,7 @@
  */
 package im.vector.riotredesign.core.platform
 
+import android.os.Bundle
 import android.view.View
 import android.widget.ProgressBar
 import android.widget.TextView
@@ -23,9 +24,10 @@ import androidx.core.view.isVisible
 import butterknife.BindView
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.hideKeyboard
 import kotlinx.android.synthetic.main.activity.*
-import org.koin.android.ext.android.get
+import javax.inject.Inject
 
 /**
  * Simple activity with a toolbar, a waiting overlay, and a fragment container and a session.
@@ -43,7 +45,11 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
     @BindView(R.id.waiting_view_status_horizontal_progress)
     lateinit var waitingHorizontalProgress: ProgressBar
 
-    protected val session = get()
+    @Inject lateinit var session: Session
+
+    override fun injectWith(injector: ScreenComponent) {
+        session = injector.session()
+    }
 
     override fun initUiAndData() {
         configureToolbar(toolbar)
diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt
index 8700e236..d966a0e4 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt
@@ -22,11 +22,17 @@ import android.os.Bundle
 import android.view.Menu
 import android.view.MenuItem
 import android.view.View
-import androidx.annotation.*
+import androidx.annotation.AttrRes
+import androidx.annotation.LayoutRes
+import androidx.annotation.MainThread
+import androidx.annotation.MenuRes
+import androidx.annotation.Nullable
+import androidx.annotation.StringRes
 import androidx.appcompat.widget.Toolbar
 import androidx.coordinatorlayout.widget.CoordinatorLayout
 import androidx.core.view.isVisible
 import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProviders
 import butterknife.BindView
 import butterknife.ButterKnife
@@ -36,6 +42,7 @@ import com.bumptech.glide.util.Util
 import com.google.android.material.snackbar.Snackbar
 import im.vector.riotredesign.BuildConfig
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.*
 import im.vector.riotredesign.core.utils.toast
 import im.vector.riotredesign.features.configuration.VectorConfiguration
 import im.vector.riotredesign.features.rageshake.BugReportActivity
@@ -46,11 +53,11 @@ import im.vector.riotredesign.features.themes.ThemeUtils
 import im.vector.riotredesign.receivers.DebugReceiver
 import io.reactivex.disposables.CompositeDisposable
 import io.reactivex.disposables.Disposable
-import org.koin.android.ext.android.inject
 import timber.log.Timber
+import kotlin.system.measureTimeMillis
 
 
-abstract class VectorBaseActivity : BaseMvRxActivity() {
+abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
     /* ==========================================================================================
      * UI
      * ========================================================================================== */
@@ -64,9 +71,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
      * DATA
      * ========================================================================================== */
 
-    private val vectorConfiguration: VectorConfiguration by inject()
-
+    protected lateinit var viewModelFactory: ViewModelProvider.Factory
     private lateinit var configurationViewModel: ConfigurationViewModel
+    protected lateinit var bugReporter: BugReporter
+    private lateinit var rageShake: RageShake
 
     private var unBinder: Unbinder? = null
 
@@ -78,9 +86,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
     private val uiDisposables = CompositeDisposable()
     private val restorables = ArrayList()
 
-    private var rageShake: RageShake? = null
+    private lateinit var screenComponent: ScreenComponent
 
     override fun attachBaseContext(base: Context) {
+        val vectorConfiguration = VectorConfiguration(this)
         super.attachBaseContext(vectorConfiguration.getLocalisedContext(base))
     }
 
@@ -107,10 +116,16 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
     }
 
     override fun onCreate(savedInstanceState: Bundle?) {
+        screenComponent = DaggerScreenComponent.factory().create(getVectorComponent(), this)
         super.onCreate(savedInstanceState)
-
-        configurationViewModel = ViewModelProviders.of(this).get(ConfigurationViewModel::class.java)
-
+        val timeForInjection = measureTimeMillis {
+            injectWith(screenComponent)
+        }
+        Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms")
+        viewModelFactory = screenComponent.viewModelFactory()
+        configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
+        bugReporter = screenComponent.bugReporter()
+        rageShake = screenComponent.rageShake()
         configurationViewModel.activityRestarter.observe(this, Observer {
             if (!it.hasBeenHandled) {
                 // Recreate the Activity because configuration has changed
@@ -120,7 +135,6 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
         })
 
         // Shake detector
-        rageShake = RageShake(this)
 
         ThemeUtils.setActivityTheme(this, getOtherThemes())
 
@@ -198,14 +212,24 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
         super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
 
         Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: $isInMultiWindowMode")
-        BugReporter.inMultiWindowMode = isInMultiWindowMode
+        bugReporter.inMultiWindowMode = isInMultiWindowMode
     }
 
 
+    override fun injector(): ScreenComponent {
+        return screenComponent
+    }
+
+    protected open fun injectWith(injector: ScreenComponent) = Unit
+
     /* ==========================================================================================
      * PRIVATE METHODS
      * ========================================================================================== */
 
+    internal fun getVectorComponent(): VectorComponent {
+        return (application as HasVectorInjector).injector()
+    }
+
     /**
      * Force to render the activity in fullscreen
      */
diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt
index f291746e..d0f8214e 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseFragment.kt
@@ -16,26 +16,33 @@
 
 package im.vector.riotredesign.core.platform
 
+import android.content.Context
 import android.os.Bundle
 import android.os.Parcelable
-import android.view.*
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.View
+import android.view.ViewGroup
 import androidx.annotation.CallSuper
 import androidx.annotation.LayoutRes
 import androidx.annotation.MainThread
 import androidx.appcompat.widget.Toolbar
+import androidx.lifecycle.ViewModelProvider
 import butterknife.ButterKnife
 import butterknife.Unbinder
 import com.airbnb.mvrx.BaseMvRxFragment
 import com.airbnb.mvrx.MvRx
 import com.bumptech.glide.util.Util.assertMainThread
+import im.vector.riotredesign.core.di.DaggerScreenComponent
+import im.vector.riotredesign.core.di.HasScreenInjector
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.features.navigation.Navigator
 import io.reactivex.disposables.CompositeDisposable
 import io.reactivex.disposables.Disposable
-import org.koin.android.ext.android.inject
-import org.koin.core.parameter.parametersOf
 import timber.log.Timber
 
-abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
+abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed, HasScreenInjector {
 
     // Butterknife unbinder
     private var mUnBinder: Unbinder? = null
@@ -48,16 +55,27 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
      * Navigator
      * ========================================================================================== */
 
-    protected val navigator: Navigator  by inject { parametersOf(this) }
+    protected lateinit var viewModelFactory: ViewModelProvider.Factory
+    protected lateinit var navigator: Navigator
+    private lateinit var screenComponent: ScreenComponent
 
     /* ==========================================================================================
      * Life cycle
      * ========================================================================================== */
 
+    override fun onAttach(context: Context) {
+        screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
+        super.onAttach(context)
+        navigator = vectorBaseActivity.getVectorComponent().navigator()
+        viewModelFactory = screenComponent.viewModelFactory()
+        injectWith(injector())
+    }
+
+    protected open fun injectWith(injector: ScreenComponent) = Unit
+
     @CallSuper
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
         if (getMenuRes() != -1) {
             setHasOptionsMenu(true)
         }
@@ -73,7 +91,6 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
     @CallSuper
     override fun onResume() {
         super.onResume()
-
         Timber.v("onResume Fragment ${this.javaClass.simpleName}")
     }
 
@@ -92,10 +109,13 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
 
     override fun onDestroy() {
         super.onDestroy()
-
         uiDisposables.dispose()
     }
 
+    override fun injector(): ScreenComponent {
+        return screenComponent
+    }
+
     /* ==========================================================================================
      * Restorable
      * ========================================================================================== */
diff --git a/vector/src/main/java/im/vector/riotredesign/core/preference/UserAvatarPreference.kt b/vector/src/main/java/im/vector/riotredesign/core/preference/UserAvatarPreference.kt
index 79969ffa..4acd8265 100755
--- a/vector/src/main/java/im/vector/riotredesign/core/preference/UserAvatarPreference.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/preference/UserAvatarPreference.kt
@@ -24,6 +24,7 @@ import androidx.preference.Preference
 import androidx.preference.PreferenceViewHolder
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.extensions.vectorComponent
 import im.vector.riotredesign.features.home.AvatarRenderer
 
 open class UserAvatarPreference : Preference {
@@ -32,6 +33,8 @@ open class UserAvatarPreference : Preference {
     internal var mSession: Session? = null
     private var mLoadingProgressBar: ProgressBar? = null
 
+    private var avatarRenderer: AvatarRenderer = context.vectorComponent().avatarRenderer()
+
     constructor(context: Context) : super(context)
 
     constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
@@ -55,11 +58,10 @@ open class UserAvatarPreference : Preference {
     open fun refreshAvatar() {
         val session = mSession ?: return
         val view = mAvatarView ?: return
-
         session.getUser(session.sessionParams.credentials.userId)?.let {
-            AvatarRenderer.render(it, view)
+            avatarRenderer.render(it, view)
         } ?: run {
-            AvatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
+            avatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
         }
 
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/riotredesign/core/pushers/PushersManager.kt
index 469057d0..cfc77576 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/pushers/PushersManager.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/pushers/PushersManager.kt
@@ -1,24 +1,26 @@
 package im.vector.riotredesign.core.pushers
 
 import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.resources.AppNameProvider
 import im.vector.riotredesign.core.resources.LocaleProvider
 import im.vector.riotredesign.core.resources.StringProvider
 import java.util.*
+import javax.inject.Inject
 
 private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
 
-class PushersManager(
-        private val currentSession: Session,
+class PushersManager @Inject constructor(
+        private val activeSessionHolder: ActiveSessionHolder,
         private val localeProvider: LocaleProvider,
         private val stringProvider: StringProvider,
         private val appNameProvider: AppNameProvider
 ) {
 
-    fun registerPusherWithFcmKey(pushKey: String) : UUID {
-        var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.sessionParams.credentials.userId.hashCode())
+    fun registerPusherWithFcmKey(pushKey: String): UUID {
+        val currentSession = activeSessionHolder.getActiveSession()
+        var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.myUserId.hashCode())
 
         return currentSession.addHttpPusher(
                 pushKey,
@@ -34,6 +36,7 @@ class PushersManager(
     }
 
     fun unregisterPusher(pushKey: String, callback: MatrixCallback) {
-        currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id),callback)
+        val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
+        currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback)
     }
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/AppNameProvider.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/AppNameProvider.kt
index 4adfde79..1c7b9225 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/resources/AppNameProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/resources/AppNameProvider.kt
@@ -2,9 +2,10 @@ package im.vector.riotredesign.core.resources
 
 import android.content.Context
 import timber.log.Timber
+import javax.inject.Inject
 
 
-class AppNameProvider(private val context: Context) {
+class AppNameProvider @Inject constructor(private val context: Context) {
 
     fun getAppName(): String {
         try {
diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/ColorProvider.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/ColorProvider.kt
index cfc48891..88844be4 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/resources/ColorProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/resources/ColorProvider.kt
@@ -18,14 +18,15 @@
 
 package im.vector.riotredesign.core.resources
 
-import android.content.Context
 import androidx.annotation.AttrRes
 import androidx.annotation.ColorInt
 import androidx.annotation.ColorRes
+import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
 import im.vector.riotredesign.features.themes.ThemeUtils
+import javax.inject.Inject
 
-class ColorProvider(private val context: Context) {
+class ColorProvider @Inject constructor(private val context: AppCompatActivity) {
 
     fun getColor(@ColorRes colorRes: Int): Int {
         return ContextCompat.getColor(context, colorRes)
diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/LocaleProvider.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/LocaleProvider.kt
index 8393bc09..17df3d96 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/resources/LocaleProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/resources/LocaleProvider.kt
@@ -19,8 +19,9 @@ package im.vector.riotredesign.core.resources
 import android.content.res.Resources
 import androidx.core.os.ConfigurationCompat
 import java.util.*
+import javax.inject.Inject
 
-class LocaleProvider(private val resources: Resources) {
+class LocaleProvider @Inject constructor(private val resources: Resources) {
 
     fun current(): Locale {
         return ConfigurationCompat.getLocales(resources.configuration)[0]
diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/StringArrayProvider.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/StringArrayProvider.kt
index f8e58d76..fe058ca3 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/resources/StringArrayProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/resources/StringArrayProvider.kt
@@ -19,8 +19,9 @@ package im.vector.riotredesign.core.resources
 import android.content.res.Resources
 import androidx.annotation.ArrayRes
 import androidx.annotation.NonNull
+import javax.inject.Inject
 
-class StringArrayProvider(private val resources: Resources) {
+class StringArrayProvider @Inject constructor(private val resources: Resources) {
 
     /**
      * Returns a localized string array from the application's package's
diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/StringProvider.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/StringProvider.kt
index e6b0452e..f575f354 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/resources/StringProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/resources/StringProvider.kt
@@ -20,8 +20,9 @@ import android.content.res.Resources
 import androidx.annotation.NonNull
 import androidx.annotation.PluralsRes
 import androidx.annotation.StringRes
+import javax.inject.Inject
 
-class StringProvider(private val resources: Resources) {
+class StringProvider @Inject constructor(private val resources: Resources) {
 
     /**
      * Returns a localized string from the application's package's
diff --git a/vector/src/main/java/im/vector/riotredesign/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/riotredesign/core/utils/SystemUtils.kt
index 5c8e3c57..c24ae28c 100644
--- a/vector/src/main/java/im/vector/riotredesign/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/riotredesign/core/utils/SystemUtils.kt
@@ -18,12 +18,17 @@ package im.vector.riotredesign.core.utils
 
 import android.annotation.TargetApi
 import android.app.Activity
-import android.content.*
+import android.content.ActivityNotFoundException
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.content.Intent
 import android.net.Uri
 import android.os.Build
 import android.os.PowerManager
 import android.provider.Settings
 import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
 import androidx.fragment.app.Fragment
 import im.vector.riotredesign.R
 import im.vector.riotredesign.features.notifications.supportNotificationChannels
@@ -44,7 +49,7 @@ import java.util.*
 fun isIgnoringBatteryOptimizations(context: Context): Boolean {
     // no issue before Android M, battery optimisations did not exist
     return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
-            || (context.getSystemService(Context.POWER_SERVICE) as PowerManager?)?.isIgnoringBatteryOptimizations(context.packageName) == true
+           || (context.getSystemService(Context.POWER_SERVICE) as PowerManager?)?.isIgnoringBatteryOptimizations(context.packageName) == true
 }
 
 /**
@@ -109,22 +114,22 @@ fun getDeviceLocale(context: Context): Locale {
  * Shows notification settings for the current app.
  * In android O will directly opens the notification settings, in lower version it will show the App settings
  */
-fun startNotificationSettingsIntent(fragment: Fragment, requestCode: Int) {
+fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: Int) {
     val intent = Intent()
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
         intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
-        intent.putExtra(Settings.EXTRA_APP_PACKAGE, fragment.context?.packageName)
+        intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
     } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
         intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
-        intent.putExtra("app_package", fragment.context?.packageName)
-        intent.putExtra("app_uid", fragment.context?.applicationInfo?.uid)
+        intent.putExtra("app_package", activity.packageName)
+        intent.putExtra("app_uid", activity.applicationInfo?.uid)
     } else {
         intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
         intent.addCategory(Intent.CATEGORY_DEFAULT);
-        val uri = Uri.fromParts("package", fragment.activity?.packageName, null)
+        val uri = Uri.fromParts("package", activity.packageName, null)
         intent.data = uri
     }
-    fragment.startActivityForResult(intent, requestCode)
+    activity.startActivityForResult(intent, requestCode)
 }
 
 /**
@@ -140,13 +145,13 @@ fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String
     fragment.startActivity(intent)
 }
 
-fun startAddGoogleAccountIntent(fragment: Fragment, requestCode: Int) {
+fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) {
     try {
         val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
         intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
-        fragment.startActivityForResult(intent, requestCode)
+        context.startActivityForResult(intent, requestCode)
     } catch (activityNotFoundException: ActivityNotFoundException) {
-        fragment.activity?.toast(R.string.error_no_external_application_found)
+        context.toast(R.string.error_no_external_application_found)
     }
 }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/MainActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/MainActivity.kt
index f22655a3..f67663ac 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/MainActivity.kt
@@ -21,10 +21,14 @@ import android.content.Intent
 import android.os.Bundle
 import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.auth.Authenticator
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import im.vector.riotredesign.features.home.HomeActivity
 import im.vector.riotredesign.features.login.LoginActivity
 import timber.log.Timber
+import javax.inject.Inject
 
 
 class MainActivity : VectorBaseActivity() {
@@ -44,46 +48,39 @@ class MainActivity : VectorBaseActivity() {
         }
     }
 
-    private val authenticator = Matrix.getInstance().authenticator()
+    @Inject lateinit var matrix: Matrix
+    @Inject lateinit var authenticator: Authenticator
+    @Inject lateinit var sessionHolder: ActiveSessionHolder
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        val session = Matrix.getInstance().currentSession
-
         val clearCache = intent.getBooleanExtra(EXTRA_CLEAR_CACHE, false)
         val clearCredentials = intent.getBooleanExtra(EXTRA_CLEAR_CREDENTIALS, false)
+        // Handle some wanted cleanup
+        when {
+            clearCredentials -> sessionHolder.getActiveSession().signOut(object : MatrixCallback {
+                override fun onSuccess(data: Unit) {
+                    Timber.w("SIGN_OUT: success, start app")
+                    sessionHolder.clearActiveSession()
+                    start()
+                }
+            })
+            clearCache       -> sessionHolder.getActiveSession().clearCache(object : MatrixCallback {
+                override fun onSuccess(data: Unit) {
+                    start()
+                }
+            })
+            else             -> start()
 
-        if (session == null) {
-            start()
-        } else {
-            // Handle some wanted cleanup
-            when {
-                clearCredentials -> {
-                    session.signOut(object : MatrixCallback {
-                        override fun onSuccess(data: Unit) {
-                            Timber.w("SIGN_OUT: success, start app")
-                            //TODO stop sync service
-                            start()
-                        }
-                    })
-                }
-                clearCache       -> {
-                    //TODO stop sync service
-                    session.clearCache(object : MatrixCallback {
-                        override fun onSuccess(data: Unit) {
-                            //TODO start sync service
-                            start()
-                        }
-                    })
-                }
-                else             -> start()
-            }
         }
     }
 
     private fun start() {
-        val intent = if (authenticator.hasActiveSessions()) {
+        val intent = if (sessionHolder.hasActiveSession()) {
             HomeActivity.newIntent(this)
         } else {
             LoginActivity.newIntent(this)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandController.kt b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandController.kt
index 7356364c..484ee2ce 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandController.kt
@@ -20,8 +20,9 @@ import com.airbnb.epoxy.TypedEpoxyController
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.autocomplete.AutocompleteClickListener
 import im.vector.riotredesign.features.command.Command
+import javax.inject.Inject
 
-class AutocompleteCommandController(private val stringProvider: StringProvider) : TypedEpoxyController>() {
+class AutocompleteCommandController @Inject constructor(private val stringProvider: StringProvider) : TypedEpoxyController>() {
 
     var listener: AutocompleteClickListener? = null
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandPresenter.kt b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandPresenter.kt
index ca894948..4449d2ed 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandPresenter.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/AutocompleteCommandPresenter.kt
@@ -20,9 +20,10 @@ import android.content.Context
 import com.airbnb.epoxy.EpoxyController
 import im.vector.riotredesign.features.autocomplete.EpoxyAutocompletePresenter
 import im.vector.riotredesign.features.command.Command
+import javax.inject.Inject
 
-class AutocompleteCommandPresenter(context: Context,
-                                   private val controller: AutocompleteCommandController) :
+class AutocompleteCommandPresenter @Inject constructor(context: Context,
+                                               private val controller: AutocompleteCommandController) :
         EpoxyAutocompletePresenter(context) {
 
     init {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/CommandAutocompletePolicy.kt b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/CommandAutocompletePolicy.kt
index 38556cdb..a6f9aa72 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/CommandAutocompletePolicy.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/command/CommandAutocompletePolicy.kt
@@ -18,8 +18,9 @@ package im.vector.riotredesign.features.autocomplete.command
 
 import android.text.Spannable
 import com.otaliastudios.autocomplete.AutocompletePolicy
+import javax.inject.Inject
 
-class CommandAutocompletePolicy : AutocompletePolicy {
+class CommandAutocompletePolicy @Inject constructor() : AutocompletePolicy {
 
     var enabled: Boolean = true
 
@@ -37,7 +38,7 @@ class CommandAutocompletePolicy : AutocompletePolicy {
     // Only if text which starts with '/' and without space
     override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean {
         return enabled && text?.startsWith("/") == true
-                && !text.contains(" ")
+               && !text.contains(" ")
     }
 
     override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserController.kt b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserController.kt
index 6f174e0d..dfc1f93f 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserController.kt
@@ -19,8 +19,9 @@ package im.vector.riotredesign.features.autocomplete.user
 import com.airbnb.epoxy.TypedEpoxyController
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotredesign.features.autocomplete.AutocompleteClickListener
+import javax.inject.Inject
 
-class AutocompleteUserController : TypedEpoxyController>() {
+class AutocompleteUserController @Inject constructor(): TypedEpoxyController>() {
 
     var listener: AutocompleteClickListener? = null
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserItem.kt b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserItem.kt
index 57694725..17cbde66 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/autocomplete/user/AutocompleteUserItem.kt
@@ -29,6 +29,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
 @EpoxyModelClass(layout = R.layout.item_autocomplete_user)
 abstract class AutocompleteUserItem : VectorEpoxyModel() {
 
+    @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
     @EpoxyAttribute var name: String? = null
     @EpoxyAttribute var userId: String = ""
     @EpoxyAttribute var avatarUrl: String? = null
@@ -37,7 +38,7 @@ abstract class AutocompleteUserItem : VectorEpoxyModel(context) {
 
     var callback: Callback? = null
diff --git a/vector/src/main/java/im/vector/riotredesign/features/configuration/VectorConfiguration.kt b/vector/src/main/java/im/vector/riotredesign/features/configuration/VectorConfiguration.kt
index 1f408554..3790661a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/configuration/VectorConfiguration.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/configuration/VectorConfiguration.kt
@@ -25,20 +25,22 @@ import im.vector.riotredesign.features.settings.VectorLocale
 import im.vector.riotredesign.features.themes.ThemeUtils
 import timber.log.Timber
 import java.util.*
+import javax.inject.Inject
 
 /**
  * Handle locale configuration change, such as theme, font size and locale chosen by the user
  */
-class VectorConfiguration(private val context: Context) {
+
+class VectorConfiguration @Inject constructor(private val context: Context) {
 
     // TODO Import mLanguageReceiver From Riot?
     fun onConfigurationChanged(newConfig: Configuration?) {
         if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
             Timber.v("## onConfigurationChanged() : the locale has been updated to " + Locale.getDefault().toString()
-                    + ", restore the expected value " + VectorLocale.applicationLocale.toString())
+                     + ", restore the expected value " + VectorLocale.applicationLocale.toString())
             updateApplicationSettings(VectorLocale.applicationLocale,
-                    FontScale.getFontScalePrefValue(context),
-                    ThemeUtils.getApplicationTheme(context))
+                                      FontScale.getFontScalePrefValue(context),
+                                      ThemeUtils.getApplicationTheme(context))
         }
     }
 
@@ -65,8 +67,8 @@ class VectorConfiguration(private val context: Context) {
     fun updateApplicationTheme(theme: String) {
         ThemeUtils.setApplicationTheme(context, theme)
         updateApplicationSettings(VectorLocale.applicationLocale,
-                FontScale.getFontScalePrefValue(context),
-                theme)
+                                  FontScale.getFontScalePrefValue(context),
+                                  theme)
     }
 
     /**
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
index 5083dd9a..333342e1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
@@ -23,7 +23,6 @@ import androidx.fragment.app.FragmentManager
 import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProviders
 import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
-import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.SimpleFragmentActivity
@@ -43,9 +42,8 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
 
     override fun initUiAndData() {
         super.initUiAndData()
-        viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
         viewModel.initSession(session)
-
         viewModel.keyVersionResult.observe(this, Observer { keyVersion ->
 
             if (keyVersion != null && supportFragmentManager.fragments.isEmpty()) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt
index 8d07b3af..0c2eb788 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt
@@ -27,8 +27,8 @@ import butterknife.BindView
 import butterknife.OnClick
 import butterknife.OnTextChanged
 import com.google.android.material.textfield.TextInputLayout
-import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.utils.startImportTextFromFileIntent
 import timber.log.Timber
@@ -51,11 +51,15 @@ class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() {
     @BindView(R.id.keys_restore_key_enter_edittext)
     lateinit var mKeyTextEdit: EditText
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromKeyViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreFromKeyViewModel::class.java)
         sharedViewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
         mKeyTextEdit.setText(viewModel.recoveryCode.value)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
index dd536944..99a36f30 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
@@ -18,7 +18,6 @@ package im.vector.riotredesign.features.crypto.keysbackup.restore
 import android.content.Context
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
-import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.listeners.StepProgressListener
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
@@ -28,8 +27,9 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.WaitingViewData
 import im.vector.riotredesign.core.ui.views.KeysBackupBanner
 import timber.log.Timber
+import javax.inject.Inject
 
-class KeysBackupRestoreFromKeyViewModel : ViewModel() {
+class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
 
     var recoveryCode: MutableLiveData = MutableLiveData()
     var recoveryCodeErrorText: MutableLiveData = MutableLiveData()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt
index 41d0a4e5..a0da69fb 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt
@@ -33,9 +33,11 @@ import butterknife.OnClick
 import butterknife.OnTextChanged
 import com.google.android.material.textfield.TextInputLayout
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.showPassword
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
+import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
 
 class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
 
@@ -65,12 +67,16 @@ class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
         fun newInstance() = KeysBackupRestoreFromPassphraseFragment()
     }
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
-        viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromPassphraseViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreFromPassphraseViewModel::class.java)
         sharedViewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
index e60923fd..bdbfd23b 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
@@ -18,7 +18,6 @@ package im.vector.riotredesign.features.crypto.keysbackup.restore
 import android.content.Context
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
-import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.listeners.StepProgressListener
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
@@ -28,8 +27,9 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.WaitingViewData
 import im.vector.riotredesign.core.ui.views.KeysBackupBanner
 import timber.log.Timber
+import javax.inject.Inject
 
-class KeysBackupRestoreFromPassphraseViewModel : ViewModel() {
+class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel() {
 
     var passphrase: MutableLiveData = MutableLiveData()
     var passphraseErrorText: MutableLiveData = MutableLiveData()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
index 3db0191e..d6c618ba 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package im.vector.fragments.keysbackup.restore
+package im.vector.riotredesign.features.crypto.keysbackup.restore
 
 import android.content.Context
 import androidx.lifecycle.LiveData
@@ -21,13 +21,14 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
-import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
 import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.WaitingViewData
 import im.vector.riotredesign.core.utils.LiveEvent
+import javax.inject.Inject
 
-class KeysBackupRestoreSharedViewModel : ViewModel() {
+class KeysBackupRestoreSharedViewModel @Inject constructor() : ViewModel() {
 
     companion object {
         const val NAVIGATE_TO_RECOVER_WITH_KEY = "NAVIGATE_TO_RECOVER_WITH_KEY"
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
index 106ed764..def561ac 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
@@ -20,8 +20,8 @@ import android.widget.TextView
 import androidx.lifecycle.ViewModelProviders
 import butterknife.BindView
 import butterknife.OnClick
-import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.utils.LiveEvent
 
@@ -37,10 +37,14 @@ class KeysBackupRestoreSuccessFragment : VectorBaseFragment() {
 
     private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         sharedViewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
         sharedViewModel.importKeyResult?.let {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
index 98b6716b..273b32aa 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
@@ -17,16 +17,16 @@ package im.vector.riotredesign.features.crypto.keysbackup.settings
 
 import android.content.Context
 import android.content.Intent
+import android.os.Bundle
 import androidx.appcompat.app.AlertDialog
 import com.airbnb.mvrx.Fail
 import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.viewModel
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.SimpleFragmentActivity
 import im.vector.riotredesign.core.platform.WaitingViewData
-import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
+import javax.inject.Inject
 
 
 class KeysBackupManageActivity : SimpleFragmentActivity() {
@@ -41,12 +41,14 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
     override fun getTitleRes() = R.string.encryption_message_recovery
 
     private val viewModel: KeysBackupSettingsViewModel by viewModel()
+    @Inject lateinit var keysBackupSettingsViewModelFactory: KeysBackupSettingsViewModel.Factory
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
 
     override fun initUiAndData() {
         super.initUiAndData()
-
-        bindScope(getOrCreateScope(KeysBackupModule.KEYS_BACKUP_SCOPE))
-
         if (supportFragmentManager.fragments.isEmpty()) {
             supportFragmentManager.beginTransaction()
                     .replace(R.id.container, KeysBackupSettingsFragment.newInstance())
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt
index 983a3e2a..72a97f37 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt
@@ -21,17 +21,15 @@ import androidx.appcompat.app.AlertDialog
 import com.airbnb.mvrx.activityViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
-import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
 import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
 import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupActivity
 import kotlinx.android.synthetic.main.fragment_keys_backup_settings.*
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
+import javax.inject.Inject
 
 class KeysBackupSettingsFragment : VectorBaseFragment(),
-        KeysBackupSettingsRecyclerViewController.Listener {
+                                   KeysBackupSettingsRecyclerViewController.Listener {
 
     companion object {
         fun newInstance() = KeysBackupSettingsFragment()
@@ -39,14 +37,11 @@ class KeysBackupSettingsFragment : VectorBaseFragment(),
 
     override fun getLayoutResId() = R.layout.fragment_keys_backup_settings
 
-    private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController by inject()
-
+    @Inject lateinit var keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController
     private val viewModel: KeysBackupSettingsViewModel by activityViewModel()
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        bindScope(getOrCreateScope(KeysBackupModule.KEYS_BACKUP_SCOPE))
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
index 80e716cc..6e555b58 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
@@ -17,11 +17,14 @@ package im.vector.riotredesign.features.crypto.keysbackup.settings
 
 import android.view.View
 import com.airbnb.epoxy.TypedEpoxyController
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
-import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.epoxy.errorWithRetryItem
 import im.vector.riotredesign.core.epoxy.loadingItem
@@ -29,9 +32,10 @@ import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.ui.list.GenericItem
 import im.vector.riotredesign.core.ui.list.genericItem
 import java.util.*
+import javax.inject.Inject
 
-class KeysBackupSettingsRecyclerViewController(val stringProvider: StringProvider,
-                                               val session: Session) : TypedEpoxyController() {
+class KeysBackupSettingsRecyclerViewController @Inject constructor(val stringProvider: StringProvider,
+                                                                   val session: Session) : TypedEpoxyController() {
 
     var listener: Listener? = null
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
index 969ff028..d656d90c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
@@ -15,7 +15,15 @@
  */
 package im.vector.riotredesign.features.crypto.keysbackup.settings
 
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
@@ -23,33 +31,37 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
 
-class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
-                                  session: Session) : VectorViewModel(initialState),
-        KeysBackupStateListener {
+class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
+                                                              session: Session
+) : VectorViewModel(initialState),
+    KeysBackupStateListener {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: KeysBackupSettingViewState): KeysBackupSettingsViewModel? {
-            val session = viewModelContext.activity.get()
-
-            val firstState = state.copy(
-                    keysBackupState = session.getKeysBackupService().state,
-                    keysBackupVersion = session.getKeysBackupService().keysBackupVersion
-            )
-
-            return KeysBackupSettingsViewModel(firstState, session)
+            val activity: KeysBackupManageActivity = (viewModelContext as ActivityViewModelContext).activity()
+            return activity.keysBackupSettingsViewModelFactory.create(state)
         }
     }
 
     private var keysBackupService: KeysBackupService = session.getKeysBackupService()
 
     init {
+        setState {
+            this.copy(
+                    keysBackupState = session.getKeysBackupService().state,
+                    keysBackupVersion = session.getKeysBackupService().keysBackupVersion
+            )
+        }
         keysBackupService.addListener(this)
-
         getKeysBackupTrust()
     }
 
@@ -90,8 +102,8 @@ class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
     }
 
     override fun onCleared() {
-        super.onCleared()
         keysBackupService.removeListener(this)
+        super.onCleared()
     }
 
     override fun onStateChange(newState: KeysBackupState) {
@@ -142,6 +154,6 @@ class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
         val currentBackupState = keysBackupService.state
 
         return currentBackupState == KeysBackupState.Unknown
-                || currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
+               || currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
     }
 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
index eecb16bf..c5e479fc 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
@@ -44,7 +44,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
                     .commitNow()
         }
 
-        viewModel = ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
         viewModel.showManualExport.value = intent.getBooleanExtra(EXTRA_SHOW_MANUAL_EXPORT, false)
         viewModel.initSession(session)
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
index eaf534f0..1381b922 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
@@ -31,11 +31,12 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.WaitingViewData
 import im.vector.riotredesign.core.utils.LiveEvent
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * The shared view model between all fragments.
  */
-class KeysBackupSetupSharedViewModel : ViewModel() {
+class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
 
     companion object {
         const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2"
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt
index 574c5dd7..d1fe142a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt
@@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModelProviders
 import butterknife.BindView
 import butterknife.OnClick
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.utils.LiveEvent
 
@@ -45,12 +46,16 @@ class KeysBackupSetupStep1Fragment : VectorBaseFragment() {
     @BindView(R.id.keys_backup_setup_step1_manualExport)
     lateinit var manualExportButton: Button
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
         viewModel.showManualExport.observe(this, Observer {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt
index df708dc0..7d0ca797 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt
@@ -31,6 +31,7 @@ import butterknife.OnTextChanged
 import com.google.android.material.textfield.TextInputLayout
 import com.nulabinc.zxcvbn.Zxcvbn
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.showPassword
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.ui.views.PasswordStrengthBar
@@ -77,11 +78,15 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
 
     private lateinit var viewModel: KeysBackupSetupSharedViewModel
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
         viewModel.shouldPromptOnBack = true
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
index eaa4da21..dd937770 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
@@ -30,6 +30,7 @@ import butterknife.BindView
 import butterknife.OnClick
 import com.google.android.material.bottomsheet.BottomSheetDialog
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.files.addEntryToDownloadManager
 import im.vector.riotredesign.core.files.writeToFile
 import im.vector.riotredesign.core.platform.VectorBaseFragment
@@ -59,10 +60,14 @@ class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
 
     private lateinit var viewModel: KeysBackupSetupSharedViewModel
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysrequest/KeyRequestHandler.kt
index 30d16a62..a583760a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysrequest/KeyRequestHandler.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysrequest/KeyRequestHandler.kt
@@ -40,6 +40,8 @@ import timber.log.Timber
 import java.text.DateFormat
 import java.text.SimpleDateFormat
 import java.util.*
+import javax.inject.Inject
+import javax.inject.Singleton
 import kotlin.collections.ArrayList
 import kotlin.collections.HashMap
 
@@ -50,21 +52,20 @@ import kotlin.collections.HashMap
  * If several requests come from same user/device, a single alert is displayed (this alert will accept/reject all request
  * depending on user action)
  */
-class KeyRequestHandler(val context: Context,
-                        val session: Session)
+
+@Singleton
+class KeyRequestHandler @Inject constructor(val context: Context,
+                                            val session: Session)
     : RoomKeysRequestListener,
-        SasVerificationService.SasVerificationListener {
+      SasVerificationService.SasVerificationListener {
 
     private val alertsToRequests = HashMap>()
 
     init {
         session.getSasVerificationService().addListener(this)
-
         session.addRoomKeysRequestListener(this)
     }
 
-    fun ensureStarted() = Unit
-
     /**
      * Handle incoming key request.
      *
@@ -194,8 +195,8 @@ class KeyRequestHandler(val context: Context,
                 Runnable {
                     alert.weakCurrentActivity?.get()?.let {
                         val intent = SASVerificationActivity.outgoingIntent(it,
-                                session.sessionParams.credentials.userId,
-                                userId, deviceId)
+                                                                            session.sessionParams.credentials.userId,
+                                                                            userId, deviceId)
                         it.startActivity(intent)
                     }
                 },
@@ -246,8 +247,8 @@ class KeyRequestHandler(val context: Context,
         val alertMgrUniqueKey = alertManagerId(deviceId!!, userId!!)
         alertsToRequests[alertMgrUniqueKey]?.removeAll {
             it.deviceId == request.deviceId
-                    && it.userId == request.userId
-                    && it.requestId == request.requestId
+            && it.userId == request.userId
+            && it.requestId == request.requestId
         }
         if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) {
             PopupAlertManager.cancelAlert(alertMgrUniqueKey)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 87cb3b54..cfa35ed4 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -22,29 +22,32 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
 import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
 import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.features.popup.PopupAlertManager
+import javax.inject.Inject
+import javax.inject.Singleton
 
 /**
  * Listens to the VerificationManager and add a new notification when an incoming request is detected.
  */
-class IncomingVerificationRequestHandler(val context: Context,
-                                         private val session: Session) : SasVerificationService.SasVerificationListener {
+@Singleton
+class IncomingVerificationRequestHandler @Inject constructor(val context: Context,
+                                                             private val activeSessionHolder: ActiveSessionHolder
+) : SasVerificationService.SasVerificationListener {
 
     init {
-        session.getSasVerificationService().addListener(this)
+        activeSessionHolder.getActiveSession().getSasVerificationService().addListener(this)
     }
 
-    fun ensureStarted() = Unit
-
     override fun transactionCreated(tx: SasVerificationTransaction) {}
 
     override fun transactionUpdated(tx: SasVerificationTransaction) {
         when (tx.state) {
             SasVerificationTxState.OnStarted -> {
                 //Add a notification for every incoming request
-                val session = Matrix.getInstance().currentSession!!
+                val session = activeSessionHolder.getActiveSession()
                 val name = session.getUser(tx.otherUserId)?.displayName
-                        ?: tx.otherUserId
+                           ?: tx.otherUserId
 
                 val alert = PopupAlertManager.VectorAlert(
                         "kvr_${tx.transactionId}",
@@ -54,9 +57,9 @@ class IncomingVerificationRequestHandler(val context: Context,
                         .apply {
                             contentAction = Runnable {
                                 val intent = SASVerificationActivity.incomingIntent(context,
-                                        session.sessionParams.credentials.userId,
-                                        tx.otherUserId,
-                                        tx.transactionId)
+                                                                                    session.sessionParams.credentials.userId,
+                                                                                    tx.otherUserId,
+                                                                                    tx.transactionId)
                                 weakCurrentActivity?.get()?.startActivity(intent)
                             }
                             dismissedAction = Runnable {
@@ -72,9 +75,9 @@ class IncomingVerificationRequestHandler(val context: Context,
                                     context.getString(R.string.action_open),
                                     Runnable {
                                         val intent = SASVerificationActivity.incomingIntent(context,
-                                                session.sessionParams.credentials.userId,
-                                                tx.otherUserId,
-                                                tx.transactionId)
+                                                                                            session.sessionParams.credentials.userId,
+                                                                                            tx.otherUserId,
+                                                                                            tx.transactionId)
                                         weakCurrentActivity?.get()?.startActivity(intent)
                                     }
                             )
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt
index 4fed4d96..c66a001c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt
@@ -84,7 +84,7 @@ class SASVerificationActivity : SimpleFragmentActivity() {
 
     override fun initUiAndData() {
         super.initUiAndData()
-        viewModel = ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
         val transactionID: String? = intent.getStringExtra(EXTRA_TRANSACTION_ID)
 
         if (isFirstCreation()) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt
index 35ef1acc..6acbad55 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt
@@ -24,8 +24,10 @@ import butterknife.BindView
 import butterknife.OnClick
 import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.home.AvatarRenderer
+import javax.inject.Inject
 
 class SASVerificationIncomingFragment : VectorBaseFragment() {
 
@@ -47,13 +49,18 @@ class SASVerificationIncomingFragment : VectorBaseFragment() {
 
     override fun getLayoutResId() = R.layout.fragment_sas_verification_incoming_request
 
+    @Inject lateinit var avatarRenderer: AvatarRenderer
     private lateinit var viewModel: SasVerificationViewModel
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
         otherUserDisplayNameTextView.text = viewModel.otherUser?.displayName ?: viewModel.otherUserId
@@ -61,10 +68,10 @@ class SASVerificationIncomingFragment : VectorBaseFragment() {
         otherDeviceTextView.text = viewModel.otherDeviceId
 
         viewModel.otherUser?.let {
-            AvatarRenderer.render(it, avatarImageView)
+            avatarRenderer.render(it, avatarImageView)
         } ?: run {
             // Fallback to what we know
-            AvatarRenderer.render(null, viewModel.otherUserId ?: "", viewModel.otherUserId, avatarImageView)
+            avatarRenderer.render(null, viewModel.otherUserId ?: "", viewModel.otherUserId, avatarImageView)
         }
 
         viewModel.transactionState.observe(this, Observer {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt
index b0363fae..faf40995 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt
@@ -69,7 +69,7 @@ class SASVerificationShortCodeFragment : VectorBaseFragment() {
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt
index e41d0c73..ce92888c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt
@@ -58,11 +58,7 @@ class SASVerificationStartFragment : VectorBaseFragment() {
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-
-        viewModel = activity?.run {
-            ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
-        } ?: throw Exception("Invalid Activity")
-
+        viewModel = ViewModelProviders.of(vectorBaseActivity, viewModelFactory).get(SasVerificationViewModel::class.java)
         viewModel.transactionState.observe(this, Observer {
             val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState
             when (uxState) {
@@ -75,14 +71,14 @@ class SASVerificationStartFragment : VectorBaseFragment() {
                     this.startButtonLoading.animate()
 
                 }
-                OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
+                OutgoingSasVerificationRequest.UxState.SHOW_SAS               -> {
                     viewModel.shortCodeReady()
                 }
                 OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
-                OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
+                OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER     -> {
                     viewModel.navigateCancel()
                 }
-                else -> {
+                else                                                          -> {
                     TransitionManager.beginDelayedTransition(this.rootLayout)
                     this.loadingText.isVisible = false
                     this.startButton.isVisible = true
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt
index a14dc3ce..47e874be 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt
@@ -35,7 +35,7 @@ class SASVerificationVerifiedFragment : VectorBaseFragment() {
         super.onActivityCreated(savedInstanceState)
 
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
 
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt
index 7f06bba8..31fc91a4 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt
@@ -25,10 +25,11 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransactio
 import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotredesign.core.utils.LiveEvent
+import javax.inject.Inject
 
 
-class SasVerificationViewModel : ViewModel(),
-        SasVerificationService.SasVerificationListener {
+class SasVerificationViewModel @Inject constructor() : ViewModel(),
+                                                       SasVerificationService.SasVerificationListener {
 
 
     companion object {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt
index 84d61fe6..a9943a6e 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt
@@ -20,36 +20,38 @@ import android.content.Context
 import android.graphics.drawable.Drawable
 import android.widget.ImageView
 import androidx.annotation.AnyThread
-import androidx.annotation.ColorRes
 import androidx.annotation.UiThread
 import androidx.core.content.ContextCompat
 import com.amulyakhare.textdrawable.TextDrawable
 import com.bumptech.glide.request.RequestOptions
 import com.bumptech.glide.request.target.DrawableImageViewTarget
 import com.bumptech.glide.request.target.Target
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixPatterns
 import im.vector.matrix.android.api.session.content.ContentUrlResolver
 import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.glide.GlideApp
 import im.vector.riotredesign.core.glide.GlideRequest
 import im.vector.riotredesign.core.glide.GlideRequests
+import javax.inject.Inject
 
 /**
  * This helper centralise ways to retrieve avatar into ImageView or even generic Target
  */
 
-object AvatarRenderer {
+class AvatarRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder){
 
-    private const val THUMBNAIL_SIZE = 250
+    companion object {
+        private const val THUMBNAIL_SIZE = 250
 
-    private val AVATAR_COLOR_LIST = listOf(
-            R.color.riotx_avatar_fill_1,
-            R.color.riotx_avatar_fill_2,
-            R.color.riotx_avatar_fill_3
-    )
+        private val AVATAR_COLOR_LIST = listOf(
+                R.color.riotx_avatar_fill_1,
+                R.color.riotx_avatar_fill_2,
+                R.color.riotx_avatar_fill_3
+        )
+    }
 
     @UiThread
     fun render(roomSummary: RoomSummary, imageView: ImageView) {
@@ -117,7 +119,7 @@ object AvatarRenderer {
 //    }
 
     private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest {
-        val resolvedUrl = Matrix.getInstance().currentSession!!.contentUrlResolver()
+        val resolvedUrl = activeSessionHolder.getActiveSession().contentUrlResolver()
                 .resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)
 
         return glideRequest
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
index 572d865e..cf924734 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
@@ -31,6 +31,8 @@ import androidx.lifecycle.ViewModelProviders
 import com.airbnb.mvrx.viewModel
 import im.vector.matrix.android.api.Matrix
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.hideKeyboard
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.extensions.replaceFragment
@@ -47,9 +49,7 @@ import im.vector.riotredesign.features.workers.signout.SignOutUiWorker
 import im.vector.riotredesign.features.workers.signout.SignOutViewModel
 import im.vector.riotredesign.push.fcm.FcmHelper
 import kotlinx.android.synthetic.main.activity_home.*
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
+import javax.inject.Inject
 
 
 class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
@@ -61,15 +61,17 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
 
     private val homeActivityViewModel: HomeActivityViewModel by viewModel()
     private lateinit var navigationViewModel: HomeNavigationViewModel
-    private val homeNavigator by inject()
-    private val pushManager by inject()
-
-    private val notificationDrawerManager by inject()
 
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
+    @Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
+    @Inject lateinit var homeNavigator: HomeNavigator
+    @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
     // TODO Move this elsewhere
-    private val incomingVerificationRequestHandler by inject()
+    @Inject lateinit var incomingVerificationRequestHandler: IncomingVerificationRequestHandler
     // TODO Move this elsewhere
-    private val keyRequestHandler by inject()
+    @Inject lateinit var keyRequestHandler: KeyRequestHandler
+    @Inject lateinit var pushManager: PushersManager
+    @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
 
     private var progress: ProgressDialog? = null
 
@@ -81,14 +83,15 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
 
     override fun getLayoutRes() = R.layout.activity_home
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        bindScope(getOrCreateScope(HomeModule.HOME_SCOPE))
         homeNavigator.activity = this
         FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager)
-
         navigationViewModel = ViewModelProviders.of(this).get(HomeNavigationViewModel::class.java)
-
         drawerLayout.addDrawerListener(drawerListener)
         if (isFirstCreation()) {
             val homeDrawerFragment = HomeDrawerFragment.newInstance()
@@ -115,9 +118,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
             }
         }
 
-        incomingVerificationRequestHandler.ensureStarted()
-        keyRequestHandler.ensureStarted()
-
         if (intent.hasExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)) {
             notificationDrawerManager.clearAllEvents()
             intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
@@ -142,14 +142,14 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
     override fun onResume() {
         super.onResume()
 
-        if (VectorUncaughtExceptionHandler.didAppCrash(this)) {
-            VectorUncaughtExceptionHandler.clearAppCrashStatus(this)
+        if (vectorUncaughtExceptionHandler.didAppCrash(this)) {
+            vectorUncaughtExceptionHandler.clearAppCrashStatus(this)
 
             AlertDialog.Builder(this)
                     .setMessage(R.string.send_bug_report_app_crashed)
                     .setCancelable(false)
-                    .setPositiveButton(R.string.yes) { _, _ -> BugReporter.openBugReportScreen(this) }
-                    .setNegativeButton(R.string.no) { _, _ -> BugReporter.deleteCrashFile(this) }
+                    .setPositiveButton(R.string.yes) { _, _ -> bugReporter.openBugReportScreen(this) }
+                    .setNegativeButton(R.string.no) { _, _ -> bugReporter.deleteCrashFile(this) }
                     .show()
         }
 
@@ -166,8 +166,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.sliding_menu_sign_out -> {
-                SignOutUiWorker(this, notificationDrawerManager)
-                        .perform(Matrix.getInstance().currentSession!!)
+                SignOutUiWorker(this, notificationDrawerManager).perform(activeSessionHolder.getActiveSession())
                 return true
             }
         }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt
index c710b477..a2f373fa 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt
@@ -19,10 +19,12 @@ package im.vector.riotredesign.features.home
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import arrow.core.Option
+import com.airbnb.mvrx.ActivityViewModelContext
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
-import im.vector.matrix.android.api.Matrix
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.group.model.GroupSummary
@@ -34,28 +36,31 @@ import im.vector.riotredesign.features.home.group.ALL_COMMUNITIES_GROUP_ID
 import im.vector.riotredesign.features.home.group.SelectedGroupStore
 import io.reactivex.Observable
 import io.reactivex.functions.BiFunction
-import org.koin.android.ext.android.get
 import java.util.concurrent.TimeUnit
 
 data class EmptyState(val isEmpty: Boolean = true) : MvRxState
 
-class HomeActivityViewModel(state: EmptyState,
-                            private val session: Session,
-                            private val selectedGroupStore: SelectedGroupStore,
-                            private val homeRoomListStore: HomeRoomListObservableStore
-) : VectorViewModel(state), Session.Listener {
+class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState: EmptyState,
+                                                        private val session: Session,
+                                                        private val selectedGroupStore: SelectedGroupStore,
+                                                        private val homeRoomListStore: HomeRoomListObservableStore
+) : VectorViewModel(initialState), Session.Listener {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: EmptyState): HomeActivityViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: EmptyState): HomeActivityViewModel? {
-            val session = Matrix.getInstance().currentSession!!
-            val selectedGroupStore = viewModelContext.activity.get()
-            val homeRoomListObservableSource = viewModelContext.activity.get()
-            return HomeActivityViewModel(state, session, selectedGroupStore, homeRoomListObservableSource)
+            val homeActivity: HomeActivity = (viewModelContext as ActivityViewModelContext).activity()
+            return homeActivity.homeActivityViewModelFactory.create(state)
         }
     }
 
+
     private val _isLoading = MutableLiveData()
     val isLoading: LiveData
         get() = _isLoading
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailFragment.kt
index 273a06d0..4ea3df5d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailFragment.kt
@@ -32,6 +32,7 @@ import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
 import im.vector.matrix.android.api.session.sync.SyncState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.ToolbarConfigurable
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.ui.views.KeysBackupBanner
@@ -41,7 +42,7 @@ import im.vector.riotredesign.features.home.room.list.UnreadCounterBadgeView
 import im.vector.riotredesign.features.workers.signout.SignOutViewModel
 import kotlinx.android.parcel.Parcelize
 import kotlinx.android.synthetic.main.fragment_home_detail.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 
 @Parcelize
@@ -67,16 +68,22 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
     private val viewModel: HomeDetailViewModel by fragmentViewModel()
     private lateinit var navigationViewModel: HomeNavigationViewModel
 
-    private val session by inject()
+    @Inject lateinit var session: Session
+    @Inject lateinit var homeDetailViewModelFactory: HomeDetailViewModel.Factory
+    @Inject lateinit var avatarRenderer: AvatarRenderer
 
     override fun getLayoutResId(): Int {
         return R.layout.fragment_home_detail
     }
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         currentDisplayMode = savedInstanceState?.getSerializable(CURRENT_DISPLAY_MODE) as? RoomListFragment.DisplayMode
-                ?: RoomListFragment.DisplayMode.HOME
+                             ?: RoomListFragment.DisplayMode.HOME
 
         navigationViewModel = ViewModelProviders.of(requireActivity()).get(HomeNavigationViewModel::class.java)
 
@@ -89,7 +96,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
     private fun setupKeysBackupBanner() {
         // Keys backup banner
         // Use the SignOutViewModel, it observe the keys backup state and this is what we need here
-        val model = ViewModelProviders.of(this).get(SignOutViewModel::class.java)
+        val model = ViewModelProviders.of(this, viewModelFactory).get(SignOutViewModel::class.java)
 
         model.init(session)
 
@@ -132,7 +139,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
             parentActivity.configure(groupToolbar)
         }
         groupToolbar.title = ""
-        AvatarRenderer.render(
+        avatarRenderer.render(
                 params.groupAvatar,
                 params.groupId,
                 params.groupName,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailViewModel.kt
index 7e2ebb7f..59ff9a75 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDetailViewModel.kt
@@ -16,28 +16,34 @@
 
 package im.vector.riotredesign.features.home
 
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.rx.rx
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
 /**
  * View model used to update the home bottom bar notification counts
  */
-class HomeDetailViewModel(initialState: HomeDetailViewState,
-                          private val session: Session,
-                          private val homeRoomListStore: HomeRoomListObservableStore)
+class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState,
+                                                      private val session: Session,
+                                                      private val homeRoomListStore: HomeRoomListObservableStore)
     : VectorViewModel(initialState) {
 
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: HomeDetailViewState): HomeDetailViewModel
+    }
+
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? {
-            val homeRoomListStore = viewModelContext.activity.get()
-            val session = viewModelContext.activity.get()
-            return HomeDetailViewModel(state, session, homeRoomListStore)
+            val fragment: HomeDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.homeDetailViewModelFactory.create(state)
         }
     }
 
@@ -65,21 +71,21 @@ class HomeDetailViewModel(initialState: HomeDetailViewState,
                 .subscribe { list ->
                     list.let { summaries ->
                         val peopleNotifications = summaries
-                                .filter { it.isDirect }
-                                .map { it.notificationCount }
-                                .takeIf { it.isNotEmpty() }
-                                ?.sumBy { i -> i }
-                                ?: 0
+                                                          .filter { it.isDirect }
+                                                          .map { it.notificationCount }
+                                                          .takeIf { it.isNotEmpty() }
+                                                          ?.sumBy { i -> i }
+                                                  ?: 0
                         val peopleHasHighlight = summaries
                                 .filter { it.isDirect }
                                 .any { it.highlightCount > 0 }
 
                         val roomsNotifications = summaries
-                                .filter { !it.isDirect }
-                                .map { it.notificationCount }
-                                .takeIf { it.isNotEmpty() }
-                                ?.sumBy { i -> i }
-                                ?: 0
+                                                         .filter { !it.isDirect }
+                                                         .map { it.notificationCount }
+                                                         .takeIf { it.isNotEmpty() }
+                                                         ?.sumBy { i -> i }
+                                                 ?: 0
                         val roomsHasHighlight = summaries
                                 .filter { !it.isDirect }
                                 .any { it.highlightCount > 0 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt
index 66195881..378b6a1b 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt
@@ -19,12 +19,13 @@ package im.vector.riotredesign.features.home
 import android.os.Bundle
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.observeK
 import im.vector.riotredesign.core.extensions.replaceChildFragment
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.home.group.GroupListFragment
 import kotlinx.android.synthetic.main.fragment_home_drawer.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 class HomeDrawerFragment : VectorBaseFragment() {
 
@@ -35,10 +36,15 @@ class HomeDrawerFragment : VectorBaseFragment() {
         }
     }
 
-    val session by inject()
+    @Inject lateinit var session: Session
+    @Inject lateinit var avatarRenderer: AvatarRenderer
 
     override fun getLayoutResId() = R.layout.fragment_home_drawer
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         if (savedInstanceState == null) {
@@ -48,7 +54,7 @@ class HomeDrawerFragment : VectorBaseFragment() {
 
         session.observeUser(session.sessionParams.credentials.userId).observeK(this) { user ->
             if (user != null) {
-                AvatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView)
+                avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView)
                 homeDrawerUsernameView.text = user.displayName
                 homeDrawerUserIdView.text = user.userId
             }
@@ -62,4 +68,4 @@ class HomeDrawerFragment : VectorBaseFragment() {
             navigator.openDebug(requireActivity())
         }
     }
-}
\ No newline at end of file
+}
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt
index 044bf881..eee5b8e8 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt
@@ -16,97 +16,20 @@
 
 package im.vector.riotredesign.features.home
 
-import androidx.fragment.app.Fragment
-import im.vector.riotredesign.core.glide.GlideApp
-import im.vector.riotredesign.core.resources.ColorProvider
-import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandController
-import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
-import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserController
-import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
-import im.vector.riotredesign.features.home.group.GroupSummaryController
-import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
-import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
-import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
-import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
-import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
-import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
-import im.vector.riotredesign.features.home.room.list.RoomSummaryController
-import im.vector.riotredesign.features.html.EventHtmlRenderer
-import org.koin.core.parameter.parametersOf
-import org.koin.dsl.module.module
+import android.os.Handler
+import dagger.Module
+import dagger.Provides
+import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventControllerHandler
+import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineAsyncHelper
 
-class HomeModule {
+@Module
+object HomeModule {
 
-    companion object {
-        const val HOME_SCOPE = "HOME_SCOPE"
-        const val ROOM_DETAIL_SCOPE = "ROOM_DETAIL_SCOPE"
+    @Provides
+    @JvmStatic
+    @TimelineEventControllerHandler
+    fun providesTimelineBackgroundHandler(): Handler {
+        return TimelineAsyncHelper.getBackgroundHandler()
     }
 
-    val definition = module {
-
-        // Activity scope
-
-        scope(HOME_SCOPE) {
-            HomeNavigator()
-        }
-
-        // Fragment scopes
-
-        factory {
-            TimelineDateFormatter(get())
-        }
-
-        factory {
-            NoticeEventFormatter(get())
-        }
-
-        factory { (fragment: Fragment) ->
-            val colorProvider = ColorProvider(fragment.requireContext())
-            val timelineDateFormatter = get()
-            val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
-            val noticeEventFormatter = get(parameters = { parametersOf(fragment) })
-            val timelineMediaSizeProvider = TimelineMediaSizeProvider()
-            val messageInformationDataFactory = MessageInformationDataFactory(timelineDateFormatter, colorProvider)
-            val messageItemFactory = MessageItemFactory(colorProvider,
-                    timelineMediaSizeProvider,
-                    eventHtmlRenderer,
-                    get(),
-                    messageInformationDataFactory,
-                    get())
-
-            val encryptedItemFactory = EncryptedItemFactory(messageInformationDataFactory, colorProvider, get())
-
-            val timelineItemFactory = TimelineItemFactory(
-                    messageItemFactory = messageItemFactory,
-                    noticeItemFactory = NoticeItemFactory(noticeEventFormatter),
-                    defaultItemFactory = DefaultItemFactory(),
-                    encryptionItemFactory = EncryptionItemFactory(get()),
-                    encryptedItemFactory = encryptedItemFactory
-            )
-            TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
-        }
-
-        factory {
-            RoomSummaryController(get(), get(), get())
-        }
-
-        factory {
-            GroupSummaryController()
-        }
-
-        scope(ROOM_DETAIL_SCOPE) {
-            PermalinkHandler(get(), get())
-        }
-
-        scope(ROOM_DETAIL_SCOPE) { (fragment: Fragment) ->
-            val commandController = AutocompleteCommandController(get())
-            AutocompleteCommandPresenter(fragment.requireContext(), commandController)
-        }
-
-        scope(ROOM_DETAIL_SCOPE) { (fragment: Fragment) ->
-            val userController = AutocompleteUserController()
-            AutocompleteUserPresenter(fragment.requireContext(), userController)
-        }
-
-    }
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigationViewModel.kt
index d922f3ae..c678b5f5 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigationViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigationViewModel.kt
@@ -17,5 +17,6 @@
 package im.vector.riotredesign.features.home
 
 import im.vector.riotredesign.core.mvrx.NavigationViewModel
+import javax.inject.Inject
 
-class HomeNavigationViewModel : NavigationViewModel()
\ No newline at end of file
+class HomeNavigationViewModel @Inject constructor() : NavigationViewModel()
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt
index 729b8722..0d1c3206 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt
@@ -22,10 +22,14 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.extensions.replaceFragment
 import kotlinx.android.synthetic.main.activity_home.*
 import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class HomeNavigator {
+@Singleton
+class HomeNavigator @Inject constructor() {
 
     var activity: HomeActivity? = null
+    private var rootRoomId: String? = null
 
     fun openSelectedGroup(groupSummary: GroupSummary) {
         Timber.v("Open selected group ${groupSummary.groupId}")
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeRoomListObservableStore.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeRoomListObservableStore.kt
index 9eaa47a7..e455961f 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeRoomListObservableStore.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeRoomListObservableStore.kt
@@ -21,8 +21,11 @@ import im.vector.riotredesign.core.utils.RxStore
 import im.vector.riotredesign.features.home.room.list.RoomListDisplayModeFilter
 import im.vector.riotredesign.features.home.room.list.RoomListFragment
 import io.reactivex.Observable
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class HomeRoomListObservableStore : RxStore>() {
+@Singleton
+class HomeRoomListObservableStore @Inject constructor() : RxStore>() {
 
     fun observeFilteredBy(displayMode: RoomListFragment.DisplayMode): Observable> {
         return observe()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/home/PermalinkHandler.kt
index 537f7f93..825e9748 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/PermalinkHandler.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/PermalinkHandler.kt
@@ -22,9 +22,10 @@ import im.vector.matrix.android.api.permalinks.PermalinkData
 import im.vector.matrix.android.api.permalinks.PermalinkParser
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.features.navigation.Navigator
+import javax.inject.Inject
 
-class PermalinkHandler(private val session: Session,
-                       private val navigator: Navigator) {
+class PermalinkHandler @Inject constructor(private val session: Session,
+                                           private val navigator: Navigator) {
 
     fun launch(context: Context, deepLink: String?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean {
         val uri = deepLink?.let { Uri.parse(it) }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt
index 8709cd35..568e8498 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt
@@ -16,18 +16,20 @@
 
 package im.vector.riotredesign.features.home.group
 
+import android.content.Context
 import android.os.Bundle
 import com.airbnb.mvrx.Incomplete
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.fragmentViewModel
 import im.vector.matrix.android.api.session.group.model.GroupSummary
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.StateView
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.home.HomeNavigator
 import kotlinx.android.synthetic.main.fragment_group_list.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback {
 
@@ -38,11 +40,17 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback
     }
 
     private val viewModel: GroupListViewModel by fragmentViewModel()
-    private val homeNavigator by inject()
-    private val groupController by inject()
+
+    @Inject lateinit var groupListViewModelFactory: GroupListViewModel.Factory
+    @Inject lateinit var homeNavigator: HomeNavigator
+    @Inject lateinit var groupController: GroupSummaryController
 
     override fun getLayoutResId() = R.layout.fragment_group_list
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         groupController.callback = this
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt
index 4b15ac71..fdb62098 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt
@@ -19,8 +19,11 @@ package im.vector.riotredesign.features.home.group
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import arrow.core.Option
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.group.model.GroupSummary
 import im.vector.matrix.rx.rx
@@ -28,24 +31,27 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.utils.LiveEvent
-import org.koin.android.ext.android.get
 
 const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID"
 
-class GroupListViewModel(initialState: GroupListViewState,
-                         private val selectedGroupHolder: SelectedGroupStore,
-                         private val session: Session,
-                         private val stringProvider: StringProvider
+class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState,
+                                                     private val selectedGroupHolder: SelectedGroupStore,
+                                                     private val session: Session,
+                                                     private val stringProvider: StringProvider
 ) : VectorViewModel(initialState) {
 
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: GroupListViewState): GroupListViewModel
+    }
+
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? {
-            val currentSession = viewModelContext.activity.get()
-            val selectedGroupHolder = viewModelContext.activity.get()
-            val stringProvider = viewModelContext.activity.get()
-            return GroupListViewModel(state, selectedGroupHolder, currentSession, stringProvider)
+            val groupListFragment: GroupListFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return groupListFragment.groupListViewModelFactory.create(state)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt
index fe40b162..02904be3 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt
@@ -18,8 +18,10 @@ package im.vector.riotredesign.features.home.group
 
 import com.airbnb.epoxy.TypedEpoxyController
 import im.vector.matrix.android.api.session.group.model.GroupSummary
+import im.vector.riotredesign.features.home.AvatarRenderer
+import javax.inject.Inject
 
-class GroupSummaryController : TypedEpoxyController() {
+class GroupSummaryController @Inject constructor(private val avatarRenderer: AvatarRenderer): TypedEpoxyController() {
 
     var callback: Callback? = null
 
@@ -34,6 +36,7 @@ class GroupSummaryController : TypedEpoxyController() {
         summaries.forEach { groupSummary ->
             val isSelected = groupSummary.groupId == selected?.groupId
             groupSummaryItem {
+                avatarRenderer(avatarRenderer)
                 id(groupSummary.groupId)
                 groupId(groupSummary.groupId)
                 groupName(groupSummary.displayName)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt
index 16749849..1abb59bd 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt
@@ -29,6 +29,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
 @EpoxyModelClass(layout = R.layout.item_group)
 abstract class GroupSummaryItem : VectorEpoxyModel() {
 
+    @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
     @EpoxyAttribute lateinit var groupName: CharSequence
     @EpoxyAttribute lateinit var groupId: String
     @EpoxyAttribute var avatarUrl: String? = null
@@ -40,7 +41,7 @@ abstract class GroupSummaryItem : VectorEpoxyModel() {
         holder.rootView.setOnClickListener { listener?.invoke() }
         holder.groupNameView.text = groupName
         holder.rootView.isChecked = selected
-        AvatarRenderer.render(avatarUrl, groupId, groupName.toString(), holder.avatarImageView)
+        avatarRenderer.render(avatarUrl, groupId, groupName.toString(), holder.avatarImageView)
     }
 
     class Holder : VectorEpoxyHolder() {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt
index 5ee3cc4b..5bd0cc4e 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt
@@ -19,5 +19,8 @@ package im.vector.riotredesign.features.home.group
 import arrow.core.Option
 import im.vector.matrix.android.api.session.group.model.GroupSummary
 import im.vector.riotredesign.core.utils.RxStore
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class SelectedGroupStore : RxStore>(Option.empty())
+@Singleton
+class SelectedGroupStore @Inject constructor() : RxStore>(Option.empty())
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
index 028e2740..f7a3b964 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
@@ -61,6 +61,7 @@ import im.vector.matrix.android.api.session.room.model.message.*
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.dialogs.DialogListItem
 import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
 import im.vector.riotredesign.core.extensions.hideKeyboard
@@ -68,7 +69,14 @@ import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.extensions.setTextOrHide
 import im.vector.riotredesign.core.glide.GlideApp
 import im.vector.riotredesign.core.platform.VectorBaseFragment
-import im.vector.riotredesign.core.utils.*
+import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
+import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
+import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA
+import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA
+import im.vector.riotredesign.core.utils.checkPermissions
+import im.vector.riotredesign.core.utils.copyToClipboard
+import im.vector.riotredesign.core.utils.openCamera
+import im.vector.riotredesign.core.utils.shareMedia
 import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
 import im.vector.riotredesign.features.autocomplete.command.CommandAutocompletePolicy
 import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
@@ -98,14 +106,11 @@ import kotlinx.android.parcel.Parcelize
 import kotlinx.android.synthetic.main.fragment_room_detail.*
 import kotlinx.android.synthetic.main.merge_composer_layout.view.*
 import org.commonmark.parser.Parser
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
-import org.koin.core.parameter.parametersOf
 import ru.noties.markwon.Markwon
 import ru.noties.markwon.html.HtmlPlugin
 import timber.log.Timber
 import java.io.File
+import javax.inject.Inject
 
 
 @Parcelize
@@ -155,24 +160,27 @@ class RoomDetailFragment :
     }
 
     private val roomDetailArgs: RoomDetailArgs by args()
-    private val session by inject()
     private val glideRequests by lazy {
         GlideApp.with(this)
     }
 
     private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
     private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
-    private val timelineEventController: TimelineEventController by inject { parametersOf(this) }
-    private val commandAutocompletePolicy = CommandAutocompletePolicy()
-    private val autocompleteCommandPresenter: AutocompleteCommandPresenter by inject { parametersOf(this) }
-    private val autocompleteUserPresenter: AutocompleteUserPresenter by inject { parametersOf(this) }
-    private val permalinkHandler: PermalinkHandler by inject()
-
-    private val notificationDrawerManager by inject()
 
+    @Inject lateinit var session: Session
+    @Inject lateinit var avatarRenderer: AvatarRenderer
+    @Inject lateinit var timelineEventController: TimelineEventController
+    @Inject lateinit var commandAutocompletePolicy: CommandAutocompletePolicy
+    @Inject lateinit var autocompleteCommandPresenter: AutocompleteCommandPresenter
+    @Inject lateinit var autocompleteUserPresenter: AutocompleteUserPresenter
+    @Inject lateinit var permalinkHandler: PermalinkHandler
+    @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
+    @Inject lateinit var roomDetailViewModelFactory: RoomDetailViewModel.Factory
+    @Inject lateinit var textComposerViewModelFactory: TextComposerViewModel.Factory
     private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
     private lateinit var scrollOnHighlightedEventCallback: ScrollOnHighlightedEventCallback
 
+
     override fun getLayoutResId() = R.layout.fragment_room_detail
 
     private lateinit var actionViewModel: ActionsHandler
@@ -180,10 +188,13 @@ class RoomDetailFragment :
     @BindView(R.id.composerLayout)
     lateinit var composerLayout: TextComposerView
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
-        bindScope(getOrCreateScope(HomeModule.ROOM_DETAIL_SCOPE))
         setupToolbar(roomToolbar)
         setupRecyclerView()
         setupComposer()
@@ -215,7 +226,7 @@ class RoomDetailFragment :
                     commandAutocompletePolicy.enabled = true
                     val uid = session.sessionParams.credentials.userId
                     val meMember = session.getRoom(roomId)?.getRoomMember(uid)
-                    AvatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
+                    avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
                     composerLayout.collapse()
                 }
                 SendMode.EDIT,
@@ -262,7 +273,7 @@ class RoomDetailFragment :
                         composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_reply))
                     }
 
-                    AvatarRenderer.render(event.senderAvatar, event.root.senderId
+                    avatarRenderer.render(event.senderAvatar, event.root.senderId
                             ?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
 
                     composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)
@@ -384,7 +395,7 @@ class RoomDetailFragment :
 
                         // Add the span
                         val user = session.getUser(item.userId)
-                        val span = PillImageSpan(glideRequests, context!!, item.userId, user)
+                        val span = PillImageSpan(glideRequests, avatarRenderer, requireContext(), item.userId, user)
                         span.bind(composerLayout.composerEditText)
 
                         editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
@@ -493,7 +504,7 @@ class RoomDetailFragment :
 
             val uid = session.sessionParams.credentials.userId
             val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
-            AvatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
+            avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
 
         } else if (summary?.membership == Membership.INVITE && inviter != null) {
             inviteView.visibility = View.VISIBLE
@@ -507,7 +518,7 @@ class RoomDetailFragment :
     private fun renderRoomSummary(state: RoomDetailViewState) {
         state.asyncRoomSummary()?.let {
             roomToolbarTitleView.text = it.displayName
-            AvatarRenderer.render(it, roomToolbarAvatarImageView)
+            avatarRenderer.render(it, roomToolbarAvatarImageView)
             roomToolbarSubtitleView.setTextOrHide(it.topic)
         }
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt
index ce777c29..d34df056 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt
@@ -19,10 +19,13 @@ package im.vector.riotredesign.features.home.room.detail
 import android.text.TextUtils
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.ViewModelContext
 import com.jakewharton.rxrelay2.BehaviorRelay
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.content.ContentAttachmentData
@@ -41,15 +44,14 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
 import io.reactivex.rxkotlin.subscribeBy
 import org.commonmark.parser.Parser
 import org.commonmark.renderer.html.HtmlRenderer
-import org.koin.android.ext.android.get
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.*
 import java.util.concurrent.TimeUnit
 
 
-class RoomDetailViewModel(initialState: RoomDetailViewState,
-                          private val session: Session
+class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
+                                                      private val session: Session
 ) : VectorViewModel(initialState) {
 
     private val room = session.getRoom(initialState.roomId)!!
@@ -63,14 +65,24 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
     }
     private var timeline = room.createTimeline(eventId, allowedTypes)
 
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: RoomDetailViewState): RoomDetailViewModel
+    }
+
     companion object : MvRxViewModelFactory {
 
         const val PAGINATION_COUNT = 50
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
-            val currentSession = viewModelContext.activity.get()
-            return RoomDetailViewModel(state, currentSession)
+            val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.roomDetailViewModelFactory.create(state)
+        }
+
+        override fun initialState(viewModelContext: ViewModelContext): RoomDetailViewState? {
+
+            return super.initialState(viewModelContext)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/composer/TextComposerViewModel.kt
index 41c09a19..ef378a29 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/composer/TextComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/composer/TextComposerViewModel.kt
@@ -17,35 +17,44 @@
 package im.vector.riotredesign.features.home.room.detail.composer
 
 import arrow.core.Option
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
 import com.jakewharton.rxrelay2.BehaviorRelay
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.matrix.rx.rx
 import im.vector.riotredesign.core.platform.VectorViewModel
+import im.vector.riotredesign.features.home.room.detail.RoomDetailFragment
 import io.reactivex.Observable
 import io.reactivex.functions.BiFunction
-import org.koin.android.ext.android.get
 import java.util.concurrent.TimeUnit
 
 typealias AutocompleteUserQuery = CharSequence
 
-class TextComposerViewModel(initialState: TextComposerViewState,
-                            private val session: Session
+class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
+                                                        private val session: Session
 ) : VectorViewModel(initialState) {
 
+
     private val room = session.getRoom(initialState.roomId)!!
     private val roomId = initialState.roomId
 
     private val usersQueryObservable = BehaviorRelay.create>()
 
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: TextComposerViewState): TextComposerViewModel
+    }
+
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel? {
-            val currentSession = viewModelContext.activity.get()
-            return TextComposerViewModel(state, currentSession)
+            val fragment : RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.textComposerViewModelFactory.create(state)
         }
     }
 
@@ -80,7 +89,7 @@ class TextComposerViewModel(initialState: TextComposerViewState,
                     } else {
                         users.filter {
                             it.displayName?.startsWith(prefix = filter, ignoreCase = true)
-                                    ?: false
+                            ?: false
                         }
                     }
                 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt
index 4c49ec2d..afb44b61 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt
@@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.room.timeline.Timeline
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.riotredesign.core.epoxy.LoadingItem_
 import im.vector.riotredesign.core.extensions.localDateTime
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.*
 import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem
@@ -39,11 +40,14 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInf
 import im.vector.riotredesign.features.media.ImageContentRenderer
 import im.vector.riotredesign.features.media.VideoContentRenderer
 import org.threeten.bp.LocalDateTime
+import javax.inject.Inject
 
-class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
-                              private val timelineItemFactory: TimelineItemFactory,
-                              private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
-                              private val backgroundHandler: Handler = TimelineAsyncHelper.getBackgroundHandler()
+class TimelineEventController @Inject constructor(private val dateFormatter: TimelineDateFormatter,
+                                                  private val timelineItemFactory: TimelineItemFactory,
+                                                  private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
+                                                  private val avatarRenderer: AvatarRenderer,
+                                                  @TimelineEventControllerHandler
+                                                  private val backgroundHandler: Handler
 ) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener {
 
     interface Callback : ReactionPillCallback, AvatarCallback, BaseCallback, UrlClickCallback {
@@ -291,7 +295,7 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
                     collapsedEventIds.removeAll(mergedEventIds)
                 }
                 val mergeId = mergedEventIds.joinToString(separator = "_") { it }
-                MergedHeaderItem(isCollapsed, mergeId, mergedData) {
+                MergedHeaderItem(isCollapsed, mergeId, mergedData, avatarRenderer) {
                     mergeItemCollapseStates[event.localId] = it
                     requestModelBuild()
                 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/RoomMemberEventHelper.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventControllerHandler.kt
similarity index 72%
rename from vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/RoomMemberEventHelper.kt
rename to vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventControllerHandler.kt
index 83b1fa6c..7ac38d97 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/RoomMemberEventHelper.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventControllerHandler.kt
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package im.vector.riotredesign.features.home.room.detail.timeline.helper
+package im.vector.riotredesign.features.home.room.detail.timeline
 
-import im.vector.matrix.android.api.session.events.model.toModel
-import im.vector.matrix.android.api.session.room.model.RoomMember
-import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
+import javax.inject.Qualifier
 
-object RoomMemberEventHelper {
-
-
-}
\ No newline at end of file
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class TimelineEventControllerHandler
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ActionsHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ActionsHandler.kt
index 84cfc40f..1755fe6b 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ActionsHandler.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ActionsHandler.kt
@@ -18,11 +18,12 @@ package im.vector.riotredesign.features.home.room.detail.timeline.action
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import im.vector.riotredesign.core.utils.LiveEvent
+import javax.inject.Inject
 
 /**
  * Activity shared view model to handle message actions
  */
-class ActionsHandler : ViewModel() {
+class ActionsHandler @Inject constructor() : ViewModel() {
 
     data class ActionData(
             val actionId: String,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
index 95777b8d..9ae2e4e5 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
@@ -33,16 +33,20 @@ import com.airbnb.mvrx.withState
 import com.google.android.material.bottomsheet.BottomSheetBehavior
 import com.google.android.material.bottomsheet.BottomSheetDialog
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import kotlinx.android.synthetic.main.bottom_sheet_message_actions.*
+import javax.inject.Inject
 
 /**
  * Bottom sheet fragment that shows a message preview with list of contextual actions
  * (Includes fragments for quick reactions and list of actions)
  */
-class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
+class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
 
+    @Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory
+    @Inject lateinit var avatarRenderer: AvatarRenderer
     private val viewModel: MessageActionsViewModel by fragmentViewModel(MessageActionsViewModel::class)
 
     private lateinit var actionHandlerModel: ActionsHandler
@@ -59,6 +63,9 @@ class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
     @BindView(R.id.bottom_sheet_message_preview_body)
     lateinit var messageBodyTextView: TextView
 
+    override fun injectWith(screenComponent: ScreenComponent) {
+        screenComponent.inject(this)
+    }
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
         val view = inflater.inflate(R.layout.bottom_sheet_message_actions, container, false)
@@ -122,7 +129,7 @@ class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
             senderNameTextView.text = it.senderName
             messageBodyTextView.text = it.messageBody
             messageTimestampText.text = it.ts
-            AvatarRenderer.render(it.senderAvatarPath, it.userId, it.senderName, senderAvatarImageView)
+            avatarRenderer.render(it.senderAvatarPath, it.userId, it.senderName, senderAvatarImageView)
         } else {
             bottom_sheet_message_preview.isVisible = false
         }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index dfe37bf9..16dd5d1b 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -19,6 +19,8 @@ import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.api.session.events.model.toModel
@@ -27,17 +29,16 @@ import im.vector.matrix.android.api.session.room.model.message.MessageTextConten
 import im.vector.matrix.android.api.session.room.model.message.MessageType
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
-import org.commonmark.parser.Parser
-import org.koin.android.ext.android.get
-import org.koin.core.parameter.parametersOf
-import ru.noties.markwon.Markwon
-import ru.noties.markwon.html.HtmlPlugin
-import timber.log.Timber
+import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
+import im.vector.riotredesign.features.html.EventHtmlRenderer
 import java.text.SimpleDateFormat
 import java.util.*
 
 
 data class MessageActionState(
+        val roomId: String,
+        val eventId: String,
+        val informationData: MessageInformationData,
         val userId: String = "",
         val senderName: String = "",
         val messageBody: CharSequence? = null,
@@ -45,65 +46,78 @@ data class MessageActionState(
         val showPreview: Boolean = false,
         val canReact: Boolean = false,
         val senderAvatarPath: String? = null)
-    : MvRxState
+    : MvRxState {
+
+    constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
+
+}
 
 /**
  * Information related to an event and used to display preview in contextual bottomsheet.
  */
-class MessageActionsViewModel(initialState: MessageActionState) : VectorViewModel(initialState) {
+class MessageActionsViewModel @AssistedInject constructor(@Assisted
+                                                          initialState: MessageActionState,
+                                                          private val eventHtmlRenderer: EventHtmlRenderer,
+                                                          private val session: Session,
+                                                          private val noticeEventFormatter: NoticeEventFormatter
+) : VectorViewModel(initialState) {
+
+    private val roomId = initialState.roomId
+    private val eventId = initialState.eventId
+    private val informationData = initialState.informationData
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: MessageActionState): MessageActionsViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
-        override fun initialState(viewModelContext: ViewModelContext): MessageActionState? {
-            val currentSession = viewModelContext.activity.get()
-            val fragment = (viewModelContext as? FragmentViewModelContext)?.fragment
-            val noticeFormatter = fragment?.get(parameters = { parametersOf(fragment) })
-            val parcel = viewModelContext.args as TimelineEventFragmentArgs
-
-            val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
-
-            val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
-            var body: CharSequence? = null
-            val originTs = event?.root?.originServerTs
-            return if (event != null) {
-                when (event.root.getClearType()) {
-                    EventType.MESSAGE     -> {
-                        val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
-                                ?: event.root.getClearContent().toModel()
-                                ?: event.root.content.toModel()
-                        body = messageContent?.body
-                        if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
-                            val parser = Parser.builder().build()
-                            val document = parser.parse(messageContent.formattedBody
-                                    ?: messageContent.body)
-                            body = Markwon.builder(viewModelContext.activity)
-                                    .usePlugin(HtmlPlugin.create()).build().render(document)
-                        }
-                    }
-                    EventType.STATE_ROOM_NAME,
-                    EventType.STATE_ROOM_TOPIC,
-                    EventType.STATE_ROOM_MEMBER,
-                    EventType.STATE_HISTORY_VISIBILITY,
-                    EventType.CALL_INVITE,
-                    EventType.CALL_HANGUP,
-                    EventType.CALL_ANSWER -> {
-                        body = noticeFormatter?.format(event)
-                    }
-                }
-                MessageActionState(
-                        userId = event.root.senderId ?: "",
-                        senderName = parcel.informationData.memberName?.toString() ?: "",
-                        messageBody = body,
-                        ts = dateFormat.format(Date(originTs ?: 0)),
-                        showPreview = body != null,
-                        canReact = event.root.type == EventType.MESSAGE && event.sendState.isSent(),
-                        senderAvatarPath = parcel.informationData.avatarUrl
-                )
-            } else {
-                //can this happen?
-                Timber.e("Failed to retrieve event")
-                null
-            }
+        override fun create(viewModelContext: ViewModelContext, state: MessageActionState): MessageActionsViewModel? {
+            val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.messageActionViewModelFactory.create(state)
         }
     }
+
+
+    init {
+        setState { reduceState(this) }
+    }
+
+    private fun reduceState(state: MessageActionState): MessageActionState {
+        val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
+        val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
+        var body: CharSequence? = null
+        val originTs = event.root.originServerTs
+        when (event.root.getClearType()) {
+            EventType.MESSAGE     -> {
+                val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
+                                                      ?: event.root.getClearContent().toModel()
+                body = messageContent?.body
+                if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
+                    body = eventHtmlRenderer.render(messageContent.formattedBody
+                                                    ?: messageContent.body)
+                }
+            }
+            EventType.STATE_ROOM_NAME,
+            EventType.STATE_ROOM_TOPIC,
+            EventType.STATE_ROOM_MEMBER,
+            EventType.STATE_HISTORY_VISIBILITY,
+            EventType.CALL_INVITE,
+            EventType.CALL_HANGUP,
+            EventType.CALL_ANSWER -> {
+                body = noticeEventFormatter.format(event)
+            }
+        }
+        return state.copy(
+                userId = event.root.senderId ?: "",
+                senderName = informationData.memberName?.toString() ?: "",
+                messageBody = body,
+                ts = dateFormat.format(Date(originTs ?: 0)),
+                showPreview = body != null,
+                canReact = event.root.type == EventType.MESSAGE && event.sendState.isSent(),
+                senderAvatarPath = informationData.avatarUrl
+        )
+    }
+
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt
index 2b47eae3..a45df7e1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt
@@ -23,24 +23,31 @@ import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
-import com.airbnb.mvrx.BaseMvRxFragment
 import com.airbnb.mvrx.MvRx
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
+import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.themes.ThemeUtils
+import javax.inject.Inject
 
 /**
  * Fragment showing the list of available contextual action for a given message.
  */
-class MessageMenuFragment : BaseMvRxFragment() {
+class MessageMenuFragment : VectorBaseFragment() {
 
+    @Inject lateinit var messageMenuViewModelFactory: MessageMenuViewModel.Factory
     private val viewModel: MessageMenuViewModel by fragmentViewModel(MessageMenuViewModel::class)
-
     private var addSeparators = false
-
     var interactionListener: InteractionListener? = null
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
+    override fun getLayoutResId() = R.layout.fragment_message_menu
+
     override fun invalidate() = withState(viewModel) { state ->
 
         val linearLayout = view as? LinearLayout
@@ -68,14 +75,6 @@ class MessageMenuFragment : BaseMvRxFragment() {
     }
 
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        //we just create programmatically
-        val contentView = LinearLayout(context)
-        contentView.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
-        contentView.orientation = LinearLayout.VERTICAL
-        return contentView
-    }
-
     private fun inflateActionView(action: SimpleAction, inflater: LayoutInflater, container: ViewGroup?): View? {
         return inflater.inflate(R.layout.adapter_item_action, container, false)?.apply {
             if (action.iconResId != null) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
index 1d9c33f6..fcd73e82 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
@@ -15,9 +15,12 @@
  */
 package im.vector.riotredesign.features.home.room.detail.timeline.action
 
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.api.session.events.model.toContent
@@ -29,44 +32,78 @@ import im.vector.matrix.android.api.session.room.send.SendState
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.VectorViewModel
+import im.vector.riotredesign.core.resources.StringProvider
+import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import org.json.JSONObject
-import org.koin.android.ext.android.get
 
 data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
 
-data class MessageMenuState(val actions: List = emptyList()) : MvRxState
+data class MessageMenuState(
+        val roomId: String,
+        val eventId: String,
+        val informationData: MessageInformationData,
+        val actions: List = emptyList()
+) : MvRxState {
+
+    constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
+
+}
 
 /**
  * Manages list actions for a given message (copy / paste / forward...)
  */
-class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel(initialState) {
+class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: MessageMenuState,
+                                                       private val session: Session,
+                                                       private val stringProvider: StringProvider) : VectorViewModel(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: MessageMenuState): MessageMenuViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
-        override fun initialState(viewModelContext: ViewModelContext): MessageMenuState? {
-            // Args are accessible from the context.
-            val currentSession = viewModelContext.activity.get()
-            val parcel = viewModelContext.args as TimelineEventFragmentArgs
-            val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
-                    ?: return null
+        const val ACTION_ADD_REACTION = "add_reaction"
+        const val ACTION_COPY = "copy"
+        const val ACTION_EDIT = "edit"
+        const val ACTION_QUOTE = "quote"
+        const val ACTION_REPLY = "reply"
+        const val ACTION_SHARE = "share"
+        const val ACTION_RESEND = "resend"
+        const val ACTION_DELETE = "delete"
+        const val VIEW_SOURCE = "VIEW_SOURCE"
+        const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE"
+        const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK"
+        const val ACTION_FLAG = "ACTION_FLAG"
+        const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
+        const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"
 
-            val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
-                    ?: event.root.content.toModel()
-            val type = messageContent?.type
+        override fun create(viewModelContext: ViewModelContext, state: MessageMenuState): MessageMenuViewModel? {
+            val fragment: MessageMenuFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.messageMenuViewModelFactory.create(state)
+        }
+    }
 
-            if (!event.sendState.isSent()) {
-                //Resend and Delete
-                return MessageMenuState(
-                        //TODO
-                        listOf(
+    init {
+        setState { reduceState(this) }
+    }
+
+    private fun reduceState(state: MessageMenuState): MessageMenuState {
+        val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
+
+        val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
+                                              ?: event.root.content.toModel()
+        val type = messageContent?.type
+
+        val actions = if (!event.sendState.isSent()) {
+            //Resend and Delete
+            listOf(
 //                                SimpleAction(ACTION_RESEND, R.string.resend, R.drawable.ic_send, event.root.eventId),
 //                                //TODO delete icon
 //                                SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId)
-                        )
-                )
-            }
-
-            val actions = ArrayList().apply {
+            )
+        } else {
+            ArrayList().apply {
 
                 if (event.sendState == SendState.SENDING) {
                     //TODO add cancel?
@@ -86,28 +123,28 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel true
-                else                     -> false
-            }
-        }
-
-        private fun canReact(event: TimelineEvent, messageContent: MessageContent?): Boolean {
-            //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
-            return event.root.getClearType() == EventType.MESSAGE
-        }
-
-        private fun canQuote(event: TimelineEvent, messageContent: MessageContent?): Boolean {
-            //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
-            if (event.root.getClearType() != EventType.MESSAGE) return false
-            return when (messageContent?.type) {
-                MessageType.MSGTYPE_TEXT,
-                MessageType.MSGTYPE_NOTICE,
-                MessageType.MSGTYPE_EMOTE,
-                MessageType.FORMAT_MATRIX_HTML,
-                MessageType.MSGTYPE_LOCATION -> {
-                    true
-                }
-                else                         -> false
+
+    private fun canReply(event: TimelineEvent, messageContent: MessageContent?): Boolean {
+        //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
+        if (event.root.getClearType() != EventType.MESSAGE) return false
+        return when (messageContent?.type) {
+            MessageType.MSGTYPE_TEXT,
+            MessageType.MSGTYPE_NOTICE,
+            MessageType.MSGTYPE_EMOTE,
+            MessageType.MSGTYPE_IMAGE,
+            MessageType.MSGTYPE_VIDEO,
+            MessageType.MSGTYPE_AUDIO,
+            MessageType.MSGTYPE_FILE -> true
+            else                     -> false
+        }
+    }
+
+    private fun canReact(event: TimelineEvent, messageContent: MessageContent?): Boolean {
+        //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
+        return event.root.getClearType() == EventType.MESSAGE
+    }
+
+    private fun canQuote(event: TimelineEvent, messageContent: MessageContent?): Boolean {
+        //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
+        if (event.root.getClearType() != EventType.MESSAGE) return false
+        return when (messageContent?.type) {
+            MessageType.MSGTYPE_TEXT,
+            MessageType.MSGTYPE_NOTICE,
+            MessageType.MSGTYPE_EMOTE,
+            MessageType.FORMAT_MATRIX_HTML,
+            MessageType.MSGTYPE_LOCATION -> {
+                true
             }
+            else                         -> false
         }
+    }
 
         private fun canRedact(event: TimelineEvent, myUserId: String): Boolean {
             //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
@@ -182,12 +219,13 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel {
-                    true
-                }
-                else                         -> false
+    private fun canCopy(type: String?): Boolean {
+        return when (type) {
+            MessageType.MSGTYPE_TEXT,
+            MessageType.MSGTYPE_NOTICE,
+            MessageType.MSGTYPE_EMOTE,
+            MessageType.FORMAT_MATRIX_HTML,
+            MessageType.MSGTYPE_LOCATION -> {
+                true
             }
+            else                         -> false
         }
+    }
 
 
-        private fun canShare(type: String?): Boolean {
-            return when (type) {
-                MessageType.MSGTYPE_IMAGE,
-                MessageType.MSGTYPE_AUDIO,
-                MessageType.MSGTYPE_VIDEO -> {
-                    true
-                }
-                else                      -> false
+    private fun canShare(type: String?): Boolean {
+        return when (type) {
+            MessageType.MSGTYPE_IMAGE,
+            MessageType.MSGTYPE_AUDIO,
+            MessageType.MSGTYPE_VIDEO -> {
+                true
             }
+            else                      -> false
         }
-
-        const val ACTION_ADD_REACTION = "add_reaction"
-        const val ACTION_COPY = "copy"
-        const val ACTION_EDIT = "edit"
-        const val ACTION_QUOTE = "quote"
-        const val ACTION_REPLY = "reply"
-        const val ACTION_SHARE = "share"
-        const val ACTION_RESEND = "resend"
-        const val ACTION_DELETE = "delete"
-        const val VIEW_SOURCE = "VIEW_SOURCE"
-        const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE"
-        const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK"
-        const val ACTION_FLAG = "ACTION_FLAG"
-        const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
-        const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"
-
-
     }
 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt
index e7c0813e..994247fa 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt
@@ -17,45 +17,42 @@ package im.vector.riotredesign.features.home.room.detail.timeline.action
 
 import android.graphics.Typeface
 import android.os.Bundle
-import android.view.LayoutInflater
 import android.view.View
-import android.view.ViewGroup
 import android.widget.TextView
-import butterknife.ButterKnife
-import com.airbnb.mvrx.BaseMvRxFragment
 import com.airbnb.mvrx.MvRx
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.EmojiCompatFontProvider
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
+import im.vector.riotredesign.core.platform.VectorBaseFragment
 import kotlinx.android.synthetic.main.adapter_item_action_quick_reaction.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 /**
  * Quick Reaction Fragment (agree / like reactions)
  */
-class QuickReactionFragment : BaseMvRxFragment() {
+class QuickReactionFragment : VectorBaseFragment() {
 
     private val viewModel: QuickReactionViewModel by fragmentViewModel(QuickReactionViewModel::class)
 
     var interactionListener: InteractionListener? = null
 
-    val fontProvider by inject()
+    @Inject lateinit var fontProvider: EmojiCompatFontProvider
+    @Inject lateinit var quickReactionViewModelFactory: QuickReactionViewModel.Factory
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        val view = inflater.inflate(R.layout.adapter_item_action_quick_reaction, container, false)
-        ButterKnife.bind(this, view)
-        return view
+    override fun getLayoutResId() = R.layout.adapter_item_action_quick_reaction
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
     }
 
     lateinit var textViews: List
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-
         textViews = listOf(quickReaction0, quickReaction1, quickReaction2, quickReaction3,
                 quickReaction4, quickReaction5, quickReaction6, quickReaction7)
-
         textViews.forEachIndexed { index, textView ->
             textView.typeface = fontProvider.typeface ?: Typeface.DEFAULT
             textView.setOnClickListener {
@@ -66,7 +63,6 @@ class QuickReactionFragment : BaseMvRxFragment() {
     }
 
     override fun invalidate() = withState(viewModel) {
-
         it.quickStates.forEachIndexed { index, qs ->
             textViews[index].text = qs.reaction
             textViews[index].alpha = if (qs.isSelected) 0.2f else 1f
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt
index 541c42cc..85a22f9a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt
@@ -15,12 +15,15 @@
  */
 package im.vector.riotredesign.features.home.room.detail.timeline.action
 
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
+import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 
 /**
  * Quick reactions state, it's a toggle with 3rd state
@@ -31,16 +34,50 @@ data class ToggleState(
 )
 
 data class QuickReactionState(
-        val quickStates: List,
-        val eventId: String = "",
+        val roomId: String,
+        val eventId: String,
+        val informationData: MessageInformationData,
+        val quickStates: List = emptyList(),
         val result: ToggleState? = null
-) : MvRxState
+        /** Pair of 'clickedOn' and current toggles state*/
+) : MvRxState {
+
+    constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
+}
 
 /**
  * Quick reaction view model
  */
-class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel(initialState) {
+class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState: QuickReactionState,
+                                                         private val session: Session) : VectorViewModel(initialState) {
 
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: QuickReactionState): QuickReactionViewModel
+    }
+
+    companion object : MvRxViewModelFactory {
+
+        val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
+
+        override fun create(viewModelContext: ViewModelContext, state: QuickReactionState): QuickReactionViewModel? {
+            val fragment: QuickReactionFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.quickReactionViewModelFactory.create(state)
+        }
+    }
+
+    init {
+        setState { reduceState(this) }
+    }
+
+    private fun reduceState(state: QuickReactionState): QuickReactionState {
+        val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
+        val summary = event.annotations?.reactionsSummary
+        val quickReactions = quickEmojis.map { emoji ->
+            ToggleState(emoji, summary?.firstOrNull { it.key == emoji }?.addedByMe ?: false)
+        }
+        return state.copy(quickStates = quickReactions)
+    }
 
     fun didSelect(index: Int) = withState {
         val isSelected = it.quickStates[index].isSelected
@@ -49,21 +86,4 @@ class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel
         }
     }
 
-    companion object : MvRxViewModelFactory {
-
-        val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
-
-        override fun initialState(viewModelContext: ViewModelContext): QuickReactionState? {
-            val currentSession = viewModelContext.activity.get()
-            val parcel = viewModelContext.args as TimelineEventFragmentArgs
-            val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
-                    ?: return null
-
-            val summary = event.annotations?.reactionsSummary
-            val quickReactions = quickEmojis.map { emoji ->
-                ToggleState(emoji, summary?.firstOrNull { it.key == emoji }?.addedByMe ?: false)
-            }
-            return QuickReactionState(quickReactions, event.root.eventId ?: "")
-        }
-    }
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/BaseMvRxBottomSheetDialog.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/VectorBaseBottomSheetDialogFragment.kt
similarity index 73%
rename from vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/BaseMvRxBottomSheetDialog.kt
rename to vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/VectorBaseBottomSheetDialogFragment.kt
index c3a068be..54b518bc 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/BaseMvRxBottomSheetDialog.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/VectorBaseBottomSheetDialogFragment.kt
@@ -15,24 +15,40 @@
  */
 package im.vector.riotredesign.features.home.room.detail.timeline.action
 
+import android.content.Context
 import android.os.Bundle
 import android.os.Parcelable
 import com.airbnb.mvrx.MvRx
 import com.airbnb.mvrx.MvRxView
 import com.airbnb.mvrx.MvRxViewModelStore
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import im.vector.riotredesign.core.di.DaggerScreenComponent
+import im.vector.riotredesign.core.di.ScreenComponent
+import im.vector.riotredesign.core.platform.VectorBaseActivity
 import java.util.*
 
 /**
  * Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment)
  */
-abstract class BaseMvRxBottomSheetDialog : BottomSheetDialogFragment(), MvRxView {
+abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MvRxView {
 
     override val mvrxViewModelStore by lazy { MvRxViewModelStore(viewModelStore) }
     private lateinit var mvrxPersistedViewId: String
-
+    private lateinit var screenComponent: ScreenComponent
     final override val mvrxViewId: String by lazy { mvrxPersistedViewId }
 
+    val vectorBaseActivity: VectorBaseActivity by lazy {
+        activity as VectorBaseActivity
+    }
+
+    override fun onAttach(context: Context) {
+        screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity)
+        super.onAttach(context)
+        injectWith(screenComponent)
+    }
+
+    protected open fun injectWith(screenComponent: ScreenComponent) = Unit
+
     override fun onCreate(savedInstanceState: Bundle?) {
         mvrxViewModelStore.restoreViewModels(this, savedInstanceState)
         mvrxPersistedViewId = savedInstanceState?.getString(PERSISTED_VIEW_ID_KEY)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
index 308c68bf..9c9a58d1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
@@ -15,24 +15,30 @@ import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.EmojiCompatFontProvider
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import kotlinx.android.synthetic.main.bottom_sheet_display_reactions.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 /**
  * Bottom sheet displaying list of reactions for a given event ordered by timestamp
  */
-class ViewReactionBottomSheet : BaseMvRxBottomSheetDialog() {
+class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
 
     private val viewModel: ViewReactionViewModel by fragmentViewModel(ViewReactionViewModel::class)
 
-    private val emojiCompatFontProvider by inject()
+    @Inject lateinit var viewReactionViewModelFactory: ViewReactionViewModel.Factory
+    @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
 
     @BindView(R.id.bottom_sheet_display_reactions_list)
     lateinit var epoxyRecyclerView: EpoxyRecyclerView
 
     private val epoxyController by lazy { ViewReactionsEpoxyController(emojiCompatFontProvider.typeface) }
 
+    override fun injectWith(screenComponent: ScreenComponent) {
+        screenComponent.inject(this)
+    }
+
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
         val view = inflater.inflate(R.layout.bottom_sheet_display_reactions, container, false)
         ButterKnife.bind(this, view)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt
index de76132b..cdcebd55 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt
@@ -1,20 +1,27 @@
 package im.vector.riotredesign.features.home.room.detail.timeline.action
 
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
 import com.airbnb.mvrx.*
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
+import im.vector.matrix.rx.RxRoom
 import im.vector.riotredesign.core.extensions.localDateTime
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
-import org.koin.android.ext.android.get
+import io.reactivex.Observable
+import io.reactivex.Single
 
 
 data class DisplayReactionsViewState(
-        val eventId: String = "",
-        val roomId: String = "",
+        val eventId: String,
+        val roomId: String,
         val mapReactionKeyToMemberList: Async> = Uninitialized)
-    : MvRxState
+    : MvRxState {
+
+    constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId)
+
+}
 
 data class ReactionInfo(
         val eventId: String,
@@ -27,75 +34,66 @@ data class ReactionInfo(
 /**
  * Used to display the list of members that reacted to a given event
  */
-class ViewReactionViewModel(private val session: Session,
-                            private val timelineDateFormatter: TimelineDateFormatter,
-                            initialState: DisplayReactionsViewState) : VectorViewModel(initialState) {
+class ViewReactionViewModel @AssistedInject constructor(@Assisted
+                                                        initialState: DisplayReactionsViewState,
+                                                        private val session: Session,
+                                                        private val timelineDateFormatter: TimelineDateFormatter
+) : VectorViewModel(initialState) {
 
-    init {
-        loadReaction()
+    private val roomId = initialState.roomId
+    private val eventId = initialState.eventId
+    private val room = session.getRoom(roomId)
+            ?: throw IllegalStateException("Shouldn't use this ViewModel without a room")
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: DisplayReactionsViewState): ViewReactionViewModel
     }
 
-    fun loadReaction() = withState { state ->
-
-        try {
-            val room = session.getRoom(state.roomId)
-            val event = room?.getTimeLineEvent(state.eventId)
-            if (event == null) {
-                setState { copy(mapReactionKeyToMemberList = Fail(Throwable())) }
-                return@withState
-            }
-            var results = ArrayList()
-            event.annotations?.reactionsSummary?.forEach { sum ->
-
-                sum.sourceEvents.mapNotNull { room.getTimeLineEvent(it) }.forEach {
-                    val localDate = it.root.localDateTime()
-                    results.add(ReactionInfo(it.root.eventId!!, sum.key, it.root.senderId
-                            ?: "", it.getDisambiguatedDisplayName(), timelineDateFormatter.formatMessageHour(localDate)))
-                }
-            }
-            setState {
-                copy(
-                        mapReactionKeyToMemberList = Success(results.sortedBy { it.timestamp })
-                )
-            }
-        } catch (t: Throwable) {
-            setState {
-                copy(
-                        mapReactionKeyToMemberList = Fail(t)
-                )
-            }
-        }
-    }
-
-
     companion object : MvRxViewModelFactory {
 
-        override fun initialState(viewModelContext: ViewModelContext): DisplayReactionsViewState? {
-
-            val roomId = (viewModelContext.args as? TimelineEventFragmentArgs)?.roomId
-                    ?: return null
-            val info = (viewModelContext.args as? TimelineEventFragmentArgs)?.informationData
-                    ?: return null
-            return DisplayReactionsViewState(info.eventId, roomId)
-        }
-
         override fun create(viewModelContext: ViewModelContext, state: DisplayReactionsViewState): ViewReactionViewModel? {
-            val session = viewModelContext.activity.get()
-            val eventId = (viewModelContext.args as TimelineEventFragmentArgs).eventId
-            val lifecycleOwner = (viewModelContext as FragmentViewModelContext).fragment()
-            val liveSummary = session.getRoom(state.roomId)?.getEventSummaryLive(eventId)
-            val viewReactionViewModel = ViewReactionViewModel(session, viewModelContext.activity.get(), state)
-            // This states observes the live summary
-            // When fragment context will be destroyed the observer will automatically removed
-            liveSummary?.observe(lifecycleOwner, Observer {
-                it?.firstOrNull()?.let {
-                    viewReactionViewModel.loadReaction()
-                }
-            })
-
-            return viewReactionViewModel
+            val fragment: ViewReactionBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.viewReactionViewModelFactory.create(state)
         }
 
-
+    }
+
+    init {
+        observeEventAnnotationSummaries()
+    }
+
+    private fun observeEventAnnotationSummaries() {
+        RxRoom(room)
+                .liveAnnotationSummary(eventId)
+                .flatMapSingle { summaries ->
+                    Observable
+                            .fromIterable(summaries)
+                            .flatMapIterable {it.reactionsSummary}
+                            .toReactionInfoList()
+                }
+                .execute {
+                    copy(mapReactionKeyToMemberList = it)
+                }
+    }
+
+    private fun Observable.toReactionInfoList(): Single> {
+        return flatMap { summary ->
+            Observable
+                    .fromIterable(summary.sourceEvents)
+                    .map {
+                        val event = room.getTimeLineEvent(it)
+                                ?: throw RuntimeException("Your eventId is not valid")
+                        val localDate = event.root.localDateTime()
+                        ReactionInfo(
+                                event.root.eventId!!,
+                                summary.key,
+                                event.root.senderId ?: "",
+                                event.getDisambiguatedDisplayName(),
+                                timelineDateFormatter.formatMessageHour(localDate)
+                        )
+                    }
+        }
+                .toList()
     }
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/DefaultItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
index 8c019a09..f3782380 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/DefaultItemFactory.kt
@@ -19,8 +19,9 @@ package im.vector.riotredesign.features.home.room.detail.timeline.factory
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
 import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_
+import javax.inject.Inject
 
-class DefaultItemFactory {
+class DefaultItemFactory @Inject constructor(){
 
     fun create(event: TimelineEvent, highlight: Boolean, exception: Exception? = null): DefaultItem? {
         val text = if (exception == null) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
index 258e11fc..3f170527 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
@@ -25,15 +25,19 @@ import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
 import im.vector.riotredesign.core.resources.ColorProvider
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.utils.DebouncedClickListener
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
+import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
 import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
 import me.gujun.android.span.span
+import javax.inject.Inject
 
 // This class handles timeline events who haven't been successfully decrypted
-class EncryptedItemFactory(private val messageInformationDataFactory: MessageInformationDataFactory,
-                           private val colorProvider: ColorProvider,
-                           private val stringProvider: StringProvider) {
+class EncryptedItemFactory @Inject constructor(private val messageInformationDataFactory: MessageInformationDataFactory,
+                                               private val colorProvider: ColorProvider,
+                                               private val stringProvider: StringProvider,
+                                               private val avatarRenderer: AvatarRenderer) {
 
     fun create(event: TimelineEvent,
                nextEvent: TimelineEvent?,
@@ -58,10 +62,11 @@ class EncryptedItemFactory(private val messageInformationDataFactory: MessageInf
                 }
 
                 // TODO This is not correct format for error, change it
-                val informationData = messageInformationDataFactory.create(event, nextEvent)
 
+                val informationData = messageInformationDataFactory.create(event, nextEvent)
                 return MessageTextItem_()
                         .message(spannableStr)
+                        .avatarRenderer(avatarRenderer)
                         .informationData(informationData)
                         .highlighted(highlight)
                         .avatarCallback(callback)
@@ -72,7 +77,7 @@ class EncryptedItemFactory(private val messageInformationDataFactory: MessageInf
                                 }))
                         .longClickListener { view ->
                             return@longClickListener callback?.onEventLongClicked(informationData, null, view)
-                                    ?: false
+                                                     ?: false
                         }
             }
             else                                             -> null
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt
index 32b7b295..b2216729 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt
@@ -23,18 +23,22 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.resources.StringProvider
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
 import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
+import javax.inject.Inject
 
-class EncryptionItemFactory(private val stringProvider: StringProvider) {
+class EncryptionItemFactory @Inject constructor(private val stringProvider: StringProvider,
+                                                private val avatarRenderer: AvatarRenderer) {
 
     fun create(event: TimelineEvent,
                highlight: Boolean,
                callback: TimelineEventController.BaseCallback?): NoticeItem? {
+
         val text = buildNoticeText(event.root, event.senderName) ?: return null
         val informationData = MessageInformationData(
                 eventId = event.root.eventId ?: "?",
@@ -45,6 +49,7 @@ class EncryptionItemFactory(private val stringProvider: StringProvider) {
                 showInformation = false
         )
         return NoticeItem_()
+                .avatarRenderer(avatarRenderer)
                 .noticeText(text)
                 .informationData(informationData)
                 .highlighted(highlight)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 967b9b29..5cb7c943 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -38,7 +38,9 @@ import im.vector.riotredesign.core.linkify.VectorLinkify
 import im.vector.riotredesign.core.resources.ColorProvider
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.utils.DebouncedClickListener
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
+import im.vector.riotredesign.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.riotredesign.features.home.room.detail.timeline.item.*
 import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
@@ -46,13 +48,19 @@ import im.vector.riotredesign.features.html.EventHtmlRenderer
 import im.vector.riotredesign.features.media.ImageContentRenderer
 import im.vector.riotredesign.features.media.VideoContentRenderer
 import me.gujun.android.span.span
+import javax.inject.Inject
+
+class MessageItemFactory @Inject constructor(
+        private val avatarRenderer: AvatarRenderer,
+        private val colorProvider: ColorProvider,
+        private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
+        private val htmlRenderer: EventHtmlRenderer,
+        private val stringProvider: StringProvider,
+        private val emojiCompatFontProvider: EmojiCompatFontProvider,
+        private val imageContentRenderer: ImageContentRenderer,
+        private val messageInformationDataFactory: MessageInformationDataFactory,
+        private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder) {
 
-class MessageItemFactory(private val colorProvider: ColorProvider,
-                         private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
-                         private val htmlRenderer: EventHtmlRenderer,
-                         private val stringProvider: StringProvider,
-                         private val messageInformationDataFactory: MessageInformationDataFactory,
-                         private val emojiCompatFontProvider: EmojiCompatFontProvider) {
 
     fun create(event: TimelineEvent,
                nextEvent: TimelineEvent?,
@@ -107,6 +115,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                                       highlight: Boolean,
                                       callback: TimelineEventController.Callback?): MessageFileItem? {
         return MessageFileItem_()
+                .avatarRenderer(avatarRenderer)
                 .informationData(informationData)
                 .highlighted(highlight)
                 .avatarCallback(callback)
@@ -133,6 +142,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                                      highlight: Boolean,
                                      callback: TimelineEventController.Callback?): MessageFileItem? {
         return MessageFileItem_()
+                .avatarRenderer(avatarRenderer)
                 .informationData(informationData)
                 .highlighted(highlight)
                 .avatarCallback(callback)
@@ -178,6 +188,9 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                 rotation = messageContent.info?.rotation
         )
         return MessageImageVideoItem_()
+                .avatarRenderer(avatarRenderer)
+                .imageContentRenderer(imageContentRenderer)
+                .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
                 .playable(messageContent.info?.mimeType == "image/gif")
                 .informationData(informationData)
                 .highlighted(highlight)
@@ -221,6 +234,9 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
         )
 
         return MessageImageVideoItem_()
+                .imageContentRenderer(imageContentRenderer)
+                .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
+                .avatarRenderer(avatarRenderer)
                 .playable(true)
                 .informationData(informationData)
                 .highlighted(highlight)
@@ -261,6 +277,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                         message(linkifiedBody)
                     }
                 }
+                .avatarRenderer(avatarRenderer)
                 .informationData(informationData)
                 .highlighted(highlight)
                 .avatarCallback(callback)
@@ -326,6 +343,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
             linkifyBody(formattedBody, callback)
         }
         return MessageTextItem_()
+                .avatarRenderer(avatarRenderer)
                 .message(message)
                 .informationData(informationData)
                 .highlighted(highlight)
@@ -366,6 +384,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                         message(message)
                     }
                 }
+                .avatarRenderer(avatarRenderer)
                 .informationData(informationData)
                 .highlighted(highlight)
                 .avatarCallback(callback)
@@ -386,6 +405,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
                                   highlight: Boolean,
                                   callback: TimelineEventController.Callback?): RedactedMessageItem? {
         return RedactedMessageItem_()
+                .avatarRenderer(avatarRenderer)
                 .informationData(informationData)
                 .highlighted(highlight)
                 .avatarCallback(callback)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/NoticeItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/NoticeItemFactory.kt
index 6e38ceb0..2c8e8d52 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/NoticeItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/NoticeItemFactory.kt
@@ -17,6 +17,7 @@
 package im.vector.riotredesign.features.home.room.detail.timeline.factory
 
 import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
 import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
@@ -24,8 +25,10 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderNa
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
 import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
+import javax.inject.Inject
 
-class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
+class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEventFormatter,
+                                            private val avatarRenderer: AvatarRenderer) {
 
     fun create(event: TimelineEvent,
                highlight: Boolean,
@@ -41,6 +44,7 @@ class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
         )
 
         return NoticeItem_()
+                .avatarRenderer(avatarRenderer)
                 .noticeText(formattedText)
                 .highlighted(highlight)
                 .informationData(informationData)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
index 63d71898..75bedd77 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
@@ -28,12 +28,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
 import timber.log.Timber
+import javax.inject.Inject
 
-class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
-                          private val encryptionItemFactory: EncryptionItemFactory,
-                          private val encryptedItemFactory: EncryptedItemFactory,
-                          private val noticeItemFactory: NoticeItemFactory,
-                          private val defaultItemFactory: DefaultItemFactory) {
+class TimelineItemFactory @Inject constructor(private val messageItemFactory: MessageItemFactory,
+                                              private val encryptionItemFactory: EncryptionItemFactory,
+                                              private val encryptedItemFactory: EncryptedItemFactory,
+                                              private val noticeItemFactory: NoticeItemFactory,
+                                              private val defaultItemFactory: DefaultItemFactory) {
 
     fun create(event: TimelineEvent,
                nextEvent: TimelineEvent?,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
index cf2dc478..53f5b3bb 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
@@ -27,8 +27,9 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
 import timber.log.Timber
+import javax.inject.Inject
 
-class NoticeEventFormatter(private val stringProvider: StringProvider) {
+class NoticeEventFormatter @Inject constructor(private val stringProvider: StringProvider) {
 
     fun format(timelineEvent: TimelineEvent): CharSequence? {
         return when (val type = timelineEvent.root.getClearType()) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
index 8d395668..3707eee0 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
@@ -22,13 +22,14 @@ import android.view.View
 import android.view.ViewGroup
 import android.widget.ProgressBar
 import android.widget.TextView
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.features.media.ImageContentRenderer
 import java.io.File
+import javax.inject.Inject
 
-object ContentUploadStateTrackerBinder {
+class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {
 
     private val updateListeners = mutableMapOf()
 
@@ -36,7 +37,7 @@ object ContentUploadStateTrackerBinder {
              mediaData: ImageContentRenderer.Data,
              progressLayout: ViewGroup) {
 
-        Matrix.getInstance().currentSession?.also { session ->
+        activeSessionHolder.getActiveSession().also { session ->
             val uploadStateTracker = session.contentUploadProgressTracker()
             val updateListener = ContentMediaProgressUpdater(progressLayout, mediaData)
             updateListeners[eventId] = updateListener
@@ -45,7 +46,7 @@ object ContentUploadStateTrackerBinder {
     }
 
     fun unbind(eventId: String) {
-        Matrix.getInstance().currentSession?.also { session ->
+        activeSessionHolder.getActiveSession().also { session ->
             val uploadStateTracker = session.contentUploadProgressTracker()
             updateListeners[eventId]?.also {
                 uploadStateTracker.untrack(eventId, it)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDateFormatter.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDateFormatter.kt
index 0a4ed952..bd2b630f 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDateFormatter.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDateFormatter.kt
@@ -19,8 +19,10 @@ package im.vector.riotredesign.features.home.room.detail.timeline.helper
 import im.vector.riotredesign.core.resources.LocaleProvider
 import org.threeten.bp.LocalDateTime
 import org.threeten.bp.format.DateTimeFormatter
+import javax.inject.Inject
 
-class TimelineDateFormatter(private val localeProvider: LocaleProvider) {
+
+class TimelineDateFormatter @Inject constructor (private val localeProvider: LocaleProvider) {
 
     private val messageHourFormatter by lazy {
         DateTimeFormatter.ofPattern("H:mm", localeProvider.current())
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt
index c4e74f3f..6d985ae6 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt
@@ -17,8 +17,11 @@
 package im.vector.riotredesign.features.home.room.detail.timeline.helper
 
 import androidx.recyclerview.widget.RecyclerView
+import im.vector.riotredesign.core.di.ScreenScope
+import javax.inject.Inject
 
-class TimelineMediaSizeProvider {
+@ScreenScope
+class TimelineMediaSizeProvider @Inject constructor() {
 
     lateinit var recyclerView: RecyclerView
     private var cachedSize: Pair? = null
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt
index 26b3090b..aeafcb18 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt
@@ -40,6 +40,8 @@ abstract class AbsMessageItem : BaseEventItem() {
 
     abstract val informationData: MessageInformationData
 
+    abstract val avatarRenderer: AvatarRenderer
+
     @EpoxyAttribute
     var longClickListener: View.OnLongClickListener? = null
 
@@ -95,7 +97,7 @@ abstract class AbsMessageItem : BaseEventItem() {
             holder.timeView.visibility = View.VISIBLE
             holder.timeView.text = informationData.time
             holder.memberNameView.text = informationData.memberName
-            AvatarRenderer.render(informationData.avatarUrl, informationData.senderId, informationData.memberName?.toString(), holder.avatarImageView)
+            avatarRenderer.render(informationData.avatarUrl, informationData.senderId, informationData.memberName?.toString(), holder.avatarImageView)
             holder.view.setOnClickListener(cellClickListener)
             holder.view.setOnLongClickListener(longClickListener)
             holder.avatarImageView.setOnLongClickListener(longClickListener)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MergedHeaderItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MergedHeaderItem.kt
index 6cdd7e15..bd1a08b2 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MergedHeaderItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MergedHeaderItem.kt
@@ -30,6 +30,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
 data class MergedHeaderItem(private val isCollapsed: Boolean,
                             private val mergeId: String,
                             private val mergeData: List,
+                            private val avatarRenderer: AvatarRenderer,
                             private val onCollapsedStateChanged: (Boolean) -> Unit
 ) : BaseEventItem() {
 
@@ -63,7 +64,7 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
                 val data = distinctMergeData.getOrNull(index)
                 if (data != null && view is ImageView) {
                     view.visibility = View.VISIBLE
-                    AvatarRenderer.render(data.avatarUrl, data.userId, data.memberName, view)
+                    avatarRenderer.render(data.avatarUrl, data.userId, data.memberName, view)
                 } else {
                     view.visibility = View.GONE
                 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageFileItem.kt
index 66055984..9ec8d08b 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageFileItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageFileItem.kt
@@ -25,6 +25,7 @@ import androidx.annotation.DrawableRes
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.riotredesign.R
+import im.vector.riotredesign.features.home.AvatarRenderer
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
 abstract class MessageFileItem : AbsMessageItem() {
@@ -37,6 +38,8 @@ abstract class MessageFileItem : AbsMessageItem() {
     @EpoxyAttribute
     override lateinit var informationData: MessageInformationData
     @EpoxyAttribute
+    override lateinit var avatarRenderer: AvatarRenderer
+    @EpoxyAttribute
     var clickListener: View.OnClickListener? = null
 
     override fun bind(holder: Holder) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
index b1c884fd..33bf5732 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
@@ -22,6 +22,7 @@ import android.widget.ImageView
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.riotredesign.R
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
 import im.vector.riotredesign.features.media.ImageContentRenderer
 
@@ -33,14 +34,20 @@ abstract class MessageImageVideoItem : AbsMessageItem() {
     @EpoxyAttribute
     var message: CharSequence? = null
     @EpoxyAttribute
+    override lateinit var avatarRenderer: AvatarRenderer
+    @EpoxyAttribute
     override lateinit var informationData: MessageInformationData
     @EpoxyAttribute
     var urlClickCallback: TimelineEventController.UrlClickCallback? = null
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/NoticeItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/NoticeItem.kt
index 72ea0330..35f17186 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/NoticeItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/NoticeItem.kt
@@ -28,6 +28,9 @@ import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventCo
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
 abstract class NoticeItem : BaseEventItem() {
 
+    @EpoxyAttribute
+    lateinit var avatarRenderer: AvatarRenderer
+
     @EpoxyAttribute
     var noticeText: CharSequence? = null
 
@@ -44,7 +47,7 @@ abstract class NoticeItem : BaseEventItem() {
     override fun bind(holder: Holder) {
         super.bind(holder)
         holder.noticeTextView.text = noticeText
-        AvatarRenderer.render(
+        avatarRenderer.render(
                 informationData.avatarUrl,
                 informationData.senderId,
                 informationData.memberName?.toString()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/RedactedMessageItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/RedactedMessageItem.kt
index 7331a6f3..d690347d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/RedactedMessageItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/RedactedMessageItem.kt
@@ -3,12 +3,15 @@ package im.vector.riotredesign.features.home.room.detail.timeline.item
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.riotredesign.R
+import im.vector.riotredesign.features.home.AvatarRenderer
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
 abstract class RedactedMessageItem : AbsMessageItem() {
 
     @EpoxyAttribute
     override lateinit var informationData: MessageInformationData
+    @EpoxyAttribute
+    override lateinit var avatarRenderer: AvatarRenderer
 
     override fun getStubType(): Int = STUB_ID
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt
index 34d27004..508ea5af 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt
@@ -25,12 +25,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData
 import me.gujun.android.span.span
+import javax.inject.Inject
 
 /**
  * This class compute if data of an event (such has avatar, display name, ...) should be displayed, depending on the previous event in the timeline
  */
-class MessageInformationDataFactory(private val timelineDateFormatter: TimelineDateFormatter,
-                                    private val colorProvider: ColorProvider) {
+class MessageInformationDataFactory @Inject constructor(private val timelineDateFormatter: TimelineDateFormatter,
+                                                        private val colorProvider: ColorProvider) {
 
     fun create(event: TimelineEvent, nextEvent: TimelineEvent?): MessageInformationData {
         // Non nullability has been tested before
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/AlphabeticalRoomComparator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/AlphabeticalRoomComparator.kt
index c1031c45..1c86d908 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/AlphabeticalRoomComparator.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/AlphabeticalRoomComparator.kt
@@ -17,9 +17,9 @@
 package im.vector.riotredesign.features.home.room.list
 
 import im.vector.matrix.android.api.session.room.model.RoomSummary
+import javax.inject.Inject
 
-class AlphabeticalRoomComparator
-    : Comparator {
+class AlphabeticalRoomComparator @Inject constructor() : Comparator {
 
     override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
         return when {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt
index 2ffe559d..ccbe89bf 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt
@@ -17,8 +17,9 @@
 package im.vector.riotredesign.features.home.room.list
 
 import im.vector.matrix.android.api.session.room.model.RoomSummary
+import javax.inject.Inject
 
-class ChronologicalRoomComparator : Comparator {
+class ChronologicalRoomComparator @Inject constructor() : Comparator {
 
     override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
         var rightTimestamp = 0L
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
index 957a0d0a..665bb011 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
@@ -28,6 +28,7 @@ import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.model.RoomSummary
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.OnBackPressed
@@ -36,7 +37,7 @@ import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.home.room.list.widget.FabMenuView
 import kotlinx.android.parcel.Parcelize
 import kotlinx.android.synthetic.main.fragment_room_list.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 @Parcelize
 data class RoomListParams(
@@ -61,11 +62,16 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
     }
 
     private val roomListParams: RoomListParams by args()
-    private val roomController by inject()
+    @Inject lateinit var roomController: RoomSummaryController
+    @Inject lateinit var roomListViewModelFactory: RoomListViewModel.Factory
     private val roomListViewModel: RoomListViewModel by fragmentViewModel()
 
     override fun getLayoutResId() = R.layout.fragment_room_list
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         setupCreateRoomButton()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt
index c9ead506..7c6f7484 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt
@@ -19,9 +19,12 @@ package im.vector.riotredesign.features.home.room.list
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import arrow.core.Option
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
 import com.jakewharton.rxrelay2.BehaviorRelay
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.android.api.session.room.model.RoomSummary
@@ -29,26 +32,27 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.core.utils.LiveEvent
 import im.vector.riotredesign.features.home.HomeRoomListObservableStore
-import org.koin.android.ext.android.get
 
 typealias RoomListFilterName = CharSequence
 
-class RoomListViewModel(initialState: RoomListViewState,
-                        private val session: Session,
-                        private val homeRoomListObservableSource: HomeRoomListObservableStore,
-                        private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
-                        private val chronologicalRoomComparator: ChronologicalRoomComparator)
+class RoomListViewModel @AssistedInject constructor(@Assisted initialState: RoomListViewState,
+                                                    private val session: Session,
+                                                    private val homeRoomListObservableSource: HomeRoomListObservableStore,
+                                                    private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
+                                                    private val chronologicalRoomComparator: ChronologicalRoomComparator)
     : VectorViewModel(initialState) {
 
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: RoomListViewState): RoomListViewModel
+    }
+
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? {
-            val currentSession = viewModelContext.activity.get()
-            val homeRoomListObservableSource = viewModelContext.activity.get()
-            val chronologicalRoomComparator = viewModelContext.activity.get()
-            val alphabeticalRoomComparator = viewModelContext.activity.get()
-            return RoomListViewModel(state, currentSession, homeRoomListObservableSource, alphabeticalRoomComparator, chronologicalRoomComparator)
+            val fragment: RoomListFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.roomListViewModelFactory.create(state)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryComparator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryComparator.kt
deleted file mode 100644
index 3c31359a..00000000
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryComparator.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2019 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.riotredesign.features.home.room.list
-
-import im.vector.matrix.android.api.session.room.model.RoomSummary
-
-class RoomSummaryComparator
-    : Comparator {
-
-    override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
-        val retValue: Int
-        var leftHighlightCount = 0
-        var rightHighlightCount = 0
-        var leftNotificationCount = 0
-        var rightNotificationCount = 0
-        var rightTimestamp = 0L
-        var leftTimestamp = 0L
-
-        if (null != leftRoomSummary) {
-            leftHighlightCount = leftRoomSummary.highlightCount
-            leftNotificationCount = leftRoomSummary.notificationCount
-            leftTimestamp = leftRoomSummary.lastMessage?.originServerTs ?: 0
-        }
-        if (null != rightRoomSummary) {
-            rightHighlightCount = rightRoomSummary.highlightCount
-            rightNotificationCount = rightRoomSummary.notificationCount
-            rightTimestamp = rightRoomSummary.lastMessage?.originServerTs ?: 0
-        }
-
-        if (rightRoomSummary?.lastMessage == null) {
-            retValue = -1
-        } else if (leftRoomSummary?.lastMessage == null) {
-            retValue = 1
-        } else if (rightHighlightCount > 0 && leftHighlightCount == 0) {
-            retValue = 1
-        } else if (rightHighlightCount == 0 && leftHighlightCount > 0) {
-            retValue = -1
-        } else if (rightNotificationCount > 0 && leftNotificationCount == 0) {
-            retValue = 1
-        } else if (rightNotificationCount == 0 && leftNotificationCount > 0) {
-            retValue = -1
-        } else {
-            val deltaTimestamp = rightTimestamp - leftTimestamp
-            if (deltaTimestamp > 0) {
-                retValue = 1
-            } else if (deltaTimestamp < 0) {
-                retValue = -1
-            } else {
-                retValue = 0
-            }
-        }
-        return retValue
-
-    }
-
-}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt
index eafcb7dd..992b697e 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt
@@ -25,12 +25,15 @@ import im.vector.matrix.android.api.session.room.model.message.MessageContent
 import im.vector.riotredesign.core.extensions.localDateTime
 import im.vector.riotredesign.core.resources.DateProvider
 import im.vector.riotredesign.core.resources.StringProvider
+import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
 import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
+import javax.inject.Inject
 
-class RoomSummaryController(private val stringProvider: StringProvider,
-                            private val eventFormatter: NoticeEventFormatter,
-                            private val timelineDateFormatter: TimelineDateFormatter
+class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
+                                                private val eventFormatter: NoticeEventFormatter,
+                                                private val timelineDateFormatter: TimelineDateFormatter,
+                                                private val avatarRenderer: AvatarRenderer
 ) : TypedEpoxyController() {
 
     var callback: Callback? = null
@@ -109,6 +112,7 @@ class RoomSummaryController(private val stringProvider: StringProvider,
 
             }
             roomSummaryItem {
+                avatarRenderer(avatarRenderer)
                 id(roomSummary.roomId)
                 roomId(roomSummary.roomId)
                 lastEventTime(lastMessageTime)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt
index bf6fbcaa..1207cb13 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt
@@ -30,6 +30,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
 @EpoxyModelClass(layout = R.layout.item_room)
 abstract class RoomSummaryItem : VectorEpoxyModel() {
 
+    @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
     @EpoxyAttribute lateinit var roomName: CharSequence
     @EpoxyAttribute lateinit var roomId: String
     @EpoxyAttribute lateinit var lastFormattedEvent: CharSequence
@@ -47,7 +48,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() {
         holder.lastEventTimeView.text = lastEventTime
         holder.lastEventView.text = lastFormattedEvent
         holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadCount, showHighlighted))
-        AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
+        avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
     }
 
     class Holder : VectorEpoxyHolder() {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt b/vector/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt
index d8012607..6e26be59 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt
@@ -19,44 +19,32 @@
 package im.vector.riotredesign.features.html
 
 import android.content.Context
-import android.text.style.ClickableSpan
 import android.text.style.URLSpan
+import androidx.appcompat.app.AppCompatActivity
 import im.vector.matrix.android.api.permalinks.PermalinkData
 import im.vector.matrix.android.api.permalinks.PermalinkParser
 import im.vector.matrix.android.api.session.Session
+import im.vector.riotredesign.core.glide.GlideApp
 import im.vector.riotredesign.core.glide.GlideRequests
+import im.vector.riotredesign.features.home.AvatarRenderer
 import org.commonmark.node.BlockQuote
 import org.commonmark.node.HtmlBlock
 import org.commonmark.node.HtmlInline
 import org.commonmark.node.Node
-import ru.noties.markwon.AbstractMarkwonPlugin
-import ru.noties.markwon.Markwon
-import ru.noties.markwon.MarkwonConfiguration
-import ru.noties.markwon.MarkwonVisitor
-import ru.noties.markwon.SpannableBuilder
+import ru.noties.markwon.*
 import ru.noties.markwon.html.HtmlTag
 import ru.noties.markwon.html.MarkwonHtmlParserImpl
 import ru.noties.markwon.html.MarkwonHtmlRenderer
 import ru.noties.markwon.html.TagHandler
-import ru.noties.markwon.html.tag.BlockquoteHandler
-import ru.noties.markwon.html.tag.EmphasisHandler
-import ru.noties.markwon.html.tag.HeadingHandler
-import ru.noties.markwon.html.tag.ImageHandler
-import ru.noties.markwon.html.tag.LinkHandler
-import ru.noties.markwon.html.tag.ListHandler
-import ru.noties.markwon.html.tag.StrikeHandler
-import ru.noties.markwon.html.tag.StrongEmphasisHandler
-import ru.noties.markwon.html.tag.SubScriptHandler
-import ru.noties.markwon.html.tag.SuperScriptHandler
-import ru.noties.markwon.html.tag.UnderlineHandler
+import ru.noties.markwon.html.tag.*
 import java.util.Arrays.asList
+import javax.inject.Inject
 
-class EventHtmlRenderer(glideRequests: GlideRequests,
-                        context: Context,
-                        session: Session) {
-
+class EventHtmlRenderer @Inject constructor(context: AppCompatActivity,
+                                            val avatarRenderer: AvatarRenderer,
+                                            session: Session) {
     private val markwon = Markwon.builder(context)
-            .usePlugin(MatrixPlugin.create(glideRequests, context, session))
+            .usePlugin(MatrixPlugin.create(GlideApp.with(context), context, avatarRenderer, session))
             .build()
 
     fun render(text: String): CharSequence {
@@ -67,6 +55,7 @@ class EventHtmlRenderer(glideRequests: GlideRequests,
 
 private class MatrixPlugin private constructor(private val glideRequests: GlideRequests,
                                                private val context: Context,
+                                               private val avatarRenderer: AvatarRenderer,
                                                private val session: Session) : AbstractMarkwonPlugin() {
 
     override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
@@ -80,7 +69,7 @@ private class MatrixPlugin private constructor(private val glideRequests: GlideR
                         ImageHandler.create())
                 .setHandler(
                         "a",
-                        MxLinkHandler(glideRequests, context, session))
+                        MxLinkHandler(glideRequests, context, avatarRenderer, session))
                 .setHandler(
                         "blockquote",
                         BlockquoteHandler())
@@ -112,7 +101,7 @@ private class MatrixPlugin private constructor(private val glideRequests: GlideR
                         asList("h1", "h2", "h3", "h4", "h5", "h6"),
                         HeadingHandler())
                 .setHandler("mx-reply",
-                            MxReplyTagHandler())
+                        MxReplyTagHandler())
 
     }
 
@@ -135,14 +124,15 @@ private class MatrixPlugin private constructor(private val glideRequests: GlideR
 
     companion object {
 
-        fun create(glideRequests: GlideRequests, context: Context, session: Session): MatrixPlugin {
-            return MatrixPlugin(glideRequests, context, session)
+        fun create(glideRequests: GlideRequests, context: Context, avatarRenderer: AvatarRenderer, session: Session): MatrixPlugin {
+            return MatrixPlugin(glideRequests, context, avatarRenderer, session)
         }
     }
 }
 
 private class MxLinkHandler(private val glideRequests: GlideRequests,
                             private val context: Context,
+                            private val avatarRenderer: AvatarRenderer,
                             private val session: Session) : TagHandler() {
 
     private val linkHandler = LinkHandler()
@@ -154,7 +144,7 @@ private class MxLinkHandler(private val glideRequests: GlideRequests,
             when (permalinkData) {
                 is PermalinkData.UserLink -> {
                     val user = session.getUser(permalinkData.userId)
-                    val span = PillImageSpan(glideRequests, context, permalinkData.userId, user)
+                    val span = PillImageSpan(glideRequests, avatarRenderer, context, permalinkData.userId, user)
                     SpannableBuilder.setSpans(
                             visitor.builder(),
                             span,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/html/PillImageSpan.kt b/vector/src/main/java/im/vector/riotredesign/features/html/PillImageSpan.kt
index e83e20e6..c5939e6d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/html/PillImageSpan.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/html/PillImageSpan.kt
@@ -37,6 +37,7 @@ import java.lang.ref.WeakReference
  * It's needed to call [bind] method to start requesting avatar, otherwise only the placeholder icon will be displayed if not already cached.
  */
 class PillImageSpan(private val glideRequests: GlideRequests,
+                    private val avatarRenderer: AvatarRenderer,
                     private val context: Context,
                     private val userId: String,
                     private val user: User?) : ReplacementSpan() {
@@ -52,7 +53,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
     @UiThread
     fun bind(textView: TextView) {
         tv = WeakReference(textView)
-        AvatarRenderer.render(context, glideRequests, user?.avatarUrl, userId, displayName, target)
+        avatarRenderer.render(context, glideRequests, user?.avatarUrl, userId, displayName, target)
     }
 
     // ReplacementSpan *****************************************************************************
@@ -105,7 +106,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
             textStartPadding = textPadding
             setChipMinHeightResource(R.dimen.pill_min_height)
             setChipIconSizeResource(R.dimen.pill_avatar_size)
-            chipIcon = AvatarRenderer.getPlaceholderDrawable(context, userId, displayName)
+            chipIcon = avatarRenderer.getPlaceholderDrawable(context, userId, displayName)
             setBounds(0, 0, intrinsicWidth, intrinsicHeight)
         }
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/invite/VectorInviteView.kt b/vector/src/main/java/im/vector/riotredesign/features/invite/VectorInviteView.kt
index 54b1476a..ada994df 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/invite/VectorInviteView.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/invite/VectorInviteView.kt
@@ -24,8 +24,10 @@ import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.updateLayoutParams
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.HasScreenInjector
 import im.vector.riotredesign.features.home.AvatarRenderer
 import kotlinx.android.synthetic.main.vector_invite_view.view.*
+import javax.inject.Inject
 
 class VectorInviteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
     : ConstraintLayout(context, attrs, defStyle) {
@@ -40,9 +42,13 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
         SMALL
     }
 
+    @Inject lateinit var avatarRenderer: AvatarRenderer
     var callback: Callback? = null
 
     init {
+        if(context is HasScreenInjector){
+            context.injector().inject(this)
+        }
         View.inflate(context, R.layout.vector_invite_view, this)
         setBackgroundColor(Color.WHITE)
         inviteRejectView.setOnClickListener { callback?.onRejectInvite() }
@@ -52,7 +58,7 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
     fun render(sender: User, mode: Mode = Mode.LARGE) {
         if (mode == Mode.LARGE) {
             updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT }
-            AvatarRenderer.render(sender.avatarUrl, sender.userId, sender.displayName, inviteAvatarView)
+            avatarRenderer.render(sender.avatarUrl, sender.userId, sender.displayName, inviteAvatarView)
             inviteIdentifierView.text = sender.userId
             inviteNameView.text = sender.displayName
             inviteLabelView.text = context.getString(R.string.send_you_invite)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt b/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt
index 9a2a5e1e..b3ac5726 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt
@@ -20,8 +20,9 @@ import android.app.Activity
 import android.app.Application
 import android.os.Bundle
 import im.vector.riotredesign.features.popup.PopupAlertManager
+import javax.inject.Inject
 
-class VectorActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {
+class VectorActivityLifecycleCallbacks @Inject constructor() : Application.ActivityLifecycleCallbacks {
     override fun onActivityPaused(activity: Activity) {
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
index a3c4d426..181d36a7 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt
@@ -23,12 +23,14 @@ import android.view.View
 import android.widget.Toast
 import arrow.core.Try
 import com.jakewharton.rxbinding2.widget.RxTextView
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.auth.Authenticator
 import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
 import im.vector.matrix.android.api.session.Session
-import im.vector.matrix.android.api.session.sync.FilterService
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.di.ScreenComponent
+import im.vector.riotredesign.core.extensions.configureAndStart
 import im.vector.riotredesign.core.extensions.showPassword
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import im.vector.riotredesign.features.home.HomeActivity
@@ -37,7 +39,7 @@ import io.reactivex.Observable
 import io.reactivex.functions.Function3
 import io.reactivex.rxkotlin.subscribeBy
 import kotlinx.android.synthetic.main.activity_login.*
-import org.koin.android.ext.android.get
+import javax.inject.Inject
 
 private const val DEFAULT_HOME_SERVER_URI = "https://matrix.org"
 private const val DEFAULT_IDENTITY_SERVER_URI = "https://vector.im"
@@ -45,10 +47,16 @@ private const val DEFAULT_ANTIVIRUS_SERVER_URI = "https://matrix.org"
 
 class LoginActivity : VectorBaseActivity() {
 
-    private val authenticator = Matrix.getInstance().authenticator()
+    @Inject lateinit var authenticator: Authenticator
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
+    @Inject lateinit var pushRuleTriggerListener: PushRuleTriggerListener
 
     private var passwordShown = false
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_login)
@@ -73,12 +81,8 @@ class LoginActivity : VectorBaseActivity() {
         progressBar.visibility = View.VISIBLE
         authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback {
             override fun onSuccess(data: Session) {
-                Matrix.getInstance().currentSession = data
-                data.open()
-                data.setFilter(FilterService.FilterPreset.RiotFilter)
-                data.startSync()
-                get().startWithSession(data)
-                data.fetchPushRules()
+                activeSessionHolder.setActiveSession(data)
+                data.configureAndStart(pushRuleTriggerListener)
                 goToHome()
             }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotredesign/features/media/ImageContentRenderer.kt
index fcd7cab1..741ad995 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/media/ImageContentRenderer.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/media/ImageContentRenderer.kt
@@ -24,12 +24,14 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners
 import com.github.piasy.biv.view.BigImageView
 import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.session.content.ContentUrlResolver
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.glide.GlideApp
 import im.vector.riotredesign.core.utils.DimensionUtils.dpToPx
 import kotlinx.android.parcel.Parcelize
 import java.io.File
+import javax.inject.Inject
 
-object ImageContentRenderer {
+class ImageContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {
 
     @Parcelize
     data class Data(
@@ -57,26 +59,26 @@ object ImageContentRenderer {
         val (width, height) = processSize(data, mode)
         imageView.layoutParams.height = height
         imageView.layoutParams.width = width
-        val contentUrlResolver = Matrix.getInstance().currentSession!!.contentUrlResolver()
+        val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
         val resolvedUrl = when (mode) {
-                              Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url)
-                              Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
-                          }
-                          //Fallback to base url
-                          ?: data.url
+            Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url)
+            Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
+        }
+        //Fallback to base url
+                ?: data.url
 
         GlideApp
                 .with(imageView)
                 .load(resolvedUrl)
                 .dontAnimate()
-                .transform(RoundedCorners(dpToPx(8,imageView.context)))
+                .transform(RoundedCorners(dpToPx(8, imageView.context)))
                 .thumbnail(0.3f)
                 .into(imageView)
     }
 
     fun render(data: Data, imageView: BigImageView) {
         val (width, height) = processSize(data, Mode.THUMBNAIL)
-        val contentUrlResolver = Matrix.getInstance().currentSession!!.contentUrlResolver()
+        val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
         val fullSize = contentUrlResolver.resolveFullSize(data.url)
         val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
         imageView.showImage(
diff --git a/vector/src/main/java/im/vector/riotredesign/features/media/ImageMediaViewerActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/media/ImageMediaViewerActivity.kt
index 902c2f9b..4b980ccb 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/media/ImageMediaViewerActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/media/ImageMediaViewerActivity.kt
@@ -24,12 +24,20 @@ import android.os.Bundle
 import androidx.appcompat.widget.Toolbar
 import com.github.piasy.biv.indicator.progresspie.ProgressPieIndicator
 import com.github.piasy.biv.view.GlideImageViewFactory
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import kotlinx.android.synthetic.main.activity_image_media_viewer.*
+import javax.inject.Inject
 
 
 class ImageMediaViewerActivity : VectorBaseActivity() {
 
+    @Inject lateinit var imageContentRenderer: ImageContentRenderer
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(im.vector.riotredesign.R.layout.activity_image_media_viewer)
@@ -40,7 +48,7 @@ class ImageMediaViewerActivity : VectorBaseActivity() {
             configureToolbar(imageMediaViewerToolbar, mediaData)
             imageMediaViewerImageView.setImageViewFactory(GlideImageViewFactory())
             imageMediaViewerImageView.setProgressIndicator(ProgressPieIndicator())
-            ImageContentRenderer.render(mediaData, imageMediaViewerImageView)
+            imageContentRenderer.render(mediaData, imageMediaViewerImageView)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/media/VideoContentRenderer.kt b/vector/src/main/java/im/vector/riotredesign/features/media/VideoContentRenderer.kt
index 577b6570..8fd4b43e 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/media/VideoContentRenderer.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/media/VideoContentRenderer.kt
@@ -20,9 +20,11 @@ import android.os.Parcelable
 import android.widget.ImageView
 import android.widget.VideoView
 import im.vector.matrix.android.api.Matrix
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import kotlinx.android.parcel.Parcelize
+import javax.inject.Inject
 
-object VideoContentRenderer {
+class VideoContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder){
 
     @Parcelize
     data class Data(
@@ -32,7 +34,7 @@ object VideoContentRenderer {
     ) : Parcelable
 
     fun render(data: Data, thumbnailView: ImageView, videoView: VideoView) {
-        val contentUrlResolver = Matrix.getInstance().currentSession!!.contentUrlResolver()
+        val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
         val resolvedUrl = contentUrlResolver.resolveFullSize(data.videoUrl)
         videoView.setVideoPath(resolvedUrl)
         videoView.start()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/media/VideoMediaViewerActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/media/VideoMediaViewerActivity.kt
index 0630a2cb..d0b70302 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/media/VideoMediaViewerActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/media/VideoMediaViewerActivity.kt
@@ -20,12 +20,21 @@ import android.content.Context
 import android.content.Intent
 import android.os.Bundle
 import androidx.appcompat.widget.Toolbar
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
+import im.vector.riotredesign.features.home.AvatarRenderer
 import kotlinx.android.synthetic.main.activity_video_media_viewer.*
+import javax.inject.Inject
 
 
 class VideoMediaViewerActivity : VectorBaseActivity() {
 
+    @Inject lateinit var videoContentRenderer: VideoContentRenderer
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(im.vector.riotredesign.R.layout.activity_video_media_viewer)
@@ -34,7 +43,7 @@ class VideoMediaViewerActivity : VectorBaseActivity() {
             finish()
         } else {
             configureToolbar(videoMediaViewerToolbar, mediaData)
-            VideoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerVideoView)
+            videoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerVideoView)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotredesign/features/navigation/DefaultNavigator.kt
index d1ca13d2..bd0a82a0 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/navigation/DefaultNavigator.kt
@@ -31,9 +31,11 @@ import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
 import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewActivity
 import im.vector.riotredesign.features.settings.VectorSettingsActivity
 import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
 
-class DefaultNavigator : Navigator {
-
+@Singleton
+class DefaultNavigator @Inject constructor() : Navigator {
 
     override fun openRoom(context: Context, roomId: String, eventId: String?) {
         val args = RoomDetailArgs(roomId, eventId)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotifiableEventResolver.kt
index beaebdbd..d2e166c2 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotifiableEventResolver.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotifiableEventResolver.kt
@@ -30,6 +30,7 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event.
@@ -37,8 +38,8 @@ import timber.log.Timber
  * The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that,
  * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk.
  */
-class NotifiableEventResolver(private val stringProvider: StringProvider,
-                              private val noticeEventFormatter: NoticeEventFormatter) {
+class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider,
+                                                  private val noticeEventFormatter: NoticeEventFormatter) {
 
     //private val eventDisplay = RiotEventDisplay(context)
 
@@ -95,8 +96,8 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
             // Ok room is not known in store, but we can still display something
             val body =
                     event.annotations?.editSummary?.aggregatedContent?.toModel()?.body
-                            ?: event.root.getClearContent().toModel()?.body
-                            ?: stringProvider.getString(R.string.notification_unknown_new_event)
+                    ?: event.root.getClearContent().toModel()?.body
+                    ?: stringProvider.getString(R.string.notification_unknown_new_event)
             val roomName = stringProvider.getString(R.string.notification_unknown_room_name)
             val senderDisplayName = event.senderName ?: event.root.senderId
 
@@ -114,8 +115,8 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
             return notifiableEvent
         } else {
             val body = event.annotations?.editSummary?.aggregatedContent?.toModel()?.body
-                    ?: event.root.getClearContent().toModel()?.body
-                    ?: stringProvider.getString(R.string.notification_unknown_new_event)
+                       ?: event.root.getClearContent().toModel()?.body
+                       ?: stringProvider.getString(R.string.notification_unknown_new_event)
             val roomName = room.roomSummary?.displayName ?: ""
             val senderDisplayName = event.senderName ?: event.root.senderId
 
@@ -137,15 +138,15 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
             // TODO They will be not displayed the first time (known limitation)
             notifiableEvent.roomAvatarPath = session.contentUrlResolver()
                     .resolveThumbnail(room.roomSummary?.avatarUrl,
-                            250,
-                            250,
-                            ContentUrlResolver.ThumbnailMethod.SCALE)
+                                      250,
+                                      250,
+                                      ContentUrlResolver.ThumbnailMethod.SCALE)
 
             notifiableEvent.senderAvatarPath = session.contentUrlResolver()
                     .resolveThumbnail(event.senderAvatar,
-                            250,
-                            250,
-                            ContentUrlResolver.ThumbnailMethod.SCALE)
+                                      250,
+                                      250,
+                                      ContentUrlResolver.ThumbnailMethod.SCALE)
 
             return notifiableEvent
         }
@@ -158,7 +159,7 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
         val dName = event.senderId?.let { session.getUser(it)?.displayName }
         if (Membership.INVITE == content.membership) {
             val body = noticeEventFormatter.format(event, dName)
-                    ?: stringProvider.getString(R.string.notification_new_invitation)
+                       ?: stringProvider.getString(R.string.notification_new_invitation)
             return InviteNotifiableEvent(
                     session.sessionParams.credentials.userId,
                     eventId = event.eventId!!,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt
index 501e8e49..2272487e 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt
@@ -20,28 +20,29 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import androidx.core.app.RemoteInput
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.Room
 import im.vector.riotredesign.R
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.extensions.vectorComponent
 import timber.log.Timber
 import java.util.*
+import javax.inject.Inject
 
 /**
  * Receives actions broadcast by notification (on click, on dismiss, inline replies, etc.)
  */
-class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent {
+class NotificationBroadcastReceiver : BroadcastReceiver() {
+
+    @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
 
-    private val notificationDrawerManager by inject()
 
     override fun onReceive(context: Context?, intent: Intent?) {
         if (intent == null || context == null) return
-
         Timber.v("NotificationBroadcastReceiver received : $intent")
-
+        context.vectorComponent().inject(this)
         when (intent.action) {
             NotificationUtils.SMART_REPLY_ACTION        ->
                 handleSmartReply(intent, context)
@@ -60,7 +61,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent {
     }
 
     private fun handleMarkAsRead(context: Context, roomId: String) {
-        Matrix.getInstance().currentSession?.let { session ->
+        activeSessionHolder.getActiveSession().let { session ->
             session.getRoom(roomId)
                     ?.markAllAsRead(object : MatrixCallback {})
         }
@@ -76,7 +77,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent {
             return
         }
         val matrixId = intent.getStringExtra(EXTRA_MATRIX_ID)
-        Matrix.getInstance().currentSession?.let { session ->
+        activeSessionHolder.getActiveSession().let { session ->
             session.getRoom(roomId)?.let { room ->
                 sendMatrixEvent(message, session, room, context)
             }
@@ -95,7 +96,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent {
                 false,
                 System.currentTimeMillis(),
                 session.getUser(session.sessionParams.credentials.userId)?.displayName
-                        ?: context?.getString(R.string.notification_sender_me),
+                ?: context?.getString(R.string.notification_sender_me),
                 session.sessionParams.credentials.userId,
                 message,
                 room.roomId,
diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt
index 67c4aa85..d673cc35 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationDrawerManager.kt
@@ -24,21 +24,25 @@ import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.session.content.ContentUrlResolver
 import im.vector.riotredesign.BuildConfig
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.utils.SecretStoringUtils
 import im.vector.riotredesign.features.settings.PreferencesManager
 import timber.log.Timber
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileOutputStream
+import javax.inject.Inject
+import javax.inject.Singleton
 
 /**
  * The NotificationDrawerManager receives notification events as they arrived (from event stream or fcm) and
  * organise them in order to display them in the notification drawer.
  * Events can be grouped into the same notification, old (already read) events can be removed to do some cleaning.
  */
-class NotificationDrawerManager(val context: Context,
-                                private val outdatedDetector: OutdatedEventDetector?) {
-
+@Singleton
+class NotificationDrawerManager @Inject constructor(private val context: Context,
+                                                    private val activeSessionHolder: ActiveSessionHolder,
+                                                    private val outdatedDetector: OutdatedEventDetector?) {
 
     //The first time the notification drawer is refreshed, we force re-render of all notifications
     private var firstTime = true
@@ -167,8 +171,7 @@ class NotificationDrawerManager(val context: Context,
 
 
     fun refreshNotificationDrawer() {
-
-        val session = Matrix.getInstance().currentSession ?: return
+        val session = activeSessionHolder.getActiveSession()
         val user = session.getUser(session.sessionParams.credentials.userId)
         val myUserDisplayName = user?.displayName ?: ""
         val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/OutdatedEventDetector.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/OutdatedEventDetector.kt
index a93f5dfe..05bbc2ea 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/notifications/OutdatedEventDetector.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/OutdatedEventDetector.kt
@@ -15,9 +15,10 @@
  */
 package im.vector.riotredesign.features.notifications
 
-import im.vector.matrix.android.api.Matrix
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import javax.inject.Inject
 
-class OutdatedEventDetector {
+class OutdatedEventDetector @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {
 
     /**
      * Returns true if the given event is outdated.
@@ -28,8 +29,7 @@ class OutdatedEventDetector {
         if (notifiableEvent is NotifiableMessageEvent) {
             val eventID = notifiableEvent.eventId
             val roomID = notifiableEvent.roomId
-            val session = Matrix.getInstance().currentSession ?: return false
-            val room = session.getRoom(roomID) ?: return false
+            val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomID) ?: return false
             return room.isEventRead(eventID)
         }
         return false
diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/PushRuleTriggerListener.kt
index e0fe0fe8..3fa30b31 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/notifications/PushRuleTriggerListener.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/PushRuleTriggerListener.kt
@@ -21,9 +21,12 @@ import im.vector.matrix.android.api.pushrules.PushRuleService
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.events.model.Event
 import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
 
 
-class PushRuleTriggerListener(
+@Singleton
+class PushRuleTriggerListener @Inject constructor(
         private val resolver: NotifiableEventResolver,
         private val notificationDrawerManager: NotificationDrawerManager
 ) : PushRuleService.PushRuleListener {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReportActivity.kt
index 6f71fb0c..28e3f212 100755
--- a/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReportActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReportActivity.kt
@@ -24,22 +24,29 @@ import androidx.core.view.isVisible
 import butterknife.OnCheckedChanged
 import butterknife.OnTextChanged
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import kotlinx.android.synthetic.main.activity_bug_report.*
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * Form to send a bug report
  */
 class BugReportActivity : VectorBaseActivity() {
 
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun getLayoutRes() = R.layout.activity_bug_report
 
     override fun initUiAndData() {
         configureToolbar(bugReportToolbar)
 
-        if (BugReporter.screenshot != null) {
-            bug_report_screenshot_preview.setImageBitmap(BugReporter.screenshot)
+        if (bugReporter.screenshot != null) {
+            bug_report_screenshot_preview.setImageBitmap(bugReporter.screenshot)
         } else {
             bug_report_screenshot_preview.isVisible = false
             bug_report_button_include_screenshot.isChecked = false
@@ -87,7 +94,7 @@ class BugReportActivity : VectorBaseActivity() {
         bug_report_progress_view.isVisible = true
         bug_report_progress_view.progress = 0
 
-        BugReporter.sendBugReport(this,
+        bugReporter.sendBugReport(this,
                 bug_report_button_include_logs.isChecked,
                 bug_report_button_include_crash_logs.isChecked,
                 bug_report_button_include_screenshot.isChecked,
@@ -149,12 +156,12 @@ class BugReportActivity : VectorBaseActivity() {
 
     @OnCheckedChanged(R.id.bug_report_button_include_screenshot)
     internal fun onSendScreenshotChanged() {
-        bug_report_screenshot_preview.isVisible = bug_report_button_include_screenshot.isChecked && BugReporter.screenshot != null
+        bug_report_screenshot_preview.isVisible = bug_report_button_include_screenshot.isChecked && bugReporter.screenshot != null
     }
 
     override fun onBackPressed() {
         // Ensure there is no crash status remaining, which will be sent later on by mistake
-        BugReporter.deleteCrashFile(this)
+        bugReporter.deleteCrashFile(this)
 
         super.onBackPressed()
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReporter.kt
index 4b586f42..1808b471 100755
--- a/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReporter.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/rageshake/BugReporter.kt
@@ -30,6 +30,7 @@ import android.view.View
 import im.vector.matrix.android.api.Matrix
 import im.vector.riotredesign.BuildConfig
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
 import im.vector.riotredesign.core.extensions.toOnOff
 import im.vector.riotredesign.core.utils.getDeviceLocale
 import im.vector.riotredesign.features.settings.VectorLocale
@@ -43,19 +44,26 @@ import java.io.*
 import java.net.HttpURLConnection
 import java.util.*
 import java.util.zip.GZIPOutputStream
+import javax.inject.Inject
+import javax.inject.Singleton
 
 /**
  * BugReporter creates and sends the bug reports.
  */
-object BugReporter {
+@Singleton
+class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder){
     var inMultiWindowMode = false
 
-    // filenames
-    private const val LOG_CAT_ERROR_FILENAME = "logcatError.log"
-    private const val LOG_CAT_FILENAME = "logcat.log"
-    private const val LOG_CAT_SCREENSHOT_FILENAME = "screenshot.png"
-    private const val CRASH_FILENAME = "crash.log"
+    companion object {
+        // filenames
+        private const val LOG_CAT_ERROR_FILENAME = "logcatError.log"
+        private const val LOG_CAT_FILENAME = "logcat.log"
+        private const val LOG_CAT_SCREENSHOT_FILENAME = "screenshot.png"
+        private const val CRASH_FILENAME = "crash.log"
 
+        private const val BUFFER_SIZE = 1024 * 1024 * 50
+
+    }
 
     // the http client
     private val mOkHttpClient = OkHttpClient()
@@ -75,8 +83,6 @@ object BugReporter {
     var screenshot: Bitmap? = null
         private set
 
-    private const val BUFFER_SIZE = 1024 * 1024 * 50
-
     private val LOGCAT_CMD_ERROR = arrayOf("logcat", ///< Run 'logcat' command
             "-d", ///< Dump the log rather than continue outputting it
             "-v", // formatting
@@ -195,7 +201,7 @@ object BugReporter {
                 var userId = "undefined"
                 var olmVersion = "undefined"
 
-                Matrix.getInstance().currentSession?.let { session ->
+                activeSessionHolder.getActiveSession().let { session ->
                     userId = session.sessionParams.credentials.userId
                     deviceId = session.sessionParams.credentials.deviceId ?: "undefined"
                     olmVersion = session.getCryptoVersion(context, true)
@@ -206,7 +212,7 @@ object BugReporter {
                     val builder = BugReporterMultipartBody.Builder()
                             .addFormDataPart("text", "[RiotX] $bugDescription")
                             .addFormDataPart("app", "riot-android")
-                            .addFormDataPart("user_agent", Matrix.getInstance().getUserAgent())
+                            .addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent())
                             .addFormDataPart("user_id", userId)
                             .addFormDataPart("device_id", deviceId)
                             .addFormDataPart("version", getVersion(longFormat = true, useBuildNumber = false))
diff --git a/vector/src/main/java/im/vector/riotredesign/features/rageshake/RageShake.kt b/vector/src/main/java/im/vector/riotredesign/features/rageshake/RageShake.kt
index 446d2f48..4125ac20 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/rageshake/RageShake.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/rageshake/RageShake.kt
@@ -16,7 +16,6 @@
 
 package im.vector.riotredesign.features.rageshake
 
-import android.app.Activity
 import android.content.Context
 import android.hardware.Sensor
 import android.hardware.SensorManager
@@ -26,8 +25,10 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.edit
 import com.squareup.seismic.ShakeDetector
 import im.vector.riotredesign.R
+import javax.inject.Inject
 
-class RageShake(val activity: Activity) : ShakeDetector.Listener {
+class RageShake @Inject constructor(private val activity: AppCompatActivity,
+                                    private val bugReporter: BugReporter) : ShakeDetector.Listener {
 
     private var shakeDetector: ShakeDetector? = null
 
@@ -94,7 +95,7 @@ class RageShake(val activity: Activity) : ShakeDetector.Listener {
     }
 
     private fun openBugReportScreen() {
-        BugReporter.openBugReportScreen(activity)
+        bugReporter.openBugReportScreen(activity)
     }
 
     companion object {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/rageshake/VectorUncaughtExceptionHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/rageshake/VectorUncaughtExceptionHandler.kt
index dc50e805..bd23eb9c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/rageshake/VectorUncaughtExceptionHandler.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/rageshake/VectorUncaughtExceptionHandler.kt
@@ -16,7 +16,6 @@
 
 package im.vector.riotredesign.features.rageshake
 
-import android.annotation.SuppressLint
 import android.content.Context
 import android.os.Build
 import androidx.core.content.edit
@@ -27,12 +26,16 @@ import im.vector.riotredesign.features.version.getVersion
 import timber.log.Timber
 import java.io.PrintWriter
 import java.io.StringWriter
+import javax.inject.Inject
+import javax.inject.Singleton
 
-@SuppressLint("StaticFieldLeak")
-object VectorUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
+@Singleton
+class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter: BugReporter) : Thread.UncaughtExceptionHandler {
 
     // key to save the crash status
-    private const val PREFS_CRASH_KEY = "PREFS_CRASH_KEY"
+    companion object {
+        private const val PREFS_CRASH_KEY = "PREFS_CRASH_KEY"
+    }
 
     private val vectorVersion = getVersion(longFormat = true, useBuildNumber = true)
     private val matrixSdkVersion = Matrix.getSdkVersion()
@@ -46,9 +49,7 @@ object VectorUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
      */
     fun activate(context: Context) {
         this.context = context
-
         previousHandler = Thread.getDefaultUncaughtExceptionHandler()
-
         Thread.setDefaultUncaughtExceptionHandler(this)
     }
 
@@ -60,15 +61,10 @@ object VectorUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
      * @return the exception description
      */
     override fun uncaughtException(thread: Thread, throwable: Throwable) {
-        if (context == null) {
-            previousHandler?.uncaughtException(thread, throwable)
-            return
-        }
-
+        Timber.v("Uncaught exception: $throwable")
         PreferenceManager.getDefaultSharedPreferences(context).edit {
             putBoolean(PREFS_CRASH_KEY, true)
         }
-
         val b = StringBuilder()
         val appName = "RiotX" // TODO Matrix.getApplicationName()
 
@@ -116,7 +112,7 @@ object VectorUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
 
         val bugDescription = b.toString()
 
-        BugReporter.saveCrashReport(context, bugDescription)
+        bugReporter.saveCrashReport(context, bugDescription)
 
         // Show the classical system popup
         previousHandler?.uncaughtException(thread, throwable)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserFragment.kt
index 6f47538a..13ade472 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserFragment.kt
@@ -15,34 +15,27 @@
  */
 package im.vector.riotredesign.features.reactions
 
-import androidx.lifecycle.ViewModelProviders
 import android.os.Bundle
-import android.util.Log
-import androidx.fragment.app.Fragment
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
+import androidx.lifecycle.ViewModelProviders
 import androidx.recyclerview.widget.RecyclerView
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.platform.VectorBaseFragment
 
 
-class EmojiChooserFragment : Fragment() {
+class EmojiChooserFragment : VectorBaseFragment() {
 
     companion object {
         fun newInstance() = EmojiChooserFragment()
     }
 
-    private lateinit var viewModel: EmojiChooserViewModel
+    override fun getLayoutResId() = R.layout.emoji_chooser_fragment
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
-                              savedInstanceState: Bundle?): View? {
-        return inflater.inflate(R.layout.emoji_chooser_fragment, container, false)
-    }
+    private lateinit var viewModel: EmojiChooserViewModel
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         viewModel = activity?.run {
-            ViewModelProviders.of(this).get(EmojiChooserViewModel::class.java)
+            ViewModelProviders.of(this, viewModelFactory).get(EmojiChooserViewModel::class.java)
         } ?: throw Exception("Invalid Activity")
         viewModel.initWithContect(context!!)
         (view as? RecyclerView)?.let {
@@ -53,9 +46,4 @@ class EmojiChooserFragment : Fragment() {
 //        val ds = EmojiDataSource(this.context!!)
     }
 
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-
-
-    }
 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserViewModel.kt
index 7b11adcd..47f2dbf1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiChooserViewModel.kt
@@ -19,8 +19,9 @@ import android.content.Context
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import im.vector.riotredesign.core.utils.LiveEvent
+import javax.inject.Inject
 
-class EmojiChooserViewModel : ViewModel() {
+class EmojiChooserViewModel @Inject constructor() : ViewModel() {
 
     var adapter: EmojiRecyclerAdapter? = null
     val emojiSourceLiveData: MutableLiveData = MutableLiveData()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt
index 8e362b61..5944c81d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt
@@ -30,10 +30,11 @@ import androidx.lifecycle.ViewModelProviders
 import com.google.android.material.tabs.TabLayout
 import im.vector.riotredesign.EmojiCompatFontProvider
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.*
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 /**
  *
@@ -55,7 +56,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvide
 
     override fun getTitleRes(): Int = R.string.title_activity_emoji_reaction_picker
 
-    val emojiCompatFontProvider by inject()
+    @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
 
     private var tabLayoutSelectionListener = object : TabLayout.BaseOnTabSelectedListener {
         override fun onTabReselected(p0: TabLayout.Tab) {
@@ -70,9 +71,12 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvide
 
     }
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun initUiAndData() {
         configureToolbar(emojiPickerToolbar)
-
         emojiCompatFontProvider.let {
             EmojiDrawView.configureTextPaint(this, it.typeface)
             it.addListener(this)
@@ -80,7 +84,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvide
 
         tabLayout = findViewById(R.id.tabs)
 
-        viewModel = ViewModelProviders.of(this).get(EmojiChooserViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(EmojiChooserViewModel::class.java)
 
         viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID)
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt
index 8e30809f..2f46fbce 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomItem.kt
@@ -31,6 +31,9 @@ import im.vector.riotredesign.features.home.AvatarRenderer
 @EpoxyModelClass(layout = R.layout.item_public_room)
 abstract class PublicRoomItem : VectorEpoxyModel() {
 
+    @EpoxyAttribute
+    lateinit var avatarRenderer: AvatarRenderer
+
     @EpoxyAttribute
     var avatarUrl: String? = null
 
@@ -61,7 +64,7 @@ abstract class PublicRoomItem : VectorEpoxyModel() {
     override fun bind(holder: Holder) {
         holder.rootView.setOnClickListener { globalListener?.invoke() }
 
-        AvatarRenderer.render(avatarUrl, roomId!!, roomName, holder.avatarView)
+        avatarRenderer.render(avatarUrl, roomId!!, roomName, holder.avatarView)
         holder.nameView.text = roomName
         holder.aliasView.setTextOrHide(roomAlias)
         holder.topicView.setTextOrHide(roomTopic)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt
index 01309e9e..b9bf30e7 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsController.kt
@@ -28,8 +28,11 @@ import im.vector.riotredesign.core.epoxy.loadingItem
 import im.vector.riotredesign.core.epoxy.noResultItem
 import im.vector.riotredesign.core.error.ErrorFormatter
 import im.vector.riotredesign.core.resources.StringProvider
+import im.vector.riotredesign.features.home.AvatarRenderer
+import javax.inject.Inject
 
-class PublicRoomsController(private val stringProvider: StringProvider,
+class PublicRoomsController @Inject constructor(private val stringProvider: StringProvider,
+                                                private val avatarRenderer: AvatarRenderer,
                             private val errorFormatter: ErrorFormatter) : TypedEpoxyController() {
 
     var callback: Callback? = null
@@ -78,6 +81,7 @@ class PublicRoomsController(private val stringProvider: StringProvider,
 
     private fun buildPublicRoom(publicRoom: PublicRoom, viewState: PublicRoomsViewState) {
         publicRoomItem {
+            avatarRenderer(avatarRenderer)
             id(publicRoom.roomId)
             roomId(publicRoom.roomId)
             avatarUrl(publicRoom.avatarUrl)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt
index ce6e24bb..05e9d493 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/PublicRoomsFragment.kt
@@ -28,17 +28,16 @@ import com.google.android.material.snackbar.Snackbar
 import com.jakewharton.rxbinding2.widget.RxTextView
 import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.error.ErrorFormatter
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.themes.ThemeUtils
 import io.reactivex.rxkotlin.subscribeBy
 import kotlinx.android.synthetic.main.fragment_public_rooms.*
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
 import timber.log.Timber
 import java.util.concurrent.TimeUnit
+import javax.inject.Inject
 
 
 /**
@@ -49,13 +48,18 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
 
     private val viewModel: RoomDirectoryViewModel by activityViewModel()
     private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
-    private val publicRoomsController: PublicRoomsController by inject()
-    private val errorFormatter: ErrorFormatter by inject()
+
+    @Inject lateinit var publicRoomsController: PublicRoomsController
+    @Inject lateinit var errorFormatter: ErrorFormatter
 
     override fun getLayoutResId() = R.layout.fragment_public_rooms
 
     override fun getMenuRes() = R.menu.menu_room_directory
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
@@ -98,10 +102,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
-
         navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
-
         setupRecyclerView()
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt
index 6f1e79e4..1b99b5a1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryActivity.kt
@@ -19,14 +19,14 @@ package im.vector.riotredesign.features.roomdirectory
 import android.os.Bundle
 import androidx.lifecycle.ViewModelProviders
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.addFragment
 import im.vector.riotredesign.core.extensions.addFragmentToBackstack
 import im.vector.riotredesign.core.extensions.observeEvent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomFragment
 import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
+import javax.inject.Inject
 
 class RoomDirectoryActivity : VectorBaseActivity() {
 
@@ -39,17 +39,18 @@ class RoomDirectoryActivity : VectorBaseActivity() {
     }
 
 
+    @Inject lateinit var roomDirectoryViewModelFactory: RoomDirectoryViewModel.Factory
     private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
 
     override fun getLayoutRes() = R.layout.activity_simple
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
-
-        navigationViewModel = ViewModelProviders.of(this).get(RoomDirectoryNavigationViewModel::class.java)
-
+        navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectoryNavigationViewModel::class.java)
         navigationViewModel.navigateTo.observeEvent(this) { navigation ->
             when (navigation) {
                 is Navigation.Back           -> onBackPressed()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryModule.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryModule.kt
deleted file mode 100644
index 2ac22864..00000000
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryModule.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.riotredesign.features.roomdirectory
-
-import im.vector.matrix.android.api.session.Session
-import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomController
-import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryListCreator
-import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerController
-import org.koin.dsl.module.module
-
-class RoomDirectoryModule {
-
-    companion object {
-        const val ROOM_DIRECTORY_SCOPE = "ROOM_DIRECTORY_SCOPE"
-    }
-
-    val definition = module(override = true) {
-
-        scope(ROOM_DIRECTORY_SCOPE) {
-            RoomDirectoryPickerController(get(), get(), get())
-        }
-
-        scope(ROOM_DIRECTORY_SCOPE) {
-            RoomDirectoryListCreator(get(), get().sessionParams.credentials)
-        }
-
-        scope(ROOM_DIRECTORY_SCOPE) {
-            PublicRoomsController(get(), get())
-        }
-
-        /* ==========================================================================================
-         * Create room
-         * ========================================================================================== */
-
-        scope(ROOM_DIRECTORY_SCOPE) {
-            CreateRoomController(get(), get())
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt
index 8183b787..e92cd883 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryNavigationViewModel.kt
@@ -17,5 +17,6 @@
 package im.vector.riotredesign.features.roomdirectory
 
 import im.vector.riotredesign.core.mvrx.NavigationViewModel
+import javax.inject.Inject
 
-class RoomDirectoryNavigationViewModel : NavigationViewModel()
\ No newline at end of file
+class RoomDirectoryNavigationViewModel @Inject constructor(): NavigationViewModel()
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryViewModel.kt
index fa980b20..f06a2bea 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/RoomDirectoryViewModel.kt
@@ -18,7 +18,15 @@ package im.vector.riotredesign.features.roomdirectory
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.ViewModelContext
+import com.airbnb.mvrx.appendAt
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.Membership
@@ -31,21 +39,24 @@ import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.rx.rx
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.core.utils.LiveEvent
-import org.koin.android.ext.android.get
 import timber.log.Timber
 
 private const val PUBLIC_ROOMS_LIMIT = 20
 
-class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
-                             private val session: Session) : VectorViewModel(initialState) {
+class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
+                                                         private val session: Session) : VectorViewModel(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
-            val currentSession = viewModelContext.activity.get()
-
-            return RoomDirectoryViewModel(state, currentSession)
+            val activity: RoomDirectoryActivity = (viewModelContext as ActivityViewModelContext).activity()
+            return activity.roomDirectoryViewModelFactory.create(state)
         }
     }
 
@@ -84,11 +95,11 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
                 .liveRoomSummaries()
                 .subscribe { list ->
                     val joinedRoomIds = list
-                            // Keep only joined room
-                            ?.filter { it.membership == Membership.JOIN }
-                            ?.map { it.roomId }
-                            ?.toList()
-                            ?: emptyList()
+                                                // Keep only joined room
+                                                ?.filter { it.membership == Membership.JOIN }
+                                                ?.map { it.roomId }
+                                                ?.toList()
+                                        ?: emptyList()
 
                     setState {
                         copy(
@@ -155,39 +166,39 @@ class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
 
     private fun load() {
         currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
-                PublicRoomsParams(
-                        limit = PUBLIC_ROOMS_LIMIT,
-                        filter = PublicRoomsFilter(searchTerm = currentFilter),
-                        includeAllNetworks = roomDirectoryData.includeAllNetworks,
-                        since = since,
-                        thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
-                ),
-                object : MatrixCallback {
-                    override fun onSuccess(data: PublicRoomsResponse) {
-                        currentTask = null
+                                             PublicRoomsParams(
+                                                     limit = PUBLIC_ROOMS_LIMIT,
+                                                     filter = PublicRoomsFilter(searchTerm = currentFilter),
+                                                     includeAllNetworks = roomDirectoryData.includeAllNetworks,
+                                                     since = since,
+                                                     thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
+                                             ),
+                                             object : MatrixCallback {
+                                                 override fun onSuccess(data: PublicRoomsResponse) {
+                                                     currentTask = null
 
-                        since = data.nextBatch
+                                                     since = data.nextBatch
 
-                        setState {
-                            copy(
-                                    asyncPublicRoomsRequest = Success(data.chunk!!),
-                                    // It's ok to append at the end of the list, so I use publicRooms.size()
-                                    publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
-                                    hasMore = since != null
-                            )
-                        }
-                    }
+                                                     setState {
+                                                         copy(
+                                                                 asyncPublicRoomsRequest = Success(data.chunk!!),
+                                                                 // It's ok to append at the end of the list, so I use publicRooms.size()
+                                                                 publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
+                                                                 hasMore = since != null
+                                                         )
+                                                     }
+                                                 }
 
-                    override fun onFailure(failure: Throwable) {
-                        currentTask = null
+                                                 override fun onFailure(failure: Throwable) {
+                                                     currentTask = null
 
-                        setState {
-                            copy(
-                                    asyncPublicRoomsRequest = Fail(failure)
-                            )
-                        }
-                    }
-                })
+                                                     setState {
+                                                         copy(
+                                                                 asyncPublicRoomsRequest = Fail(failure)
+                                                         )
+                                                     }
+                                                 }
+                                             })
     }
 
     fun joinRoom(publicRoom: PublicRoom) = withState { state ->
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomController.kt
index 6d925e08..abd78d96 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomController.kt
@@ -28,8 +28,9 @@ import im.vector.riotredesign.core.error.ErrorFormatter
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.form.formEditTextItem
 import im.vector.riotredesign.features.form.formSwitchItem
+import javax.inject.Inject
 
-class CreateRoomController(private val stringProvider: StringProvider,
+class CreateRoomController @Inject constructor(private val stringProvider: StringProvider,
                            private val errorFormatter: ErrorFormatter
 ) : TypedEpoxyController() {
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt
index 07c6c0e3..ee962961 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomFragment.kt
@@ -24,36 +24,34 @@ import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
-import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
 import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
 import kotlinx.android.synthetic.main.fragment_create_room.*
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
 import timber.log.Timber
+import javax.inject.Inject
 
 class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
 
     private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
     private val viewModel: CreateRoomViewModel by fragmentViewModel()
-    private val createRoomController: CreateRoomController by inject()
+    @Inject lateinit var createRoomController: CreateRoomController
+    @Inject lateinit var createRoomViewModelFactory: CreateRoomViewModel.Factory
 
     override fun getLayoutResId() = R.layout.fragment_create_room
 
     override fun getMenuRes() = R.menu.vector_room_creation
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
-
         vectorBaseActivity.setSupportActionBar(createRoomToolbar)
-
         navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
-
         setupRecyclerView()
-
         createRoomClose.setOnClickListener {
             navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
         }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt
index 3790381a..f928e20c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/createroom/CreateRoomViewModel.kt
@@ -17,24 +17,30 @@
 package im.vector.riotredesign.features.roomdirectory.createroom
 
 import com.airbnb.mvrx.*
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
 import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
 import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
-class CreateRoomViewModel(initialState: CreateRoomViewState,
-                          private val session: Session) : VectorViewModel(initialState) {
+class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
+                                                      private val session: Session
+) : VectorViewModel(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: CreateRoomViewState): CreateRoomViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? {
-            val currentSession = viewModelContext.activity.get()
-
-            return CreateRoomViewModel(state, currentSession)
+            val fragment: CreateRoomFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.createRoomViewModelFactory.create(state)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryListCreator.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryListCreator.kt
index 49fed53e..dadb4fa9 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryListCreator.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryListCreator.kt
@@ -16,14 +16,17 @@
 
 package im.vector.riotredesign.features.roomdirectory.picker
 
-import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
 import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.resources.StringArrayProvider
+import javax.inject.Inject
 
-class RoomDirectoryListCreator(private val stringArrayProvider: StringArrayProvider,
-                               private val credentials: Credentials) {
+class RoomDirectoryListCreator @Inject constructor(private val stringArrayProvider: StringArrayProvider,
+                                                   private val session: Session) {
+
+    private val credentials = session.sessionParams.credentials
 
     fun computeDirectories(thirdPartyProtocolData: Map): List {
         val result = ArrayList()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerController.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerController.kt
index e19c7a0b..b80944b9 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerController.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerController.kt
@@ -26,10 +26,11 @@ import im.vector.riotredesign.core.epoxy.errorWithRetryItem
 import im.vector.riotredesign.core.epoxy.loadingItem
 import im.vector.riotredesign.core.error.ErrorFormatter
 import im.vector.riotredesign.core.resources.StringProvider
+import javax.inject.Inject
 
-class RoomDirectoryPickerController(private val stringProvider: StringProvider,
-                                    private val errorFormatter: ErrorFormatter,
-                                    private val roomDirectoryListCreator: RoomDirectoryListCreator
+class RoomDirectoryPickerController @Inject constructor(private val stringProvider: StringProvider,
+                                                        private val errorFormatter: ErrorFormatter,
+                                                        private val roomDirectoryListCreator: RoomDirectoryListCreator
 ) : TypedEpoxyController() {
 
     var callback: Callback? = null
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt
index 4d1ca98b..bde76fd2 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt
@@ -26,16 +26,14 @@ import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
-import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
 import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
 import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
 import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
-import org.koin.android.ext.android.inject
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
 import timber.log.Timber
+import javax.inject.Inject
 
 // TODO Set title to R.string.select_room_directory
 // TODO Menu to add custom room directory (not done in RiotWeb so far...)
@@ -44,7 +42,9 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
     private val viewModel: RoomDirectoryViewModel by activityViewModel()
     private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
     private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()
-    private val roomDirectoryPickerController: RoomDirectoryPickerController by inject()
+
+    @Inject lateinit var roomDirectoryPickerViewModelFactory: RoomDirectoryPickerViewModel.Factory
+    @Inject lateinit var roomDirectoryPickerController: RoomDirectoryPickerController
 
     override fun getLayoutResId() = R.layout.fragment_room_directory_picker
 
@@ -71,12 +71,13 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
         return super.onOptionsItemSelected(item)
     }
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
-
         navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
-
         setupRecyclerView()
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
index 30e687e9..6b5d86fb 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt
@@ -17,25 +17,32 @@
 package im.vector.riotredesign.features.roomdirectory.picker
 
 import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
-class RoomDirectoryPickerViewModel(initialState: RoomDirectoryPickerViewState,
-                                   private val session: Session) : VectorViewModel(initialState) {
+class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState,
+                                                               private val session: Session) : VectorViewModel(initialState) {
+
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel? {
-            val currentSession = viewModelContext.activity.get()
-
-            return RoomDirectoryPickerViewModel(state, currentSession)
+            val fragment: RoomDirectoryPickerFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.roomDirectoryPickerViewModelFactory.create(state)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewActivity.kt
index 0728ad56..46044914 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewActivity.kt
@@ -18,7 +18,6 @@ package im.vector.riotredesign.features.roomdirectory.roompreview
 
 import android.content.Context
 import android.content.Intent
-import android.os.Bundle
 import android.os.Parcelable
 import androidx.appcompat.widget.Toolbar
 import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
@@ -26,10 +25,7 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.extensions.addFragment
 import im.vector.riotredesign.core.platform.ToolbarConfigurable
 import im.vector.riotredesign.core.platform.VectorBaseActivity
-import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
 import kotlinx.android.parcel.Parcelize
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
 
 @Parcelize
 data class RoomPreviewData(
@@ -65,12 +61,6 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
         configureToolbar(toolbar)
     }
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
-    }
-
     override fun initUiAndData() {
         if (isFirstCreation()) {
             val args = intent.getParcelableExtra(ARG)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt
index 1fdd5e09..2fbd8a7c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt
@@ -24,17 +24,15 @@ import com.airbnb.mvrx.args
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.error.ErrorFormatter
 import im.vector.riotredesign.core.extensions.setTextOrHide
 import im.vector.riotredesign.core.platform.ButtonStateView
 import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.features.home.AvatarRenderer
 import im.vector.riotredesign.features.roomdirectory.JoinState
-import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
 import kotlinx.android.synthetic.main.fragment_room_preview_no_preview.*
-import org.koin.android.ext.android.get
-import org.koin.android.scope.ext.android.bindScope
-import org.koin.android.scope.ext.android.getOrCreateScope
+import javax.inject.Inject
 
 /**
  * Note: this Fragment is also used for world readable room for the moment
@@ -47,14 +45,18 @@ class RoomPreviewNoPreviewFragment : VectorBaseFragment() {
         }
     }
 
-    private val errorFormatter = get()
+    @Inject lateinit var errorFormatter: ErrorFormatter
+    @Inject lateinit var roomPreviewViewModelFactory: RoomPreviewViewModel.Factory
+    @Inject lateinit var avatarRenderer: AvatarRenderer
     private val roomPreviewViewModel: RoomPreviewViewModel by fragmentViewModel()
-
     private val roomPreviewData: RoomPreviewData by args()
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
         setupToolbar(roomPreviewNoPreviewToolbar)
     }
 
@@ -64,11 +66,11 @@ class RoomPreviewNoPreviewFragment : VectorBaseFragment() {
         super.onViewCreated(view, savedInstanceState)
 
         // Toolbar
-        AvatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewToolbarAvatar)
+        avatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewToolbarAvatar)
         roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName
 
         // Screen
-        AvatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewAvatar)
+        avatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewAvatar)
         roomPreviewNoPreviewName.text = roomPreviewData.roomName
         roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic)
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
index 131f2719..61102f11 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/roomdirectory/roompreview/RoomPreviewViewModel.kt
@@ -16,27 +16,33 @@
 
 package im.vector.riotredesign.features.roomdirectory.roompreview
 
+import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.room.model.Membership
 import im.vector.matrix.rx.rx
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.features.roomdirectory.JoinState
-import org.koin.android.ext.android.get
 import timber.log.Timber
 
-class RoomPreviewViewModel(initialState: RoomPreviewViewState,
-                           private val session: Session) : VectorViewModel(initialState) {
+class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
+                                                       private val session: Session) : VectorViewModel(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: RoomPreviewViewState): RoomPreviewViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: RoomPreviewViewState): RoomPreviewViewModel? {
-            val currentSession = viewModelContext.activity.get()
-
-            return RoomPreviewViewModel(state, currentSession)
+            val fragment: RoomPreviewNoPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.roomPreviewViewModelFactory.create(state)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsActivity.kt
index 23fccedf..5207efd5 100755
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsActivity.kt
@@ -23,10 +23,11 @@ import androidx.preference.Preference
 import androidx.preference.PreferenceFragmentCompat
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import kotlinx.android.synthetic.main.activity_vector_settings.*
-import org.koin.android.ext.android.inject
 import timber.log.Timber
+import javax.inject.Inject
 
 /**
  * Displays the client settings.
@@ -43,13 +44,17 @@ class VectorSettingsActivity : VectorBaseActivity(),
 
     private var keyToHighlight: String? = null
 
-    private val session by inject()
+    @Inject lateinit var session: Session
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
 
     override fun initUiAndData() {
         configureToolbar(settingsToolbar)
 
         if (isFirstCreation()) {
-            val vectorSettingsPreferencesFragment = VectorSettingsRoot.newInstance()
+            val vectorSettingsPreferencesFragment = VectorSettingsRootFragment.newInstance()
             // display the fragment
             supportFragmentManager.beginTransaction()
                     .replace(R.id.vector_settings_page, vectorSettingsPreferencesFragment, FRAGMENT_TAG)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
index 05109c28..1392a3ba 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt
@@ -34,13 +34,9 @@ import im.vector.riotredesign.core.preference.BingRule
 import im.vector.riotredesign.core.preference.BingRulePreference
 import im.vector.riotredesign.features.notifications.NotificationUtils
 import im.vector.riotredesign.features.notifications.supportNotificationChannels
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
-class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFragment() {
-
-    // members
-    private val mSession by inject()
-    private var mLoadingView: View? = null
+class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseFragment() {
 
     // events listener
     /* TODO
@@ -53,10 +49,9 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
 
     override var titleRes: Int = R.string.settings_notification_advanced
 
-    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
-        // define the layout
-        addPreferencesFromResource(R.xml.vector_settings_notification_advanced_preferences)
+    override val preferenceXmlRes = R.xml.vector_settings_notification_advanced_preferences
 
+    override fun bindPref() {
         val callNotificationsSystemOptions = findPreference(PreferencesManager.SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY)
         if (supportNotificationChannels()) {
             callNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
@@ -177,33 +172,6 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
         }
     }
 
-    override fun onResume() {
-        super.onResume()
-        // find the view from parent activity
-        mLoadingView = activity!!.findViewById(R.id.vector_settings_spinner_views)
-
-        /* TODO
-        if (session.isAlive) {
-
-            session.dataHandler.addListener(mEventsListener)
-
-            // refresh anything else
-            refreshPreferences()
-            refreshDisplay()
-        }
-        */
-    }
-
-    override fun onPause() {
-        super.onPause()
-
-        /* TODO
-        if (session.isAlive) {
-            session.dataHandler.removeListener(mEventsListener)
-        }
-        */
-    }
-
     /**
      * Refresh the known information about the account
      */
@@ -246,30 +214,6 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
         }
     }
 
-
-    //==============================================================================================================
-    // Display methods
-    //==============================================================================================================
-
-    /**
-     * Display the loading view.
-     */
-    private fun displayLoadingView() {
-        if (null != mLoadingView) {
-            mLoadingView!!.visibility = View.VISIBLE
-        }
-    }
-
-    /**
-     * Hide the loading view.
-     */
-    private fun hideLoadingView() {
-        if (null != mLoadingView) {
-            mLoadingView!!.visibility = View.GONE
-        }
-    }
-
-
     /* ==========================================================================================
      * Companion
      * ========================================================================================== */
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsBaseFragment.kt
index a4ba7f63..f5abb7ce 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsBaseFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsBaseFragment.kt
@@ -16,42 +16,75 @@
 
 package im.vector.riotredesign.features.settings
 
+import android.content.Context
 import android.os.Bundle
 import android.text.TextUtils
 import android.view.View
 import androidx.annotation.CallSuper
+import androidx.preference.PreferenceFragmentCompat
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
-import im.vector.riotredesign.core.platform.VectorPreferenceFragment
+import im.vector.riotredesign.core.di.DaggerScreenComponent
+import im.vector.riotredesign.core.di.HasScreenInjector
+import im.vector.riotredesign.core.di.ScreenComponent
+import im.vector.riotredesign.core.platform.VectorBaseActivity
 import im.vector.riotredesign.core.utils.toast
-import org.koin.android.ext.android.inject
+import timber.log.Timber
 
-abstract class VectorSettingsBaseFragment : VectorPreferenceFragment() {
+abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScreenInjector {
+
+    val vectorActivity: VectorBaseActivity by lazy {
+        activity as VectorBaseActivity
+    }
 
     private var mLoadingView: View? = null
 
-
     // members
-    protected val mSession by inject()
+    protected lateinit var session: Session
+    private lateinit var screenComponent: ScreenComponent
 
     abstract val preferenceXmlRes: Int
 
     @CallSuper
     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
         addPreferencesFromResource(preferenceXmlRes)
-
         bindPref()
     }
 
+    override fun onAttach(context: Context) {
+        screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity)
+        super.onAttach(context)
+        session = screenComponent.session()
+        injectWith(injector())
+    }
+
+    protected open fun injectWith(injector: ScreenComponent) = Unit
+
+    override fun injector(): ScreenComponent {
+        return screenComponent
+    }
+
     override fun onResume() {
         super.onResume()
-
+        Timber.v("onResume Fragment ${this.javaClass.simpleName}")
+        vectorActivity.supportActionBar?.setTitle(titleRes)
         // find the view from parent activity
-        mLoadingView = activity?.findViewById(R.id.vector_settings_spinner_views)
+        mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views)
     }
 
     abstract fun bindPref()
 
+    abstract var titleRes: Int
+
+    /* ==========================================================================================
+     * Protected
+     * ========================================================================================== */
+
+    protected fun notImplemented() {
+        // Snackbar cannot be display on PreferenceFragment
+        // Snackbar.make(view!!, R.string.not_implemented, Snackbar.LENGTH_SHORT)
+        activity?.toast(R.string.not_implemented)
+    }
 
     /**
      * Display the loading view.
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlair.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlairFragment.kt
similarity index 98%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlair.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlairFragment.kt
index 5346825f..abab4160 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlair.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsFlairFragment.kt
@@ -20,7 +20,7 @@ import androidx.preference.PreferenceCategory
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.preference.ProgressBarPreference
 
-class VectorSettingsFlair : VectorSettingsBaseFragment() {
+class VectorSettingsFlairFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.settings_flair
     override val preferenceXmlRes = R.xml.vector_settings_flair
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneral.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneralFragment.kt
similarity index 98%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneral.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneralFragment.kt
index 6f38ef18..8b52cebc 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneral.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsGeneralFragment.kt
@@ -48,7 +48,7 @@ import im.vector.riotredesign.features.themes.ThemeUtils
 import java.lang.ref.WeakReference
 import java.util.*
 
-class VectorSettingsGeneral : VectorSettingsBaseFragment() {
+class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.settings_general_title
     override val preferenceXmlRes = R.xml.vector_settings_general
@@ -82,7 +82,7 @@ class VectorSettingsGeneral : VectorSettingsBaseFragment() {
     override fun bindPref() {
         // Avatar
         mUserAvatarPreference.let {
-            it.setSession(mSession)
+            it.setSession(session)
             it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
                 onUpdateAvatarClick()
                 false
@@ -91,7 +91,7 @@ class VectorSettingsGeneral : VectorSettingsBaseFragment() {
 
         // Display name
         mDisplayNamePreference.let {
-            it.summary = mSession.getUser(mSession.sessionParams.credentials.userId)?.displayName ?: ""
+            it.summary = session.getUser(session.sessionParams.credentials.userId)?.displayName ?: ""
             it.text = it.summary.toString()
             it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
                 onDisplayNameClick(newValue?.let { (it as String).trim() })
@@ -144,15 +144,15 @@ class VectorSettingsGeneral : VectorSettingsBaseFragment() {
 
         // user account
         findPreference(PreferencesManager.SETTINGS_LOGGED_IN_PREFERENCE_KEY)
-                .summary = mSession.sessionParams.credentials.userId
+                .summary = session.sessionParams.credentials.userId
 
         // home server
         findPreference(PreferencesManager.SETTINGS_HOME_SERVER_PREFERENCE_KEY)
-                .summary = mSession.sessionParams.homeServerConnectionConfig.homeServerUri.toString()
+                .summary = session.sessionParams.homeServerConnectionConfig.homeServerUri.toString()
 
         // identity server
         findPreference(PreferencesManager.SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY)
-                .summary = mSession.sessionParams.homeServerConnectionConfig.identityServerUri.toString()
+                .summary = session.sessionParams.homeServerConnectionConfig.identityServerUri.toString()
 
 
         refreshEmailsList()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAbout.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAboutFragment.kt
similarity index 96%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAbout.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAboutFragment.kt
index 6c920a1d..f67f89bb 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAbout.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsHelpAboutFragment.kt
@@ -27,7 +27,7 @@ import im.vector.riotredesign.core.utils.copyToClipboard
 import im.vector.riotredesign.core.utils.displayInWebView
 import im.vector.riotredesign.features.version.getVersion
 
-class VectorSettingsHelpAbout : VectorSettingsBaseFragment() {
+class VectorSettingsHelpAboutFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.preference_root_help_about
     override val preferenceXmlRes = R.xml.vector_settings_help_about
@@ -74,7 +74,7 @@ class VectorSettingsHelpAbout : VectorSettingsBaseFragment() {
 
         // olm version
         findPreference(PreferencesManager.SETTINGS_OLM_VERSION_PREFERENCE_KEY)
-                .summary = mSession.getCryptoVersion(requireContext(), false)
+                .summary = session.getCryptoVersion(requireContext(), false)
 
         // copyright
         findPreference(PreferencesManager.SETTINGS_COPYRIGHT_PREFERENCE_KEY)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsers.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsersFragment.kt
similarity index 98%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsers.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsersFragment.kt
index 0bef7541..5fce7b63 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsers.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsIgnoredUsersFragment.kt
@@ -27,7 +27,7 @@ import kotlin.getValue
 import kotlin.lazy
 import kotlin.let
 
-class VectorSettingsIgnoredUsers : VectorSettingsBaseFragment() {
+class VectorSettingsIgnoredUsersFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.settings_ignored_users
     override val preferenceXmlRes = R.xml.vector_settings_ignored_users
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabs.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabsFragment.kt
similarity index 95%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabs.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabsFragment.kt
index cdaa549e..4c9f1bee 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabs.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsLabsFragment.kt
@@ -23,7 +23,7 @@ import androidx.preference.PreferenceCategory
 import androidx.preference.SwitchPreference
 import im.vector.riotredesign.R
 
-class VectorSettingsLabs : VectorSettingsBaseFragment() {
+class VectorSettingsLabsFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.room_settings_labs_pref_title
     override val preferenceXmlRes = R.xml.vector_settings_labs
@@ -38,7 +38,7 @@ class VectorSettingsLabs : VectorSettingsBaseFragment() {
         val cryptoIsEnabledPref = findPreference(PreferencesManager.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY)
 
 
-        if (mSession.isCryptoEnabled()) {
+        if (session.isCryptoEnabled()) {
             mLabsCategory.removePreference(useCryptoPref)
 
             cryptoIsEnabledPref.isEnabled = false
@@ -48,7 +48,7 @@ class VectorSettingsLabs : VectorSettingsBaseFragment() {
             useCryptoPref.isChecked = false
 
             useCryptoPref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValueAsVoid ->
-                if (TextUtils.isEmpty(mSession.sessionParams.credentials.deviceId)) {
+                if (TextUtils.isEmpty(session.sessionParams.credentials.deviceId)) {
                     activity?.let { activity ->
                         AlertDialog.Builder(activity)
                                 .setMessage(R.string.room_settings_labs_end_to_end_warnings)
@@ -67,7 +67,7 @@ class VectorSettingsLabs : VectorSettingsBaseFragment() {
                 } else {
                     val newValue = newValueAsVoid as Boolean
 
-                    if (mSession.isCryptoEnabled() != newValue) {
+                    if (session.isCryptoEnabled() != newValue) {
                         notImplemented()
                         /* TODO
                         displayLoadingView()
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationFragment.kt
index 746a8fae..ead9f9e3 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationFragment.kt
@@ -19,13 +19,14 @@ package im.vector.riotredesign.features.settings
 import android.widget.Toast
 import androidx.preference.Preference
 import androidx.preference.SwitchPreference
-import im.vector.matrix.android.api.Matrix
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.pushrules.RuleIds
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.pushers.PushersManager
 import im.vector.riotredesign.push.fcm.FcmHelper
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 // Referenced in vector_settings_preferences_root.xml
 class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment() {
@@ -33,11 +34,12 @@ class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment(
     override var titleRes: Int = R.string.settings_notifications
     override val preferenceXmlRes = R.xml.vector_settings_notifications
 
-    val pushManager: PushersManager by inject()
+    @Inject lateinit var pushManager: PushersManager
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
 
     override fun bindPref() {
         findPreference(PreferencesManager.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY).let { pref ->
-            val pushRuleService = Matrix.getInstance().currentSession ?: return
+            val pushRuleService = session
             val mRuleMaster = pushRuleService.getPushRules()
                     .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
 
@@ -51,10 +53,13 @@ class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment(
         }
     }
 
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
 
     override fun onResume() {
         super.onResume()
-        Matrix.getInstance().currentSession?.refreshPushers()
+        activeSessionHolder.getSafeActiveSession()?.refreshPushers()
     }
 
     override fun onPreferenceTreeClick(preference: Preference?): Boolean {
@@ -87,12 +92,12 @@ class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment(
             FcmHelper.getFcmToken(requireContext())?.let {
                 pushManager.unregisterPusher(it, object : MatrixCallback {
                     override fun onSuccess(data: Unit) {
-                        Matrix.getInstance().currentSession?.refreshPushers()
+                        session.refreshPushers()
                         super.onSuccess(data)
                     }
 
                     override fun onFailure(failure: Throwable) {
-                        Matrix.getInstance().currentSession?.refreshPushers()
+                        session.refreshPushers()
                         Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
                     }
                 })
@@ -102,25 +107,25 @@ class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment(
 
 
     private fun updateEnabledForAccount(preference: Preference?) {
-        val pushRuleService = Matrix.getInstance().currentSession ?: return
+        val pushRuleService = session
         val switchPref = preference as SwitchPreference
         pushRuleService.getPushRules()
                 .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
                 ?.let {
                     //Trick, we must enable this room to disable notifications
                     pushRuleService.updatePushRuleEnableStatus("override", it, !switchPref.isChecked,
-                            object : MatrixCallback {
+                                                               object : MatrixCallback {
 
-                                override fun onSuccess(data: Unit) {
-                                    pushRuleService.fetchPushRules()
-                                }
+                                                                   override fun onSuccess(data: Unit) {
+                                                                       pushRuleService.fetchPushRules()
+                                                                   }
 
-                                override fun onFailure(failure: Throwable) {
-                                    //revert the check box
-                                    switchPref.isChecked = !switchPref.isChecked
-                                    Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
-                                }
-                            })
+                                                                   override fun onFailure(failure: Throwable) {
+                                                                       //revert the check box
+                                                                       switchPref.isChecked = !switchPref.isChecked
+                                                                       Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
+                                                                   }
+                                                               })
                 }
 
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
index 0fb39fff..0db2bfd6 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
@@ -30,6 +30,7 @@ import androidx.transition.TransitionManager
 import butterknife.BindView
 import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.core.extensions.withArgs
 import im.vector.riotredesign.core.platform.VectorBaseActivity
 import im.vector.riotredesign.core.platform.VectorBaseFragment
@@ -37,7 +38,7 @@ import im.vector.riotredesign.features.rageshake.BugReporter
 import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
 import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
 import im.vector.riotredesign.push.fcm.NotificationTroubleshootTestManagerFactory
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
 
@@ -54,16 +55,17 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
 
     private var testManager: NotificationTroubleshootTestManager? = null
     // members
-    private val mSession by inject()
+    @Inject lateinit var session: Session
+    @Inject lateinit var bugReporter: BugReporter
+    @Inject lateinit var testManagerFactory: NotificationTroubleshootTestManagerFactory
+
 
     override fun getLayoutResId() = R.layout.fragment_settings_notifications_troubleshoot
 
     private var interactionListener: VectorSettingsFragmentInteractionListener? = null
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        val appContext = activity!!.applicationContext
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -73,12 +75,12 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
         mRecyclerView.layoutManager = layoutManager
 
         val dividerItemDecoration = DividerItemDecoration(mRecyclerView.context,
-                layoutManager.orientation)
+                                                          layoutManager.orientation)
         mRecyclerView.addItemDecoration(dividerItemDecoration)
 
 
         mSummaryButton.setOnClickListener {
-            BugReporter.openBugReportScreen(activity!!)
+            bugReporter.openBugReportScreen(activity!!)
         }
 
         mRunButton.setOnClickListener {
@@ -90,10 +92,8 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
     private fun startUI() {
 
         mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
-                0, 0)
-
-        testManager = NotificationTroubleshootTestManagerFactory.createTestManager(this, mSession)
-
+                                             0, 0)
+        testManager = testManagerFactory.create(this)
         testManager?.statusListener = { troubleshootTestManager ->
             if (isAdded) {
                 TransitionManager.beginDelayedTransition(mBottomView)
@@ -103,7 +103,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
                         mSummaryButton.visibility = View.GONE
                         mRunButton.visibility = View.VISIBLE
                     }
-                    TroubleshootTest.TestStatus.RUNNING -> {
+                    TroubleshootTest.TestStatus.RUNNING     -> {
                         //Forces int type because it's breaking lint
                         val size: Int = troubleshootTestManager.testList.size
                         val currentTestIndex: Int = troubleshootTestManager.currentTestIndex
@@ -115,7 +115,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
                         mSummaryButton.visibility = View.GONE
                         mRunButton.visibility = View.GONE
                     }
-                    TroubleshootTest.TestStatus.FAILED -> {
+                    TroubleshootTest.TestStatus.FAILED      -> {
                         //check if there are quick fixes
                         var hasQuickFix = false
                         testManager?.testList?.let {
@@ -134,7 +134,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
                         mSummaryButton.visibility = View.VISIBLE
                         mRunButton.visibility = View.VISIBLE
                     }
-                    TroubleshootTest.TestStatus.SUCCESS -> {
+                    TroubleshootTest.TestStatus.SUCCESS     -> {
                         mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_success_status)
                         mSummaryButton.visibility = View.VISIBLE
                         mRunButton.visibility = View.VISIBLE
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferences.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt
similarity index 94%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferences.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt
index fcb4791b..a77f0f73 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferences.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt
@@ -26,11 +26,12 @@ import androidx.appcompat.app.AlertDialog
 import androidx.preference.Preference
 import androidx.preference.SwitchPreference
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ScreenComponent
 import im.vector.riotredesign.features.configuration.VectorConfiguration
 import im.vector.riotredesign.features.themes.ThemeUtils
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
-class VectorSettingsPreferences : VectorSettingsBaseFragment() {
+class VectorSettingsPreferencesFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.settings_preferences
     override val preferenceXmlRes = R.xml.vector_settings_preferences
@@ -42,7 +43,12 @@ class VectorSettingsPreferences : VectorSettingsBaseFragment() {
         findPreference(PreferencesManager.SETTINGS_INTERFACE_TEXT_SIZE_KEY)
     }
 
-    private val vectorConfiguration by inject()
+    @Inject lateinit var vectorConfiguration: VectorConfiguration
+
+    override fun injectWith(injector: ScreenComponent) {
+        injector.inject(this)
+    }
+
 
     override fun bindPref() {
         // user interface preferences
@@ -113,7 +119,7 @@ class VectorSettingsPreferences : VectorSettingsBaseFragment() {
                 context?.let { context: Context ->
                     AlertDialog.Builder(context)
                             .setSingleChoiceItems(R.array.media_saving_choice,
-                                    PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n ->
+                                                  PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n ->
                                 PreferencesManager.setSelectedMediasSavingPeriod(activity, n)
                                 d.cancel()
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRoot.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRootFragment.kt
similarity index 89%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRoot.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRootFragment.kt
index 69b64fc5..842f161f 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRoot.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsRootFragment.kt
@@ -19,7 +19,7 @@ package im.vector.riotredesign.features.settings
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.extensions.withArgs
 
-class VectorSettingsRoot : VectorSettingsBaseFragment() {
+class VectorSettingsRootFragment : VectorSettingsBaseFragment() {
 
     override var titleRes: Int = R.string.title_activity_settings
     override val preferenceXmlRes = R.xml.vector_settings_root
@@ -29,7 +29,7 @@ class VectorSettingsRoot : VectorSettingsBaseFragment() {
     }
 
     companion object {
-        fun newInstance() = VectorSettingsRoot()
+        fun newInstance() = VectorSettingsRootFragment()
                 .withArgs {
                     //putString(ARG_MATRIX_ID, matrixId)
                 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacy.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacyFragment.kt
similarity index 96%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacy.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacyFragment.kt
index 2e746db7..39895235 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacy.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsSecurityPrivacyFragment.kt
@@ -58,7 +58,7 @@ import java.text.DateFormat
 import java.text.SimpleDateFormat
 import java.util.*
 
-class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
+class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.settings_security_and_privacy
     override val preferenceXmlRes = R.xml.vector_settings_security_privacy
@@ -212,7 +212,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
                     override fun onPassphrase(passphrase: String) {
                         displayLoadingView()
 
-                        KeysExporter(mSession)
+                        KeysExporter(session)
                                 .export(requireContext(),
                                         passphrase,
                                         object : MatrixCallback {
@@ -314,7 +314,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
 
                 displayLoadingView()
 
-                KeysImporter(mSession)
+                KeysImporter(session)
                         .import(requireContext(),
                                 uri,
                                 mimetype,
@@ -367,8 +367,8 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
      * @param aMyDeviceInfo the device info
      */
     private fun refreshCryptographyPreference(aMyDeviceInfo: DeviceInfo?) {
-        val userId = mSession.sessionParams.credentials.userId
-        val deviceId = mSession.sessionParams.credentials.deviceId
+        val userId = session.sessionParams.credentials.userId
+        val deviceId = session.sessionParams.credentials.deviceId
 
         // device name
         if (null != aMyDeviceInfo) {
@@ -399,7 +399,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
 
         // crypto section: device key (fingerprint)
         if (!TextUtils.isEmpty(deviceId) && !TextUtils.isEmpty(userId)) {
-            val deviceInfo = mSession.getDeviceInfo(userId, deviceId)
+            val deviceInfo = session.getDeviceInfo(userId, deviceId)
 
             if (null != deviceInfo && !TextUtils.isEmpty(deviceInfo.fingerprint())) {
                 cryptoInfoTextPreference.summary = deviceInfo.getFingerprintHumanReadable()
@@ -415,10 +415,10 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
 
         sendToUnverifiedDevicesPref.isChecked = false
 
-        sendToUnverifiedDevicesPref.isChecked = mSession.getGlobalBlacklistUnverifiedDevices()
+        sendToUnverifiedDevicesPref.isChecked = session.getGlobalBlacklistUnverifiedDevices()
 
         sendToUnverifiedDevicesPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
-            mSession.setGlobalBlacklistUnverifiedDevices(sendToUnverifiedDevicesPref.isChecked)
+            session.setGlobalBlacklistUnverifiedDevices(sendToUnverifiedDevicesPref.isChecked)
 
             true
         }
@@ -442,7 +442,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
      * It can be any mobile device, as any browser.
      */
     private fun refreshDevicesList() {
-        if (mSession.isCryptoEnabled() && !TextUtils.isEmpty(mSession.sessionParams.credentials.deviceId)) {
+        if (session.isCryptoEnabled() && !TextUtils.isEmpty(session.sessionParams.credentials.deviceId)) {
             // display a spinner while loading the devices list
             if (0 == mDevicesListSettingsCategory.preferenceCount) {
                 activity?.let {
@@ -451,7 +451,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
                 }
             }
 
-            mSession.getDevicesList(object : MatrixCallback {
+            session.getDevicesList(object : MatrixCallback {
                 override fun onSuccess(data: DevicesListResponse) {
                     if (!isAdded) {
                         return
@@ -490,7 +490,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
         var preference: VectorPreference
         var typeFaceHighlight: Int
         var isNewList = true
-        val myDeviceId = mSession.sessionParams.credentials.deviceId
+        val myDeviceId = session.sessionParams.credentials.deviceId
 
         if (aDeviceInfoList.size == mDevicesNameList.size) {
             isNewList = !mDevicesNameList.containsAll(aDeviceInfoList)
@@ -596,7 +596,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
                     .setPositiveButton(R.string.rename) { _, _ -> displayDeviceRenameDialog(aDeviceInfo) }
 
             // disable the deletion for our own device
-            if (!TextUtils.equals(mSession.getMyDevice()?.deviceId, aDeviceInfo.deviceId)) {
+            if (!TextUtils.equals(session.getMyDevice()?.deviceId, aDeviceInfo.deviceId)) {
                 builder.setNegativeButton(R.string.delete) { _, _ -> deleteDevice(aDeviceInfo) }
             }
 
@@ -633,7 +633,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
 
                         val newName = input.text.toString()
 
-                        mSession.setDeviceName(aDeviceInfoToRename.deviceId!!, newName, object : MatrixCallback {
+                        session.setDeviceName(aDeviceInfoToRename.deviceId!!, newName, object : MatrixCallback {
                             override fun onSuccess(data: Unit) {
                                 hideLoadingView()
 
@@ -680,7 +680,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
         }
 
         displayLoadingView()
-        mSession.deleteDevice(deviceId, object : MatrixCallback {
+        session.deleteDevice(deviceId, object : MatrixCallback {
             override fun onSuccess(data: Unit) {
                 hideLoadingView()
                 // force settings update
@@ -752,7 +752,7 @@ class VectorSettingsSecurityPrivacy : VectorSettingsBaseFragment() {
     }
 
     private fun deleteDeviceWithPassword(deviceId: String, authSession: String?, accountPassword: String) {
-        mSession.deleteDeviceWithUserPassword(deviceId, authSession, accountPassword, object : MatrixCallback {
+        session.deleteDeviceWithUserPassword(deviceId, authSession, accountPassword, object : MatrixCallback {
             override fun onSuccess(data: Unit) {
                 hideLoadingView()
                 // force settings update
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideo.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideoFragment.kt
similarity index 98%
rename from vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideo.kt
rename to vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideoFragment.kt
index 8e8adbd0..04c9393d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideo.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsVoiceVideoFragment.kt
@@ -28,7 +28,7 @@ import im.vector.riotredesign.core.utils.getCallRingtoneUri
 import im.vector.riotredesign.core.utils.setCallRingtoneUri
 import im.vector.riotredesign.core.utils.setUseRiotDefaultRingtone
 
-class VectorSettingsVoiceVideo : VectorSettingsBaseFragment() {
+class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() {
 
     override var titleRes = R.string.preference_voice_and_video
     override val preferenceXmlRes = R.xml.vector_settings_voice_video
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysFragment.kt
index 1ea57264..b51c131c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysFragment.kt
@@ -29,14 +29,15 @@ import im.vector.riotredesign.core.platform.VectorBaseFragment
 import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.ui.list.genericFooterItem
 import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
+import javax.inject.Inject
 
 // Referenced in vector_settings_notifications.xml
 class PushGatewaysFragment : VectorBaseFragment() {
 
     override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
 
+    @Inject lateinit var pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory
     private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class)
-
     private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) }
 
     override fun onResume() {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysViewModel.kt
index edb032a0..70bbd17a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushGatewaysViewModel.kt
@@ -16,38 +16,50 @@
 
 package im.vector.riotredesign.features.settings.push
 
-import androidx.lifecycle.Observer
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.pushers.Pusher
+import im.vector.matrix.rx.RxSession
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
 
 data class PushGatewayViewState(
         val pushGateways: Async> = Uninitialized
 ) : MvRxState
 
-class PushGatewaysViewModel(initialState: PushGatewayViewState) : VectorViewModel(initialState) {
+class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
+                                                        private val session: Session) : VectorViewModel(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: PushGatewayViewState): PushGatewaysViewModel
+    }
 
     companion object : MvRxViewModelFactory {
 
         override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? {
-            val session = viewModelContext.activity.get()
-            val fragment = (viewModelContext as FragmentViewModelContext).fragment
-
-            val livePushers = session.livePushers()
-
-            val viewModel = PushGatewaysViewModel(state)
-
-            livePushers.observe(fragment, Observer {
-                viewModel.setState {
-                    PushGatewayViewState(Success(it))
-                }
-            })
-            return viewModel
+            val fragment: PushGatewaysFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return fragment.pushGatewaysViewModelFactory.create(state)
         }
+    }
 
+    init {
+        observePushers()
+    }
+
+    private fun observePushers() {
+        RxSession(session)
+                .livePushers()
+                .execute {
+                    copy(pushGateways = it)
+                }
     }
 
 }
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushRulesViewModel.kt
index 03a2e600..24294353 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushRulesViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/push/PushRulesViewModel.kt
@@ -19,23 +19,20 @@ import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
 import im.vector.matrix.android.api.pushrules.rest.PushRule
-import im.vector.matrix.android.api.session.Session
+import im.vector.riotredesign.core.di.HasScreenInjector
 import im.vector.riotredesign.core.platform.VectorViewModel
-import org.koin.android.ext.android.get
 
 data class PushRulesViewState(
         val rules: List = emptyList()
 ) : MvRxState
 
-
 class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel(initialState) {
 
     companion object : MvRxViewModelFactory {
 
         override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? {
-            val session = viewModelContext.activity.get()
+            val session = (viewModelContext.activity as HasScreenInjector).injector().session()
             val rules = session.getPushRules()
-
             return PushRulesViewState(rules)
         }
 
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestAccountSettings.kt
index 4580f097..efb91607 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestAccountSettings.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestAccountSettings.kt
@@ -15,45 +15,48 @@
  */
 package im.vector.riotredesign.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.pushrules.RuleIds
-import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.resources.StringProvider
+import javax.inject.Inject
 
 /**
  * Check that the main pushRule (RULE_ID_DISABLE_ALL) is correctly setup
  */
-class TestAccountSettings(val fragment: Fragment, val session: Session)
+class TestAccountSettings @Inject constructor(private val stringProvider: StringProvider,
+                                              private val activeSessionHolder: ActiveSessionHolder)
     : TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
 
     override fun perform() {
+        val session = activeSessionHolder.getSafeActiveSession() ?: return
         val defaultRule = session.getPushRules()
                 .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
 
         if (defaultRule != null) {
             if (!defaultRule.enabled) {
-                description = fragment.getString(R.string.settings_troubleshoot_test_account_settings_success)
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_account_settings_success)
                 quickFix = null
                 status = TestStatus.SUCCESS
             } else {
-                description = fragment.getString(R.string.settings_troubleshoot_test_account_settings_failed)
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_account_settings_failed)
                 quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_account_settings_quickfix) {
                     override fun doFix() {
                         if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
 
                         // TODO Use constant for kind
                         session.updatePushRuleEnableStatus("override", defaultRule, !defaultRule.enabled,
-                                object : MatrixCallback {
+                                                           object : MatrixCallback {
 
-                                    override fun onSuccess(data: Unit) {
-                                        manager?.retry()
-                                    }
+                                                               override fun onSuccess(data: Unit) {
+                                                                   manager?.retry()
+                                                               }
 
-                                    override fun onFailure(failure: Throwable) {
-                                        manager?.retry()
-                                    }
-                                })
+                                                               override fun onFailure(failure: Throwable) {
+                                                                   manager?.retry()
+                                                               }
+                                                           })
                     }
                 }
                 status = TestStatus.FAILED
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestBingRulesSettings.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestBingRulesSettings.kt
index 447f45ab..13187f6a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestBingRulesSettings.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestBingRulesSettings.kt
@@ -15,31 +15,34 @@
  */
 package im.vector.riotredesign.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
 import im.vector.matrix.android.api.pushrules.Action
 import im.vector.matrix.android.api.pushrules.RuleIds
-import im.vector.matrix.android.api.session.Session
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.di.ActiveSessionHolder
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.notifications.NotificationAction
+import javax.inject.Inject
 
-class TestBingRulesSettings(val fragment: Fragment, val session: Session) : TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) {
+class TestBingRulesSettings @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
+                                                private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) {
 
     private val testedRules =
             listOf(RuleIds.RULE_ID_CONTAIN_DISPLAY_NAME,
-                    RuleIds.RULE_ID_CONTAIN_USER_NAME,
-                    RuleIds.RULE_ID_ONE_TO_ONE_ROOM,
-                    RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS)
+                   RuleIds.RULE_ID_CONTAIN_USER_NAME,
+                   RuleIds.RULE_ID_ONE_TO_ONE_ROOM,
+                   RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS)
 
 
     val ruleSettingsName = arrayOf(R.string.settings_containing_my_display_name,
-            R.string.settings_containing_my_user_name,
-            R.string.settings_messages_in_one_to_one,
-            R.string.settings_messages_in_group_chat)
+                                   R.string.settings_containing_my_user_name,
+                                   R.string.settings_messages_in_one_to_one,
+                                   R.string.settings_messages_in_group_chat)
 
     override fun perform() {
+        val session = activeSessionHolder.getSafeActiveSession() ?: return
         val pushRules = session.getPushRules()
         if (pushRules == null) {
-            description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_failed_to_load_rules)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_failed_to_load_rules)
             status = TestStatus.FAILED
         } else {
             var oneOrMoreRuleIsOff = false
@@ -62,7 +65,7 @@ class TestBingRulesSettings(val fragment: Fragment, val session: Session) : Trou
             }
 
             if (oneOrMoreRuleIsOff) {
-                description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
                 //TODO
 //                quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_bing_settings_quickfix) {
 //                    override fun doFix() {
@@ -76,7 +79,7 @@ class TestBingRulesSettings(val fragment: Fragment, val session: Session) : Trou
                 status = TestStatus.FAILED
             } else {
                 if (oneOrMoreRuleAreSilent) {
-                    description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_success_with_warn)
+                    description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_success_with_warn)
                 } else {
                     description = null
                 }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestDeviceSettings.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestDeviceSettings.kt
index f92da5a2..cfafedc7 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestDeviceSettings.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestDeviceSettings.kt
@@ -15,29 +15,32 @@
  */
 package im.vector.riotredesign.features.settings.troubleshoot
 
-import androidx.fragment.app.Fragment
+import androidx.appcompat.app.AppCompatActivity
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.features.settings.PreferencesManager
+import javax.inject.Inject
 
 /**
  * Checks if notifications are enable in the system settings for this app.
  */
-class TestDeviceSettings(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
+class TestDeviceSettings @Inject constructor(private val context: AppCompatActivity,
+                                             private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
 
     override fun perform() {
 
-        if (PreferencesManager.areNotificationEnabledForDevice(fragment.requireContext())) {
-            description = fragment.getString(R.string.settings_troubleshoot_test_device_settings_success)
+        if (PreferencesManager.areNotificationEnabledForDevice(context)) {
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
             quickFix = null
             status = TestStatus.SUCCESS
         } else {
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
                 override fun doFix() {
-                    PreferencesManager.setNotificationEnabledForDevice(fragment.requireContext(), true)
+                    PreferencesManager.setNotificationEnabledForDevice(context, true)
                     manager?.retry()
                 }
             }
-            description = fragment.getString(R.string.settings_troubleshoot_test_device_settings_failed)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed)
             status = TestStatus.FAILED
         }
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestSystemSettings.kt
index df94fc1f..9d2ebabc 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestSystemSettings.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/settings/troubleshoot/TestSystemSettings.kt
@@ -15,27 +15,30 @@
  */
 package im.vector.riotredesign.features.settings.troubleshoot
 
+import androidx.appcompat.app.AppCompatActivity
 import androidx.core.app.NotificationManagerCompat
-import androidx.fragment.app.Fragment
 import im.vector.riotredesign.R
+import im.vector.riotredesign.core.resources.StringProvider
 import im.vector.riotredesign.core.utils.startNotificationSettingsIntent
+import javax.inject.Inject
 
 /**
  * Checks if notifications are enable in the system settings for this app.
  */
-class TestSystemSettings(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
+class TestSystemSettings @Inject constructor(private val context: AppCompatActivity,
+                                             private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
 
     override fun perform() {
-        if (NotificationManagerCompat.from(fragment.context!!).areNotificationsEnabled()) {
-            description = fragment.getString(R.string.settings_troubleshoot_test_system_settings_success)
+        if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
             quickFix = null
             status = TestStatus.SUCCESS
         } else {
-            description = fragment.getString(R.string.settings_troubleshoot_test_system_settings_failed)
+            description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
             quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
                 override fun doFix() {
                     if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
-                    startNotificationSettingsIntent(fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
+                    startNotificationSettingsIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX)
                 }
 
             }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutBottomSheetDialogFragment.kt
index 2d53f36a..ff66800a 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutBottomSheetDialogFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutBottomSheetDialogFragment.kt
@@ -30,6 +30,7 @@ import android.widget.TextView
 import androidx.appcompat.app.AlertDialog
 import androidx.core.view.isVisible
 import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProviders
 import androidx.transition.TransitionManager
 import butterknife.BindView
@@ -43,12 +44,14 @@ import im.vector.riotredesign.R
 import im.vector.riotredesign.core.utils.toast
 import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupManageActivity
 import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupActivity
-import org.koin.android.ext.android.inject
+import javax.inject.Inject
 
 
 class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
 
-    val session by inject()
+    @Inject lateinit var session: Session
+    @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
+
 
     @BindView(R.id.bottom_sheet_signout_warning_text)
     lateinit var sheetTitle: TextView
@@ -99,7 +102,7 @@ class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
 
-        viewModel = ViewModelProviders.of(this).get(SignOutViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, viewModelFactory).get(SignOutViewModel::class.java)
 
         viewModel.init(session)
 
@@ -131,7 +134,7 @@ class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
                                         startActivity(KeysBackupManageActivity.intent(context))
                                     }
                                 }
-                                KeysBackupState.Disabled -> {
+                                KeysBackupState.Disabled   -> {
                                     context?.let { context ->
                                         startActivityForResult(KeysBackupSetupActivity.intent(context, true), EXPORT_REQ)
                                     }
@@ -141,7 +144,7 @@ class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
                                     //keys are already backing up please wait
                                     context?.toast(R.string.keys_backup_is_not_finished_please_wait)
                                 }
-                                else -> {
+                                else                       -> {
                                     //nop
                                 }
                             }
@@ -190,7 +193,7 @@ class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
                     sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple)
                 }
                 KeysBackupState.BackingUp,
-                KeysBackupState.WillBackUp -> {
+                KeysBackupState.WillBackUp    -> {
                     backingUpStatusGroup.isVisible = true
                     sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backing_up)
                     dontWantClickableView.isVisible = true
@@ -202,14 +205,14 @@ class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
                     backupStatusTex.text = getString(R.string.sign_out_bottom_sheet_backing_up_keys)
 
                 }
-                KeysBackupState.NotTrusted -> {
+                KeysBackupState.NotTrusted    -> {
                     backingUpStatusGroup.isVisible = false
                     dontWantClickableView.isVisible = true
                     setupClickableView.isVisible = false
                     activateClickableView.isVisible = true
                     sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backup_not_active)
                 }
-                else -> {
+                else                          -> {
                     backingUpStatusGroup.isVisible = false
                     dontWantClickableView.isVisible = true
                     setupClickableView.isVisible = true
diff --git a/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutViewModel.kt
index a56681ce..bc975743 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/workers/signout/SignOutViewModel.kt
@@ -21,8 +21,9 @@ import androidx.lifecycle.ViewModel
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
 import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
+import javax.inject.Inject
 
-class SignOutViewModel : ViewModel(), KeysBackupStateListener {
+class SignOutViewModel @Inject constructor() : ViewModel(), KeysBackupStateListener {
     // Keys exported manually
     var keysExportedToFile = MutableLiveData()
 
@@ -47,9 +48,9 @@ class SignOutViewModel : ViewModel(), KeysBackupStateListener {
      */
     fun getCurrentBackupVersion(): String {
         return mxSession
-                ?.getKeysBackupService()
-                ?.currentBackupVersion
-                ?: ""
+                       ?.getKeysBackupService()
+                       ?.currentBackupVersion
+               ?: ""
     }
 
     /**
@@ -57,8 +58,8 @@ class SignOutViewModel : ViewModel(), KeysBackupStateListener {
      */
     fun getNumberOfKeysToBackup(): Int {
         return mxSession
-                ?.inboundGroupSessionsCount(false)
-                ?: 0
+                       ?.inboundGroupSessionsCount(false)
+               ?: 0
     }
 
     /**
@@ -93,11 +94,11 @@ class SignOutViewModel : ViewModel(), KeysBackupStateListener {
          */
         fun doYouNeedToBeDisplayed(session: Session?): Boolean {
             return session
-                    ?.inboundGroupSessionsCount(false)
-                    ?: 0 > 0
-                    && session
-                    ?.getKeysBackupService()
-                    ?.state != KeysBackupState.ReadyToBackUp
+                           ?.inboundGroupSessionsCount(false)
+                   ?: 0 > 0
+                   && session
+                           ?.getKeysBackupService()
+                           ?.state != KeysBackupState.ReadyToBackUp
         }
     }
 }
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_message_menu.xml b/vector/src/main/res/layout/fragment_message_menu.xml
new file mode 100644
index 00000000..4538ac93
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_message_menu.xml
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/xml/vector_settings_root.xml b/vector/src/main/res/xml/vector_settings_root.xml
index af457942..2baad761 100644
--- a/vector/src/main/res/xml/vector_settings_root.xml
+++ b/vector/src/main/res/xml/vector_settings_root.xml
@@ -6,14 +6,14 @@
         android:layout_width="match_parent"
         android:icon="@drawable/ic_settings_root_general"
         android:title="@string/settings_general_title"
-        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsGeneral" />
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsGeneralFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsFlairFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsVoiceVideoFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsIgnoredUsersFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsSecurityPrivacyFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsLabsFragment" />
 
     
+        app:fragment="im.vector.riotredesign.features.settings.VectorSettingsHelpAboutFragment" />
 
 
\ No newline at end of file