diff --git a/app/src/main/java/io/element/android/x/ElementXApplication.kt b/app/src/main/java/io/element/android/x/ElementXApplication.kt index 9f6af001ff..7526b9e788 100644 --- a/app/src/main/java/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/java/io/element/android/x/ElementXApplication.kt @@ -3,29 +3,33 @@ package io.element.android.x import android.app.Application import androidx.startup.AppInitializer import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.core.di.bindings +import io.element.android.x.di.AppBindings +import io.element.android.x.di.AppComponent import io.element.android.x.di.DaggerAppComponent +import io.element.android.x.di.SessionComponentsOwner import io.element.android.x.initializer.CoilInitializer import io.element.android.x.initializer.MatrixInitializer import io.element.android.x.initializer.MavericksInitializer -import io.element.android.x.matrix.MatrixInstance -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.plus class ElementXApplication : Application(), DaggerComponentOwner { - override lateinit var daggerComponent: Any + private lateinit var appComponent: AppComponent + private var sessionComponentsOwner: SessionComponentsOwner? = null - private val applicationScope = MainScope() + CoroutineName("ElementX Scope") + override val daggerComponent: Any + get() = listOfNotNull(sessionComponentsOwner?.activeSessionComponent, appComponent) override fun onCreate() { super.onCreate() - daggerComponent = DaggerAppComponent.factory().create(applicationContext) - MatrixInstance.init(this, applicationScope) + appComponent = DaggerAppComponent.factory().create(applicationContext) + sessionComponentsOwner = bindings().sessionComponentsOwner() AppInitializer.getInstance(this).apply { initializeComponent(MatrixInitializer::class.java) initializeComponent(CoilInitializer::class.java) initializeComponent(MavericksInitializer::class.java) } } + + } diff --git a/app/src/main/java/io/element/android/x/MainViewModel.kt b/app/src/main/java/io/element/android/x/MainViewModel.kt index 0b1ac97f28..36a7b176e0 100644 --- a/app/src/main/java/io/element/android/x/MainViewModel.kt +++ b/app/src/main/java/io/element/android/x/MainViewModel.kt @@ -8,8 +8,7 @@ import dagger.assisted.AssistedInject import io.element.android.x.anvilannotations.ContributesViewModel import io.element.android.x.core.di.daggerMavericksViewModelFactory import io.element.android.x.di.AppScope -import io.element.android.x.features.messages.MessagesViewModel -import io.element.android.x.features.messages.model.MessagesViewState +import io.element.android.x.di.SessionComponentsOwner import io.element.android.x.matrix.Matrix import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -19,10 +18,12 @@ data class MainState(val fake: Boolean = false) : MavericksState @ContributesViewModel(AppScope::class) class MainViewModel @AssistedInject constructor( private val matrix: Matrix, + private val sessionComponentsOwner: SessionComponentsOwner, @Assisted initialState: MainState ) : MavericksViewModel(initialState) { - companion object : MavericksViewModelFactory by daggerMavericksViewModelFactory() + companion object : + MavericksViewModelFactory by daggerMavericksViewModelFactory() suspend fun isLoggedIn(): Boolean { return matrix.isLoggedIn().first() @@ -31,19 +32,22 @@ class MainViewModel @AssistedInject constructor( fun startSyncIfLogged() { viewModelScope.launch { if (!isLoggedIn()) return@launch - matrix.activeClient().startSync() } } fun stopSyncIfLogged() { viewModelScope.launch { if (!isLoggedIn()) return@launch - matrix.activeClient().stopSync() } } suspend fun restoreSession() { - matrix.restoreSession() - matrix.activeClient().startSync() + val matrixClient = matrix.restoreSession() + if (matrixClient == null) { + throw IllegalStateException("Couldn't restore session...") + } else { + sessionComponentsOwner.create(matrixClient) + matrixClient.startSync() + } } } \ No newline at end of file diff --git a/app/src/main/java/io/element/android/x/Navigation.kt b/app/src/main/java/io/element/android/x/Navigation.kt index 5e67ebfff4..30b544256d 100644 --- a/app/src/main/java/io/element/android/x/Navigation.kt +++ b/app/src/main/java/io/element/android/x/Navigation.kt @@ -1,11 +1,14 @@ package io.element.android.x import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.popUpTo +import io.element.android.x.core.di.bindings import io.element.android.x.destinations.* +import io.element.android.x.di.AppBindings import io.element.android.x.features.login.LoginScreen import io.element.android.x.features.login.changeserver.ChangeServerScreen import io.element.android.x.features.messages.MessagesScreen @@ -29,11 +32,13 @@ fun OnBoardingScreenNavigation(navigator: DestinationsNavigator) { @Destination @Composable fun LoginScreenNavigation(navigator: DestinationsNavigator) { + val sessionComponentsOwner = LocalContext.current.bindings().sessionComponentsOwner() LoginScreen( onChangeServer = { navigator.navigate(ChangeServerScreenNavigationDestination) }, onLoginWithSuccess = { + sessionComponentsOwner.create(it) navigator.navigate(RoomListScreenNavigationDestination) { popUpTo(OnBoardingScreenNavigationDestination) { inclusive = true @@ -58,11 +63,13 @@ fun ChangeServerScreenNavigation(navigator: DestinationsNavigator) { @Destination @Composable fun RoomListScreenNavigation(navigator: DestinationsNavigator) { + val sessionComponentsOwner = LocalContext.current.bindings().sessionComponentsOwner() RoomListScreen( onRoomClicked = { roomId: RoomId -> navigator.navigate(MessagesScreenNavigationDestination(roomId = roomId.value)) }, onSuccessLogout = { + sessionComponentsOwner.releaseActiveSession() navigator.navigate(OnBoardingScreenNavigationDestination) { popUpTo(RoomListScreenNavigationDestination) { inclusive = true @@ -75,7 +82,7 @@ fun RoomListScreenNavigation(navigator: DestinationsNavigator) { @Destination @Composable fun MessagesScreenNavigation(roomId: String, navigator: DestinationsNavigator) { - MessagesScreen(roomId, navigator::navigateUp) + MessagesScreen(roomId = roomId, onBackPressed = navigator::navigateUp) } diff --git a/app/src/main/java/io/element/android/x/di/AppBindings.kt b/app/src/main/java/io/element/android/x/di/AppBindings.kt index b11192b8b1..e3141ccf02 100644 --- a/app/src/main/java/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/java/io/element/android/x/di/AppBindings.kt @@ -1,7 +1,6 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo -import io.element.android.x.di.AppScope import io.element.android.x.matrix.Matrix import kotlinx.coroutines.CoroutineScope @@ -9,4 +8,5 @@ import kotlinx.coroutines.CoroutineScope interface AppBindings { fun coroutineScope(): CoroutineScope fun matrix(): Matrix + fun sessionComponentsOwner(): SessionComponentsOwner } \ No newline at end of file diff --git a/app/src/main/java/io/element/android/x/di/SessionComponent.kt b/app/src/main/java/io/element/android/x/di/SessionComponent.kt new file mode 100644 index 0000000000..5acc18d384 --- /dev/null +++ b/app/src/main/java/io/element/android/x/di/SessionComponent.kt @@ -0,0 +1,27 @@ +package io.element.android.x.di + +import com.squareup.anvil.annotations.ContributesTo +import com.squareup.anvil.annotations.MergeSubcomponent +import dagger.BindsInstance +import dagger.Subcomponent +import io.element.android.x.core.di.DaggerMavericksBindings +import io.element.android.x.matrix.MatrixClient + +@SingleIn(SessionScope::class) +@MergeSubcomponent(SessionScope::class) +interface SessionComponent: DaggerMavericksBindings { + + fun matrixClient(): MatrixClient + + @Subcomponent.Builder + interface Builder { + @BindsInstance + fun client(matrixClient: MatrixClient): Builder + fun build(): SessionComponent + } + + @ContributesTo(AppScope::class) + interface ParentBindings { + fun sessionComponentBuilder(): Builder + } +} diff --git a/app/src/main/java/io/element/android/x/di/SessionComponentsOwner.kt b/app/src/main/java/io/element/android/x/di/SessionComponentsOwner.kt new file mode 100644 index 0000000000..ef95205250 --- /dev/null +++ b/app/src/main/java/io/element/android/x/di/SessionComponentsOwner.kt @@ -0,0 +1,45 @@ +package io.element.android.x.di + +import android.content.Context +import io.element.android.x.core.di.bindings +import io.element.android.x.matrix.MatrixClient +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject + +@SingleIn(AppScope::class) +class SessionComponentsOwner @Inject constructor(@ApplicationContext private val context: Context) { + + private val sessionComponents = ConcurrentHashMap() + var activeSessionComponent: SessionComponent? = null + private set + + fun setActive(sessionId: String) { + val sessionComponent = sessionComponents[sessionId] + if (activeSessionComponent != sessionComponent) { + activeSessionComponent = sessionComponent + } + } + + fun create(matrixClient: MatrixClient) { + val sessionId = matrixClient.sessionId + val sessionComponent = + context.bindings().sessionComponentBuilder() + .client(matrixClient).build() + sessionComponents[sessionId] = sessionComponent + setActive(sessionId) + } + + fun releaseActiveSession() { + activeSessionComponent?.also { + release(it.matrixClient().sessionId) + } + } + + fun release(sessionId: String) { + val sessionComponent = sessionComponents.remove(sessionId) + if (activeSessionComponent == sessionComponent) { + activeSessionComponent = null + } + } + +} diff --git a/app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt b/app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt index 491f498d36..1b99716606 100644 --- a/app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt +++ b/app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt @@ -25,7 +25,13 @@ private class ElementImageLoaderFactory( return ImageLoader .Builder(context) .components { - context.bindings().matrix().registerCoilComponents(this) + val appBindings = context.bindings() + val matrix = appBindings.matrix() + val matrixClientProvider = { + appBindings + .sessionComponentsOwner().activeSessionComponent?.matrixClient() + } + matrix.registerCoilComponents(this, matrixClientProvider) } .build() } diff --git a/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt b/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt index 9e5540ba01..6736b98e3d 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt @@ -30,13 +30,14 @@ import com.airbnb.mvrx.compose.collectAsState import com.airbnb.mvrx.compose.mavericksViewModel import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.features.login.error.loginError +import io.element.android.x.matrix.MatrixClient import timber.log.Timber @Composable fun LoginScreen( viewModel: LoginViewModel = mavericksViewModel(), onChangeServer: () -> Unit = { }, - onLoginWithSuccess: () -> Unit = { }, + onLoginWithSuccess: (MatrixClient) -> Unit = { }, ) { val state: LoginViewState by viewModel.collectAsState() val formState: LoginFormState by viewModel.formState @@ -65,7 +66,7 @@ fun LoginContent( onLoginChanged: (String) -> Unit = {}, onPasswordChanged: (String) -> Unit = {}, onSubmitClicked: () -> Unit = {}, - onLoginWithSuccess: () -> Unit = {}, + onLoginWithSuccess: (MatrixClient) -> Unit = {}, ) { Surface(color = MaterialTheme.colorScheme.background) { Box( @@ -82,7 +83,7 @@ fun LoginContent( ) .padding(horizontal = 16.dp), ) { - val isError = state.isLoggedIn is Fail + val isError = state.loggedInClient is Fail // Title Text( text = "Welcome back", @@ -137,7 +138,7 @@ fun LoginContent( ), ) var passwordVisible by remember { mutableStateOf(false) } - if (state.isLoggedIn is Loading) { + if (state.loggedInClient is Loading) { // Ensure password is hidden when user submits the form passwordVisible = false } @@ -170,9 +171,9 @@ fun LoginContent( onDone = { onSubmitClicked() } ), ) - if (state.isLoggedIn is Fail) { + if (state.loggedInClient is Fail) { Text( - text = loginError(state.formState, state.isLoggedIn.error), + text = loginError(state.formState, state.loggedInClient.error), color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(start = 16.dp) @@ -189,11 +190,12 @@ fun LoginContent( ) { Text(text = "Continue") } - if (state.isLoggedIn is Success) { - onLoginWithSuccess() + when (val loggedInClient = state.loggedInClient) { + is Success -> onLoginWithSuccess(loggedInClient()) + else -> Unit } } - if (state.isLoggedIn is Loading) { + if (state.loggedInClient is Loading) { CircularProgressIndicator( modifier = Modifier.align(Alignment.Center) ) diff --git a/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt b/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt index 75795e0937..3c39a6a139 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt @@ -11,7 +11,6 @@ import io.element.android.x.anvilannotations.ContributesViewModel import io.element.android.x.core.di.daggerMavericksViewModelFactory import io.element.android.x.di.AppScope import io.element.android.x.matrix.Matrix -import io.element.android.x.matrix.MatrixInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -49,21 +48,22 @@ class LoginViewModel @AssistedInject constructor( val state = awaitState() // Ensure the server is provided to the Rust SDK matrix.setHomeserver(state.homeserver) - matrix.login(state.formState.login.trim(), state.formState.password.trim()) - matrix.activeClient().startSync() + matrix.login(state.formState.login.trim(), state.formState.password.trim()).also { + it.startSync() + } }.execute { - copy(isLoggedIn = it) + copy(loggedInClient = it) } } } fun onSetPassword(password: String) { formState.value = formState.value.copy(password = password) - setState { copy(isLoggedIn = Uninitialized) } + setState { copy(loggedInClient = Uninitialized) } } fun onSetName(name: String) { formState.value = formState.value.copy(login = name) - setState { copy(isLoggedIn = Uninitialized) } + setState { copy(loggedInClient = Uninitialized) } } } \ No newline at end of file diff --git a/features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt b/features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt index 62513cff08..f4d4820430 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt @@ -4,14 +4,15 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized +import io.element.android.x.matrix.MatrixClient data class LoginViewState( val homeserver: String = "", - val isLoggedIn: Async = Uninitialized, + val loggedInClient: Async = Uninitialized, val formState: LoginFormState = LoginFormState.Default, ) : MavericksState { val submitEnabled = - formState.login.isNotEmpty() && formState.password.isNotEmpty() && isLoggedIn !is Loading + formState.login.isNotEmpty() && formState.password.isNotEmpty() && loggedInClient !is Loading } data class LoginFormState( diff --git a/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt b/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt index 58679beb91..f625c2e75b 100644 --- a/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt +++ b/features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt @@ -1,14 +1,25 @@ package io.element.android.x.features.login.changeserver import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import io.element.android.x.matrix.MatrixInstance +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.x.anvilannotations.ContributesViewModel +import io.element.android.x.core.di.daggerMavericksViewModelFactory +import io.element.android.x.di.AppScope +import io.element.android.x.matrix.Matrix import kotlinx.coroutines.launch -class ChangeServerViewModel(initialState: ChangeServerViewState) : +@ContributesViewModel(AppScope::class) +class ChangeServerViewModel @AssistedInject constructor( + private val matrix: Matrix, + @Assisted initialState: ChangeServerViewState +) : MavericksViewModel(initialState) { - private val matrix = MatrixInstance.getInstance() + companion object : + MavericksViewModelFactory by daggerMavericksViewModelFactory() init { setState { @@ -32,7 +43,7 @@ class ChangeServerViewModel(initialState: ChangeServerViewState) : suspend { val state = awaitState() matrix.setHomeserver(state.homeserver) - }.execute { it -> + }.execute { copy(changeServerAction = it) } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt index ed48d8305d..0b8fb62529 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt @@ -10,6 +10,7 @@ import io.element.android.x.core.di.daggerMavericksViewModelFactory import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarSize import io.element.android.x.di.AppScope +import io.element.android.x.di.SessionScope import io.element.android.x.features.messages.model.MessagesItemAction import io.element.android.x.features.messages.model.MessagesItemActionsSheetState import io.element.android.x.features.messages.model.MessagesTimelineItemState @@ -17,6 +18,7 @@ import io.element.android.x.features.messages.model.MessagesViewState import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent import io.element.android.x.matrix.Matrix +import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.media.MediaResolver import io.element.android.x.matrix.timeline.MatrixTimeline import io.element.android.x.matrix.timeline.MatrixTimelineItem @@ -28,16 +30,15 @@ import kotlinx.coroutines.launch private const val PAGINATION_COUNT = 50 -@ContributesViewModel(AppScope::class) +@ContributesViewModel(SessionScope::class) class MessagesViewModel @AssistedInject constructor( - matrix: Matrix, + private val client: MatrixClient, @Assisted private val initialState: MessagesViewState ) : MavericksViewModel(initialState) { companion object : MavericksViewModelFactory by daggerMavericksViewModelFactory() - private val client = matrix.activeClient() private val room = client.getRoom(initialState.roomId)!! private val messageTimelineItemStateFactory = MessageTimelineItemStateFactory(client, room, Dispatchers.Default) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt index 933dfdadb2..b589b18f8e 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt @@ -7,20 +7,18 @@ import dagger.assisted.AssistedInject import io.element.android.x.anvilannotations.ContributesViewModel import io.element.android.x.core.data.StableCharSequence import io.element.android.x.core.di.daggerMavericksViewModelFactory -import io.element.android.x.di.AppScope -import io.element.android.x.matrix.Matrix +import io.element.android.x.di.SessionScope +import io.element.android.x.matrix.MatrixClient -@ContributesViewModel(AppScope::class) +@ContributesViewModel(SessionScope::class) class MessageComposerViewModel @AssistedInject constructor( - private val matrix: Matrix, + private val client: MatrixClient, @Assisted private val initialState: MessageComposerViewState ) : MavericksViewModel(initialState) { companion object : MavericksViewModelFactory by daggerMavericksViewModelFactory() - private val client = matrix.activeClient() - fun onComposerFullScreenChange() { setState { copy( diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt index 8c8f8817fa..3eaf231818 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt @@ -9,13 +9,13 @@ import io.element.android.x.core.di.daggerMavericksViewModelFactory import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarSize import io.element.android.x.di.AppScope +import io.element.android.x.di.SessionScope import io.element.android.x.features.roomlist.model.MatrixUser import io.element.android.x.features.roomlist.model.RoomListRoomSummary import io.element.android.x.features.roomlist.model.RoomListRoomSummaryPlaceholders import io.element.android.x.features.roomlist.model.RoomListViewState import io.element.android.x.matrix.Matrix import io.element.android.x.matrix.MatrixClient -import io.element.android.x.matrix.MatrixInstance import io.element.android.x.matrix.media.MediaResolver import io.element.android.x.matrix.room.RoomSummary import kotlinx.coroutines.Dispatchers @@ -28,9 +28,9 @@ import kotlinx.coroutines.launch private const val extendedRangeSize = 40 -@ContributesViewModel(AppScope::class) +@ContributesViewModel(SessionScope::class) class RoomListViewModel @AssistedInject constructor( - matrix: Matrix, + private val client: MatrixClient, @Assisted initialState: RoomListViewState ) : MavericksViewModel(initialState) { @@ -38,7 +38,6 @@ class RoomListViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by daggerMavericksViewModelFactory() private val lastMessageFormatter = LastMessageFormatter() - private val client = matrix.activeClient() init { handleInit() diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt index cd7db813b6..31e13261dc 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt @@ -3,8 +3,8 @@ package io.element.android.x.matrix import android.content.Context import coil.ComponentRegistry import io.element.android.x.core.coroutine.CoroutineDispatchers -import io.element.android.x.di.ApplicationContext import io.element.android.x.di.AppScope +import io.element.android.x.di.ApplicationContext import io.element.android.x.di.SingleIn import io.element.android.x.matrix.media.MediaFetcher import io.element.android.x.matrix.media.MediaKeyer @@ -37,35 +37,18 @@ class Matrix @Inject constructor( ) private val baseDirectory = File(context.filesDir, "sessions") private val sessionStore = SessionStore(context) - private val matrixClient = MutableStateFlow>(Optional.empty()) private val authService = AuthenticationService(baseDirectory.absolutePath) - init { - sessionStore.isLoggedIn() - .distinctUntilChanged() - .onEach { isLoggedIn -> - if (!isLoggedIn) { - matrixClient.value = Optional.empty() - } - } - .launchIn(coroutineScope) - } - fun isLoggedIn(): Flow { return sessionStore.isLoggedIn() } - fun client(): Flow> { - return matrixClient - } - - fun activeClient(): MatrixClient { - return matrixClient.value.get() - } - - fun registerCoilComponents(builder: ComponentRegistry.Builder) { + fun registerCoilComponents( + builder: ComponentRegistry.Builder, + activeClientProvider: () -> MatrixClient? + ) { builder.add(MediaKeyer()) - builder.add(MediaFetcher.Factory(this)) + builder.add(MediaFetcher.Factory(activeClientProvider)) } suspend fun restoreSession() = withContext(coroutineDispatchers.io) { @@ -116,8 +99,6 @@ class Matrix @Inject constructor( coroutineScope = coroutineScope, dispatchers = coroutineDispatchers, baseDirectory = baseDirectory, - ).also { - matrixClient.value = Optional.of(it) - } + ) } } \ No newline at end of file diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt index 2ac426a03d..81a0f408f5 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt @@ -1,6 +1,7 @@ package io.element.android.x.matrix import io.element.android.x.core.coroutine.CoroutineDispatchers +import io.element.android.x.di.SingleIn import io.element.android.x.matrix.core.UserId import io.element.android.x.matrix.media.MediaResolver import io.element.android.x.matrix.media.RustMediaResolver @@ -25,6 +26,9 @@ class MatrixClient internal constructor( private val baseDirectory: File, ) : Closeable { + val sessionId: String + get() = "${client.session().userId}_${client.session().deviceId}" + private val clientDelegate = object : ClientDelegate { override fun didReceiveAuthError(isSoftLogout: Boolean) { Timber.v("didReceiveAuthError()") @@ -78,7 +82,7 @@ class MatrixClient internal constructor( client.setDelegate(clientDelegate) } - private fun onRestartSync(){ + private fun onRestartSync() { slidingSyncObserverToken = slidingSync.sync() } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt deleted file mode 100644 index 90fc39d858..0000000000 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.element.android.x.matrix - -import android.annotation.SuppressLint -import android.app.Application -import kotlinx.coroutines.CoroutineScope - - -object MatrixInstance { - @SuppressLint("StaticFieldLeak") - private lateinit var instance: Matrix - - fun init(context: Application, coroutineScope: CoroutineScope) { - instance = Matrix(coroutineScope, context) - } - - fun getInstance(): Matrix { - return instance - } -} \ No newline at end of file diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaFetcher.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaFetcher.kt index 7ed5f3c7e9..442af55bf7 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaFetcher.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaFetcher.kt @@ -4,31 +4,32 @@ import coil.ImageLoader import coil.fetch.FetchResult import coil.fetch.Fetcher import coil.request.Options -import io.element.android.x.matrix.Matrix +import io.element.android.x.matrix.MatrixClient import java.nio.ByteBuffer internal class MediaFetcher( - private val mediaResolver: MediaResolver, + private val mediaResolver: MediaResolver?, private val meta: MediaResolver.Meta, private val options: Options, private val imageLoader: ImageLoader ) : Fetcher { override suspend fun fetch(): FetchResult? { - val byteArray = mediaResolver.resolve(meta) ?: return null + val byteArray = mediaResolver?.resolve(meta) ?: return null val byteBuffer = ByteBuffer.wrap(byteArray) return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch() } - class Factory(private val matrix: Matrix) : Fetcher.Factory { + class Factory(private val activeClientProvider: () -> MatrixClient?) : + Fetcher.Factory { override fun create( data: MediaResolver.Meta, options: Options, imageLoader: ImageLoader ): Fetcher { - val activeClient = matrix.activeClient() + val activeClient = activeClientProvider() return MediaFetcher( - mediaResolver = activeClient.mediaResolver(), + mediaResolver = activeClient?.mediaResolver(), meta = data, options = options, imageLoader = imageLoader