diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 63d4afe..fd105f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,10 +16,16 @@ dependencies { implementation(project(":core:data")) 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.activity.compose) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.bundles.lifecycle.compose) + implementation(libs.androidx.navigation.compose) // Material Components — required for the Material3 XML Activity theme. implementation(libs.material) // Logging — the DebugTree is planted here; other modules log via Timber's static API. diff --git a/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt b/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt index 3005658..6842088 100644 --- a/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt +++ b/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt @@ -2,14 +2,16 @@ package com.example.architecture import android.app.Application 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.androidLogger import org.koin.core.context.startKoin import timber.log.Timber /** - * Single Koin entry point. Feature modules append their own `*DataModule` / `*PresentationModule` - * to the [modules] list — assembly happens only here, never inside feature modules. + * Single Koin entry point. Every feature's `*DataModule` / `*PresentationModule` is assembled here, + * never inside feature modules. */ class ArchitectureApp : Application() { override fun onCreate() { @@ -24,7 +26,11 @@ class ArchitectureApp : Application() { androidLogger() androidContext(this@ArchitectureApp) modules( + // core coreDataModule, + // characters feature + charactersDataModule, + charactersPresentationModule, ) } } diff --git a/app/src/main/kotlin/com/example/architecture/MainActivity.kt b/app/src/main/kotlin/com/example/architecture/MainActivity.kt index abbb074..bbeab40 100644 --- a/app/src/main/kotlin/com/example/architecture/MainActivity.kt +++ b/app/src/main/kotlin/com/example/architecture/MainActivity.kt @@ -4,31 +4,26 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -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 androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController 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() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - // Compose themes via AppTheme; the navigation host lands in a later milestone. AppTheme { - AppScaffold { innerPadding -> - Box( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), - contentAlignment = Alignment.Center, - ) { - Text(text = "Android Architecture Showcase") - } + val navController = rememberNavController() + NavHost( + navController = navController, + startDestination = CharacterListRoute, + ) { + charactersGraph( + onCharacterClick = { /* Detail navigation is wired in the next milestone. */ }, + ) } } } diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f15e066 --- /dev/null +++ b/core/data/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/feature/characters/presentation-compose/build.gradle.kts b/feature/characters/presentation-compose/build.gradle.kts index c9981f0..016b9c0 100644 --- a/feature/characters/presentation-compose/build.gradle.kts +++ b/feature/characters/presentation-compose/build.gradle.kts @@ -1,5 +1,7 @@ plugins { alias(libs.plugins.architecture.android.feature) + // For @Serializable type-safe navigation routes. + alias(libs.plugins.architecture.kotlinx.serialization) } android { diff --git a/feature/characters/presentation-compose/src/main/kotlin/com/example/architecture/feature/characters/presentation/compose/CharactersNavigation.kt b/feature/characters/presentation-compose/src/main/kotlin/com/example/architecture/feature/characters/presentation/compose/CharactersNavigation.kt new file mode 100644 index 0000000..3ae9849 --- /dev/null +++ b/feature/characters/presentation-compose/src/main/kotlin/com/example/architecture/feature/characters/presentation/compose/CharactersNavigation.kt @@ -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 { + CharacterListRoot(onCharacterClick = onCharacterClick) + } +} diff --git a/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/di/CharactersPresentationModule.kt b/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/di/CharactersPresentationModule.kt new file mode 100644 index 0000000..466b610 --- /dev/null +++ b/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/di/CharactersPresentationModule.kt @@ -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) +}