feat(characters): Koin module + nav graph + wire into :app (REDI-89)
- charactersPresentationModule: viewModelOf(::CharacterListViewModel) (in the UI-agnostic module).
- @Serializable CharacterListRoute + NavGraphBuilder.charactersGraph { composable<CharacterListRoute> }
in presentation-compose (serialization plugin added for type-safe routes).
- :app registers coreDataModule + charactersDataModule + charactersPresentationModule in startKoin,
and hosts a NavHost(startDestination = CharacterListRoute) calling charactersGraph.
- core:data manifest declares INTERNET (merges into :app) for live API calls.
This commit is contained in:
@@ -16,10 +16,16 @@ dependencies {
|
|||||||
implementation(project(":core:data"))
|
implementation(project(":core:data"))
|
||||||
implementation(project(":core:design-system"))
|
implementation(project(":core:design-system"))
|
||||||
|
|
||||||
|
// Characters feature: data + presentation (Koin modules) + Compose renderer (nav graph).
|
||||||
|
implementation(project(":feature:characters:data"))
|
||||||
|
implementation(project(":feature:characters:presentation"))
|
||||||
|
implementation(project(":feature:characters:presentation-compose"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.bundles.lifecycle.compose)
|
implementation(libs.bundles.lifecycle.compose)
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
// Material Components — required for the Material3 XML Activity theme.
|
// Material Components — required for the Material3 XML Activity theme.
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
// Logging — the DebugTree is planted here; other modules log via Timber's static API.
|
// Logging — the DebugTree is planted here; other modules log via Timber's static API.
|
||||||
|
|||||||
@@ -2,14 +2,16 @@ package com.example.architecture
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.example.architecture.core.data.di.coreDataModule
|
import com.example.architecture.core.data.di.coreDataModule
|
||||||
|
import com.example.architecture.feature.characters.data.di.charactersDataModule
|
||||||
|
import com.example.architecture.feature.characters.presentation.di.charactersPresentationModule
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.android.ext.koin.androidLogger
|
import org.koin.android.ext.koin.androidLogger
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single Koin entry point. Feature modules append their own `*DataModule` / `*PresentationModule`
|
* Single Koin entry point. Every feature's `*DataModule` / `*PresentationModule` is assembled here,
|
||||||
* to the [modules] list — assembly happens only here, never inside feature modules.
|
* never inside feature modules.
|
||||||
*/
|
*/
|
||||||
class ArchitectureApp : Application() {
|
class ArchitectureApp : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
@@ -24,7 +26,11 @@ class ArchitectureApp : Application() {
|
|||||||
androidLogger()
|
androidLogger()
|
||||||
androidContext(this@ArchitectureApp)
|
androidContext(this@ArchitectureApp)
|
||||||
modules(
|
modules(
|
||||||
|
// core
|
||||||
coreDataModule,
|
coreDataModule,
|
||||||
|
// characters feature
|
||||||
|
charactersDataModule,
|
||||||
|
charactersPresentationModule,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,31 +4,26 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import com.example.architecture.core.design.system.component.AppScaffold
|
|
||||||
import com.example.architecture.core.design.system.theme.AppTheme
|
import com.example.architecture.core.design.system.theme.AppTheme
|
||||||
|
import com.example.architecture.feature.characters.presentation.compose.CharacterListRoute
|
||||||
|
import com.example.architecture.feature.characters.presentation.compose.charactersGraph
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
// Compose themes via AppTheme; the navigation host lands in a later milestone.
|
|
||||||
AppTheme {
|
AppTheme {
|
||||||
AppScaffold { innerPadding ->
|
val navController = rememberNavController()
|
||||||
Box(
|
NavHost(
|
||||||
modifier = Modifier
|
navController = navController,
|
||||||
.fillMaxSize()
|
startDestination = CharacterListRoute,
|
||||||
.padding(innerPadding),
|
) {
|
||||||
contentAlignment = Alignment.Center,
|
charactersGraph(
|
||||||
) {
|
onCharacterClick = { /* Detail navigation is wired in the next milestone. */ },
|
||||||
Text(text = "Android Architecture Showcase")
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
core/data/src/main/AndroidManifest.xml
Normal file
7
core/data/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Networking lives in this module, so the permission is declared here and merges into :app. -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.architecture.android.feature)
|
alias(libs.plugins.architecture.android.feature)
|
||||||
|
// For @Serializable type-safe navigation routes.
|
||||||
|
alias(libs.plugins.architecture.kotlinx.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.example.architecture.feature.characters.presentation.compose
|
||||||
|
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/** Type-safe route for the characters list screen. */
|
||||||
|
@Serializable
|
||||||
|
data object CharacterListRoute
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The characters feature nav graph. `:app` only calls this and supplies cross-screen navigation as
|
||||||
|
* a callback. The detail destination is added here in a later milestone.
|
||||||
|
*/
|
||||||
|
fun NavGraphBuilder.charactersGraph(
|
||||||
|
onCharacterClick: (Int) -> Unit,
|
||||||
|
) {
|
||||||
|
composable<CharacterListRoute> {
|
||||||
|
CharacterListRoot(onCharacterClick = onCharacterClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.example.architecture.feature.characters.presentation.di
|
||||||
|
|
||||||
|
import com.example.architecture.feature.characters.presentation.CharacterListViewModel
|
||||||
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
/** Presentation DI for the characters feature. Lives with the (UI-agnostic) ViewModel it provides. */
|
||||||
|
val charactersPresentationModule = module {
|
||||||
|
viewModelOf(::CharacterListViewModel)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user