diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index f80a46d..5f4e82b 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -51,16 +51,17 @@ kover { classes( "com.naturecurly.deck.compose.DeckKt", "com.naturecurly.deck.compose.DeckComposeContainerUi", - "com.naturecurly.deck.compose.DeckInitializer", "com.naturecurly.deck.compose.DeckScope", "com.naturecurly.deck.compose.DeckScopeImpl", + "com.naturecurly.deck.compose.di.*", + "hilt_aggregated_deps.*", ) } } verify { rule { minBound(95, CoverageUnit.LINE) - minBound(80, CoverageUnit.BRANCH) + minBound(75, CoverageUnit.BRANCH) } } } @@ -69,7 +70,6 @@ kover { dependencies { api(projects.core) implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.compose.runtime) implementation(libs.androidx.compose.ui) implementation(libs.hilt) ksp(libs.hilt.compiler) diff --git a/compose/src/main/AndroidManifest.xml b/compose/src/main/AndroidManifest.xml deleted file mode 100644 index 69087bf..0000000 --- a/compose/src/main/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/compose/src/main/java/com/naturecurly/deck/compose/WharfImpl.kt b/compose/src/main/java/com/naturecurly/deck/compose/WharfImpl.kt index 3a43369..4cfe387 100644 --- a/compose/src/main/java/com/naturecurly/deck/compose/WharfImpl.kt +++ b/compose/src/main/java/com/naturecurly/deck/compose/WharfImpl.kt @@ -23,8 +23,6 @@ package com.naturecurly.deck.compose import android.app.Application -import android.content.Context -import com.naturecurly.deck.DeckContainer import com.naturecurly.deck.DeckProvider import com.naturecurly.deck.Wharf import com.naturecurly.deck.compose.log.DeckLog @@ -34,36 +32,31 @@ import kotlin.reflect.KClass class WharfImpl : Wharf() { private var application: Application? = null - // synchronizedMap is used to make the map thread-safe? private val entryPoints: MutableMap, Class> = mutableMapOf() - internal fun init(context: Context) { + internal fun init(app: Application) { entryPoints.clear() deckEntry.clear() runCatching { entryPoints.putAll( - EntryPoints.get(context, DeckDependenciesEntryPoint::class.java).dependencies(), + EntryPoints.get(app, DeckDependenciesEntryPoint::class.java).dependencies(), ) }.onFailure { DeckLog.e("WharfImpl initialization failed", it) return } - if (context is Application) { - application = context - } else { - DeckLog.w("Context is not an instance of Application") - } + application = app } @Suppress("UNCHECKED_CAST") - override fun registerNewProvider( + override fun registerNewProvider( providerClass: KClass>, providerIdentity: Int, ) { entryPoints[providerClass.java]?.let { dep -> application?.let { app -> val dependencies = EntryPoints.get(app, dep) - val containers = dependencies.containers() as Set> + val containers = dependencies.containers() deckEntry.addProvider(providerIdentity) containers.forEach { deckEntry.addContainer( diff --git a/core/src/main/java/com/naturecurly/deck/WharfLocal.kt b/compose/src/main/java/com/naturecurly/deck/compose/di/WharfModule.kt similarity index 62% rename from core/src/main/java/com/naturecurly/deck/WharfLocal.kt rename to compose/src/main/java/com/naturecurly/deck/compose/di/WharfModule.kt index d81bcf7..b71bc57 100644 --- a/core/src/main/java/com/naturecurly/deck/WharfLocal.kt +++ b/compose/src/main/java/com/naturecurly/deck/compose/di/WharfModule.kt @@ -20,13 +20,29 @@ * SOFTWARE. */ -package com.naturecurly.deck +package com.naturecurly.deck.compose.di -internal object WharfLocal { - private lateinit var wharf: Wharf - internal fun init(wharf: Wharf) { - this.wharf = wharf +import android.app.Application +import com.naturecurly.deck.Wharf +import com.naturecurly.deck.WharfAccess +import com.naturecurly.deck.WharfAccessImpl +import com.naturecurly.deck.compose.WharfImpl +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class WharfModule { + @Provides + @Singleton + fun provideWharf(app: Application): Wharf = WharfImpl().apply { + init(app) } - internal fun get() = wharf + @Provides + @Singleton + fun provideWharfAccess(wharf: Wharf): WharfAccess = WharfAccessImpl(wharf) } diff --git a/compose/src/test/kotlin/com/naturecurly/deck/compose/WharfImplTest.kt b/compose/src/test/kotlin/com/naturecurly/deck/compose/WharfImplTest.kt index 626a8f9..1aee9f9 100644 --- a/compose/src/test/kotlin/com/naturecurly/deck/compose/WharfImplTest.kt +++ b/compose/src/test/kotlin/com/naturecurly/deck/compose/WharfImplTest.kt @@ -23,12 +23,14 @@ package com.naturecurly.deck.compose import android.app.Application -import android.content.Context import android.util.Log import com.naturecurly.deck.ContainerEvent import com.naturecurly.deck.DeckContainer import com.naturecurly.deck.DeckContainerUi import com.naturecurly.deck.DeckProvider +import com.naturecurly.deck.Wharf +import com.naturecurly.deck.WharfAccess +import com.naturecurly.deck.WharfAccessImpl import com.naturecurly.deck.compose.log.DeckLog import dagger.hilt.EntryPoints import io.mockk.every @@ -92,30 +94,6 @@ class WharfImplTest { verify { DeckLog.e(any(), any()) } } - @Test - fun `verify WharfImpl with non Application context`() { - // Given - val mockedContext: Context = mockk(relaxed = true) - val entryPoints: DeckDependenciesEntryPoint = object : DeckDependenciesEntryPoint { - override fun dependencies(): Map, @JvmSuppressWildcards Class> { - return mapOf() - } - } - mockkStatic(EntryPoints::class) - every { - EntryPoints.get( - mockedContext, - DeckDependenciesEntryPoint::class.java, - ) - } returns entryPoints - // When - val wharf = WharfImpl() - wharf.init(mockedContext) - // Then - verify(exactly = 0) { DeckLog.e(any(), any()) } - verify { DeckLog.w(any()) } - } - @Test fun `verify WharfImpl registerNewProvider`() { // Given @@ -142,7 +120,7 @@ class WharfImplTest { // When val wharf = WharfImpl() wharf.init(mockedContext) - wharf.registerNewProvider(DeckProviderTest::class, 123) + wharf.registerNewProvider(DeckProviderTest::class, 123) // Then verify(exactly = 0) { DeckLog.e(any(), any()) } verify(exactly = 0) { DeckLog.w(any()) } @@ -153,10 +131,12 @@ class WharfImplTest { // Given // When val wharf = WharfImpl() - wharf.registerNewProvider(DeckProviderTest::class, 123) + wharf.registerNewProvider(DeckProviderTest::class, 123) } - class DeckProviderTest : DeckProvider { + class DeckProviderTest(wharf: Wharf) : + DeckProvider, + WharfAccess by WharfAccessImpl(wharf) { override fun onContainerEvent(containerEvent: ContainerEvent) { } } diff --git a/core/src/main/java/com/naturecurly/deck/DeckProvider.kt b/core/src/main/java/com/naturecurly/deck/DeckProvider.kt index 1d9d8ad..a3cbf69 100644 --- a/core/src/main/java/com/naturecurly/deck/DeckProvider.kt +++ b/core/src/main/java/com/naturecurly/deck/DeckProvider.kt @@ -26,14 +26,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch -interface DeckProvider { +interface DeckProvider : WharfAccess { private val containers: Set> - get() = WharfLocal.get().getDeckContainers(System.identityHashCode(this)) + get() = getDeckContainers(System.identityHashCode(this)) val containerUis: Map> - get() = WharfLocal.get().getDeckContainerUis(System.identityHashCode(this)) + get() = getDeckContainerUis(System.identityHashCode(this)) fun initDeckProvider(scope: CoroutineScope) { - WharfLocal.get().registerNewProvider(this::class, System.identityHashCode(this)) + registerNewProvider(this::class, System.identityHashCode(this)) containers.forEach { it.init(scope) } val containersFlow = merge(*(containers.map { it.containerEventFlow }.toTypedArray())) scope.launch { @@ -50,6 +50,6 @@ interface DeckProvider { fun onContainerEvent(containerEvent: ContainerEvent) fun onDeckClear() { - WharfLocal.get().clearProvider(System.identityHashCode(this)) + clearProvider(System.identityHashCode(this)) } } diff --git a/core/src/main/java/com/naturecurly/deck/Wharf.kt b/core/src/main/java/com/naturecurly/deck/Wharf.kt index 701c79f..c69376a 100644 --- a/core/src/main/java/com/naturecurly/deck/Wharf.kt +++ b/core/src/main/java/com/naturecurly/deck/Wharf.kt @@ -23,14 +23,11 @@ package com.naturecurly.deck import com.naturecurly.deck.model.DeckEntry +import kotlin.reflect.KClass -abstract class Wharf : ProviderRegister { +abstract class Wharf { protected val deckEntry = DeckEntry() - init { - WharfLocal.init(this) - } - internal fun getDeckContainers( providerIdentity: Int, ): Set> { @@ -57,4 +54,9 @@ abstract class Wharf : ProviderRegister { internal fun clearProvider(providerIdentity: Int) { deckEntry.clearProvider(providerIdentity) } + + abstract fun registerNewProvider( + providerClass: KClass>, + providerIdentity: Int, + ) } diff --git a/core/src/main/java/com/naturecurly/deck/ProviderRegister.kt b/core/src/main/java/com/naturecurly/deck/WharfAccess.kt similarity index 84% rename from core/src/main/java/com/naturecurly/deck/ProviderRegister.kt rename to core/src/main/java/com/naturecurly/deck/WharfAccess.kt index c71a948..50cfeb3 100644 --- a/core/src/main/java/com/naturecurly/deck/ProviderRegister.kt +++ b/core/src/main/java/com/naturecurly/deck/WharfAccess.kt @@ -24,9 +24,13 @@ package com.naturecurly.deck import kotlin.reflect.KClass -interface ProviderRegister { +interface WharfAccess { fun registerNewProvider( providerClass: KClass>, providerIdentity: Int, ) + + fun getDeckContainers(providerIdentity: Int): Set> + fun getDeckContainerUis(providerIdentity: Int): Map> + fun clearProvider(providerIdentity: Int) } diff --git a/compose/src/main/java/com/naturecurly/deck/compose/DeckInitializer.kt b/core/src/main/java/com/naturecurly/deck/WharfAccessImpl.kt similarity index 58% rename from compose/src/main/java/com/naturecurly/deck/compose/DeckInitializer.kt rename to core/src/main/java/com/naturecurly/deck/WharfAccessImpl.kt index 4607863..a615656 100644 --- a/compose/src/main/java/com/naturecurly/deck/compose/DeckInitializer.kt +++ b/core/src/main/java/com/naturecurly/deck/WharfAccessImpl.kt @@ -20,16 +20,27 @@ * SOFTWARE. */ -package com.naturecurly.deck.compose +package com.naturecurly.deck -import android.content.Context -import androidx.startup.Initializer +import kotlin.reflect.KClass -class DeckInitializer : Initializer { - override fun create(context: Context): WharfImpl = - WharfImpl().apply { - init(context) - } +class WharfAccessImpl(private val wharf: Wharf) : WharfAccess { + override fun getDeckContainers(providerIdentity: Int): Set> { + return wharf.getDeckContainers(providerIdentity) + } - override fun dependencies(): List?>?> = emptyList() + override fun getDeckContainerUis(providerIdentity: Int): Map> { + return wharf.getDeckContainerUis(providerIdentity) + } + + override fun clearProvider(providerIdentity: Int) { + return wharf.clearProvider(providerIdentity) + } + + override fun registerNewProvider( + providerClass: KClass>, + providerIdentity: Int, + ) { + wharf.registerNewProvider(providerClass, providerIdentity) + } } diff --git a/core/src/test/kotlin/com/naturecurly/deck/DeckProviderTest.kt b/core/src/test/kotlin/com/naturecurly/deck/DeckProviderTest.kt index 4f7b639..ad697e3 100644 --- a/core/src/test/kotlin/com/naturecurly/deck/DeckProviderTest.kt +++ b/core/src/test/kotlin/com/naturecurly/deck/DeckProviderTest.kt @@ -48,14 +48,13 @@ class DeckProviderTest { @Before fun setUp() { - every { wharf.registerNewProvider(any(), any()) } just runs + every { wharf.registerNewProvider(any(), any()) } just runs every { wharf.getDeckContainers(any()) } returns setOf(mockedContainer) every { wharf.getDeckContainerUis(any()) } returns mapOf("1" to mockedContainerUi) every { wharf.clearProvider(any()) } just runs every { mockedContainer.init(any()) } just runs every { mockedContainer.onDataReady(any(), any()) } just runs every { mockedContainer.containerEventFlow } returns mockedContainerEventFlow - WharfLocal.init(wharf) } @Test @@ -68,7 +67,7 @@ class DeckProviderTest { provider.initDeckProvider(testScope) mockedContainerEventFlow.emit(RefreshProvider) // Then - verify { wharf.registerNewProvider(provider::class, any()) } + verify { wharf.registerNewProvider(provider::class, any()) } verify { mockedContainer.init(testScope) } assertThat(events.first()).isEqualTo(RefreshProvider) } @@ -90,7 +89,7 @@ class DeckProviderTest { provider.initDeckProvider(testScope) provider.onDeckReady(testScope, "test") // Then - verify { wharf.registerNewProvider(provider::class, any()) } + verify { wharf.registerNewProvider(provider::class, any()) } verify { mockedContainer.init(testScope) } verify { mockedContainer.onDataReady(testScope, "test") } } @@ -105,7 +104,8 @@ class DeckProviderTest { } private fun getDeckProvider(eventList: MutableList = mutableListOf()): DeckProvider { - val provider = object : DeckProvider { + val mockedWharfAccess: WharfAccess = WharfAccessImpl(wharf) + val provider = object : DeckProvider, WharfAccess by mockedWharfAccess { override fun onContainerEvent(containerEvent: ContainerEvent) { eventList.add(containerEvent) } diff --git a/core/src/test/kotlin/com/naturecurly/deck/WharfTest.kt b/core/src/test/kotlin/com/naturecurly/deck/WharfTest.kt index 1977fdb..4df6aa6 100644 --- a/core/src/test/kotlin/com/naturecurly/deck/WharfTest.kt +++ b/core/src/test/kotlin/com/naturecurly/deck/WharfTest.kt @@ -41,7 +41,7 @@ class WharfTest { listOf(mockContainerUiToContainerPairOne, mockContainerUiToContainerPairTwo), ) // When - wharfImpl.registerNewProvider(providerClass, 123) + wharfImpl.registerNewProvider(providerClass, 123) val containers = wharfImpl.getDeckContainers(providerIdentity = 123) // Then assertThat(containers).isNotEmpty() @@ -59,7 +59,7 @@ class WharfTest { listOf(mockContainerUiToContainerPairOne, mockContainerUiToContainerPairTwo), ) // When - wharfImpl.registerNewProvider(providerClass, 123) + wharfImpl.registerNewProvider(providerClass, 123) val containers = wharfImpl.getDeckContainers(providerIdentity = 123) var containerUis = wharfImpl.getDeckContainerUis(123, filterDisabled = false) // Then @@ -94,7 +94,7 @@ class WharfTest { listOf(mockContainerUiToContainerPairOne, mockContainerUiToContainerPairTwo), ) // When - wharfImpl.registerNewProvider(providerClass, 123) + wharfImpl.registerNewProvider(providerClass, 123) val containers = wharfImpl.getDeckContainers(providerIdentity = 123) wharfImpl.clearProvider(123) // Then @@ -103,17 +103,8 @@ class WharfTest { assertThat(wharfImpl.getDeckContainerUis(123, filterDisabled = false)).isEmpty() } - @Test - fun `verify WharfLocal`() { - // Given - val wharfImpl = WharfTest(listOf()) - val actual = WharfLocal.get() - // Then - assertThat(actual).isEqualTo(wharfImpl) - } - class WharfTest(private val containerContainerUiPairs: List, DeckContainer<*, *>>>) : Wharf() { - override fun registerNewProvider( + override fun registerNewProvider( providerClass: KClass>, providerIdentity: Int, ) { diff --git a/sample/mainFeature/src/main/java/com/naturecurly/deck/sample/feature1/MainViewModel.kt b/sample/mainFeature/src/main/java/com/naturecurly/deck/sample/feature1/MainViewModel.kt index 1eb43a9..396d355 100644 --- a/sample/mainFeature/src/main/java/com/naturecurly/deck/sample/feature1/MainViewModel.kt +++ b/sample/mainFeature/src/main/java/com/naturecurly/deck/sample/feature1/MainViewModel.kt @@ -28,15 +28,17 @@ import androidx.lifecycle.viewModelScope import com.naturecurly.deck.ContainerEvent import com.naturecurly.deck.DeckProvider import com.naturecurly.deck.RefreshProvider +import com.naturecurly.deck.WharfAccess import com.naturecurly.deck.annotations.Provider import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel @Provider("MainFeature") -class MainViewModel @Inject constructor() : +class MainViewModel @Inject constructor(private val wharfAccess: WharfAccess) : ViewModel(), - DeckProvider { + DeckProvider, + WharfAccess by wharfAccess { init { initDeckProvider(viewModelScope) onDeckReady(viewModelScope, "Hello, World!")