Refactor where Dagger Components belongs (in node)
This commit is contained in:
@@ -18,13 +18,9 @@ package io.element.android.x
|
||||
|
||||
import android.app.Application
|
||||
import androidx.startup.AppInitializer
|
||||
import io.element.android.x.architecture.bindings
|
||||
import io.element.android.x.core.di.DaggerComponentOwner
|
||||
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.CrashInitializer
|
||||
import io.element.android.x.initializer.MatrixInitializer
|
||||
import io.element.android.x.initializer.MavericksInitializer
|
||||
@@ -33,20 +29,17 @@ import io.element.android.x.initializer.TimberInitializer
|
||||
class ElementXApplication : Application(), DaggerComponentOwner {
|
||||
|
||||
private lateinit var appComponent: AppComponent
|
||||
private var sessionComponentsOwner: SessionComponentsOwner? = null
|
||||
|
||||
override val daggerComponent: Any
|
||||
get() = listOfNotNull(sessionComponentsOwner?.activeSessionComponent, appComponent)
|
||||
get() = appComponent
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
appComponent = DaggerAppComponent.factory().create(applicationContext)
|
||||
sessionComponentsOwner = bindings<AppBindings>().sessionComponentsOwner()
|
||||
AppInitializer.getInstance(this).apply {
|
||||
initializeComponent(CrashInitializer::class.java)
|
||||
initializeComponent(TimberInitializer::class.java)
|
||||
initializeComponent(MatrixInitializer::class.java)
|
||||
initializeComponent(CoilInitializer::class.java)
|
||||
initializeComponent(MavericksInitializer::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ class MainActivity : NodeComponentActivity() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContent {
|
||||
ElementXTheme {
|
||||
// A surface container using the 'background' color from the theme
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
@@ -49,7 +48,6 @@ class MainActivity : NodeComponentActivity() {
|
||||
buildContext = it,
|
||||
appComponentOwner = applicationContext as DaggerComponentOwner,
|
||||
matrix = appBindings.matrix(),
|
||||
sessionComponentsOwner = appBindings.sessionComponentsOwner(),
|
||||
rootPresenter = appBindings.rootPresenter()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package io.element.android.x.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.ui.MatrixUi
|
||||
import io.element.android.x.root.RootPresenter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@@ -27,6 +26,4 @@ interface AppBindings {
|
||||
fun coroutineScope(): CoroutineScope
|
||||
fun rootPresenter(): RootPresenter
|
||||
fun matrix(): Matrix
|
||||
fun matrixUi(): MatrixUi
|
||||
fun sessionComponentsOwner(): SessionComponentsOwner
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import io.element.android.x.matrix.MatrixClient
|
||||
|
||||
@SingleIn(SessionScope::class)
|
||||
@MergeSubcomponent(SessionScope::class)
|
||||
interface SessionComponent: DaggerMavericksBindings, NodeFactoriesBindings {
|
||||
interface SessionComponent: NodeFactoriesBindings {
|
||||
|
||||
fun matrixClient(): MatrixClient
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.x.di
|
||||
|
||||
import android.content.Context
|
||||
import io.element.android.x.architecture.bindings
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.core.SessionId
|
||||
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<SessionId, SessionComponent>()
|
||||
var activeSessionComponent: SessionComponent? = null
|
||||
private set
|
||||
|
||||
fun setActive(sessionId: SessionId) {
|
||||
val sessionComponent = sessionComponents[sessionId]
|
||||
if (activeSessionComponent != sessionComponent) {
|
||||
activeSessionComponent = sessionComponent
|
||||
}
|
||||
}
|
||||
|
||||
fun create(matrixClient: MatrixClient) {
|
||||
val sessionId = matrixClient.sessionId
|
||||
val sessionComponent =
|
||||
context.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder()
|
||||
.client(matrixClient).build()
|
||||
sessionComponents[sessionId] = sessionComponent
|
||||
setActive(sessionId)
|
||||
}
|
||||
|
||||
fun releaseActiveSession() {
|
||||
activeSessionComponent?.also {
|
||||
release(it.matrixClient().sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun release(sessionId: SessionId) {
|
||||
val sessionComponent = sessionComponents.remove(sessionId)
|
||||
if (activeSessionComponent == sessionComponent) {
|
||||
activeSessionComponent = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.x.initializer
|
||||
|
||||
import android.content.Context
|
||||
import androidx.startup.Initializer
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import io.element.android.x.architecture.bindings
|
||||
import io.element.android.x.di.AppBindings
|
||||
|
||||
class CoilInitializer : Initializer<Unit> {
|
||||
|
||||
override fun create(context: Context) {
|
||||
Coil.setImageLoader(ElementImageLoaderFactory(context))
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
|
||||
}
|
||||
|
||||
private class ElementImageLoaderFactory(
|
||||
private val context: Context
|
||||
) : ImageLoaderFactory {
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.components {
|
||||
val appBindings = context.bindings<AppBindings>()
|
||||
val matrixUi = appBindings.matrixUi()
|
||||
val matrixClientProvider = {
|
||||
appBindings
|
||||
.sessionComponentsOwner().activeSessionComponent?.matrixClient()
|
||||
}
|
||||
matrixUi.registerCoilComponents(this, matrixClientProvider)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,30 @@ package io.element.android.x.node
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import coil.Coil
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.node.ParentNode
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import io.element.android.x.architecture.bindings
|
||||
import io.element.android.x.architecture.createNode
|
||||
import io.element.android.x.core.di.DaggerComponentOwner
|
||||
import io.element.android.x.di.SessionComponent
|
||||
import io.element.android.x.features.preferences.PreferencesFlowNode
|
||||
import io.element.android.x.features.roomlist.RoomListNode
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.core.RoomId
|
||||
import io.element.android.x.matrix.core.SessionId
|
||||
import io.element.android.x.matrix.ui.di.MatrixUIBindings
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
class LoggedInFlowNode(
|
||||
buildContext: BuildContext,
|
||||
val sessionId: SessionId,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val onOpenBugReport: () -> Unit,
|
||||
private val backstack: BackStack<NavTarget> = BackStack(
|
||||
initialElement = NavTarget.RoomList,
|
||||
@@ -27,7 +35,26 @@ class LoggedInFlowNode(
|
||||
) : ParentNode<LoggedInFlowNode.NavTarget>(
|
||||
navModel = backstack,
|
||||
buildContext = buildContext
|
||||
) {
|
||||
), DaggerComponentOwner {
|
||||
|
||||
override val daggerComponent: Any by lazy {
|
||||
parent!!.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(matrixClient).build()
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
||||
Coil.setImageLoader(imageLoaderFactory)
|
||||
matrixClient.startSync()
|
||||
},
|
||||
onDestroy = {
|
||||
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
|
||||
Coil.setImageLoader(imageLoaderFactory)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private val roomListCallback = object : RoomListNode.Callback {
|
||||
override fun onRoomClicked(roomId: RoomId) {
|
||||
|
||||
@@ -27,6 +27,8 @@ class RoomFlowNode(
|
||||
buildContext = buildContext
|
||||
) {
|
||||
|
||||
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onCreate = { Timber.v("OnCreate") },
|
||||
|
||||
@@ -9,10 +9,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.children.whenChildAttached
|
||||
import com.bumble.appyx.core.clienthelper.interactor.Interactor
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
@@ -25,13 +22,11 @@ import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import io.element.android.x.architecture.createNode
|
||||
import io.element.android.x.architecture.presenterConnector
|
||||
import io.element.android.x.core.compose.OnLifecycleEvent
|
||||
import io.element.android.x.core.di.DaggerComponentOwner
|
||||
import io.element.android.x.di.SessionComponentsOwner
|
||||
import io.element.android.x.features.rageshake.bugreport.BugReportNode
|
||||
import io.element.android.x.matrix.Matrix
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.core.SessionId
|
||||
import io.element.android.x.root.RootEvents
|
||||
import io.element.android.x.root.RootPresenter
|
||||
import io.element.android.x.root.RootView
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
@@ -39,22 +34,7 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import timber.log.Timber
|
||||
|
||||
class SessionComponentsOwnerInteractor(private val sessionComponentsOwner: SessionComponentsOwner) : Interactor<RootFlowNode>() {
|
||||
override fun onCreate(lifecycle: Lifecycle) {
|
||||
lifecycle.subscribe(onCreate = {
|
||||
whenChildAttached { commonLifecycle: Lifecycle, child: LoggedInFlowNode ->
|
||||
Timber.v("LoggedInFlowNode attached: ${child.sessionId} ")
|
||||
commonLifecycle.subscribe(
|
||||
onDestroy = {
|
||||
Timber.v("LoggedInFlowNode destroyed: ${child.sessionId}")
|
||||
sessionComponentsOwner.release(child.sessionId)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class RootFlowNode(
|
||||
buildContext: BuildContext,
|
||||
@@ -64,20 +44,25 @@ class RootFlowNode(
|
||||
),
|
||||
private val appComponentOwner: DaggerComponentOwner,
|
||||
private val matrix: Matrix,
|
||||
private val sessionComponentsOwner: SessionComponentsOwner,
|
||||
rootPresenter: RootPresenter
|
||||
) :
|
||||
ParentNode<RootFlowNode.NavTarget>(
|
||||
navModel = backstack,
|
||||
buildContext = buildContext,
|
||||
plugins = listOf(SessionComponentsOwnerInteractor(sessionComponentsOwner)),
|
||||
),
|
||||
|
||||
DaggerComponentOwner by appComponentOwner {
|
||||
|
||||
private val matrixClientsHolder = ConcurrentHashMap<SessionId, MatrixClient>()
|
||||
private val presenterConnector = presenterConnector(rootPresenter)
|
||||
|
||||
init {
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
whenChildAttached(LoggedInFlowNode::class) { _, child ->
|
||||
child.lifecycle.subscribe(
|
||||
onDestroy = { matrixClientsHolder.remove(child.sessionId) }
|
||||
)
|
||||
}
|
||||
matrix.isLoggedIn()
|
||||
.distinctUntilChanged()
|
||||
.onEach { isLoggedIn ->
|
||||
@@ -87,8 +72,7 @@ class RootFlowNode(
|
||||
if (matrixClient == null) {
|
||||
backstack.newRoot(NavTarget.NotLoggedInFlow)
|
||||
} else {
|
||||
matrixClient.startSync()
|
||||
sessionComponentsOwner.create(matrixClient)
|
||||
matrixClientsHolder[matrixClient.sessionId] = matrixClient
|
||||
backstack.newRoot(NavTarget.LoggedInFlow(matrixClient.sessionId))
|
||||
}
|
||||
} else {
|
||||
@@ -136,9 +120,12 @@ class RootFlowNode(
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
is NavTarget.LoggedInFlow -> {
|
||||
val matrixClient =
|
||||
matrixClientsHolder[navTarget.sessionId] ?: throw IllegalStateException("Makes sure to give a matrixClient with the given sessionId")
|
||||
LoggedInFlowNode(
|
||||
buildContext = buildContext,
|
||||
sessionId = navTarget.sessionId,
|
||||
matrixClient = matrixClient,
|
||||
onOpenBugReport = this::onOpenBugReport
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ class Matrix @Inject constructor(
|
||||
return sessionStore.isLoggedIn()
|
||||
}
|
||||
|
||||
suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io){
|
||||
sessionStore.getLatestSession()?.sessionId()
|
||||
}
|
||||
|
||||
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
||||
sessionStore.getLatestSession()
|
||||
?.let { session ->
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.x.matrix.ui
|
||||
|
||||
import coil.ComponentRegistry
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.ui.media.MediaFetcher
|
||||
import io.element.android.x.matrix.ui.media.MediaKeyer
|
||||
import javax.inject.Inject
|
||||
|
||||
class MatrixUi @Inject constructor() {
|
||||
|
||||
fun registerCoilComponents(
|
||||
builder: ComponentRegistry.Builder,
|
||||
activeClientProvider: () -> MatrixClient?
|
||||
) {
|
||||
builder.add(MediaKeyer())
|
||||
builder.add(MediaFetcher.Factory(activeClientProvider))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.element.android.x.matrix.ui.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.x.di.SessionScope
|
||||
import io.element.android.x.matrix.ui.media.LoggedInImageLoaderFactory
|
||||
import io.element.android.x.matrix.ui.media.NotLoggedInImageLoaderFactory
|
||||
|
||||
@ContributesTo(SessionScope::class)
|
||||
interface MatrixUIBindings {
|
||||
fun loggedInImageLoaderFactory(): LoggedInImageLoaderFactory
|
||||
fun notLoggedInImageLoaderFactory(): NotLoggedInImageLoaderFactory
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.element.android.x.matrix.ui.media
|
||||
|
||||
import android.content.Context
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import io.element.android.x.di.ApplicationContext
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoggedInImageLoaderFactory @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val matrixClient: MatrixClient,
|
||||
) : ImageLoaderFactory {
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.components {
|
||||
add(MediaKeyer())
|
||||
add(MediaFetcher.Factory(matrixClient))
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
class NotLoggedInImageLoaderFactory @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : ImageLoaderFactory {
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,16 +37,15 @@ internal class MediaFetcher(
|
||||
return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
|
||||
}
|
||||
|
||||
class Factory(private val activeClientProvider: () -> MatrixClient?) :
|
||||
class Factory(private val client: MatrixClient) :
|
||||
Fetcher.Factory<MediaResolver.Meta> {
|
||||
override fun create(
|
||||
data: MediaResolver.Meta,
|
||||
options: Options,
|
||||
imageLoader: ImageLoader
|
||||
): Fetcher {
|
||||
val activeClient = activeClientProvider()
|
||||
return MediaFetcher(
|
||||
mediaResolver = activeClient?.mediaResolver(),
|
||||
mediaResolver = client.mediaResolver(),
|
||||
meta = data,
|
||||
options = options,
|
||||
imageLoader = imageLoader
|
||||
|
||||
Reference in New Issue
Block a user