diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fd105f7..cb519c4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,8 @@ plugins { alias(libs.plugins.architecture.android.application) alias(libs.plugins.architecture.compose) alias(libs.plugins.architecture.koin) + // For the @Serializable CharactersViewsRoute (Compose↔View interop destination). + alias(libs.plugins.architecture.kotlinx.serialization) } android { @@ -16,16 +18,23 @@ dependencies { implementation(project(":core:data")) implementation(project(":core:design-system")) - // Characters feature: data + presentation (Koin modules) + Compose renderer (nav graph). + // Characters feature: data + presentation (Koin modules) + both renderers (Compose nav graph, + // Views Fragment hosted via interop). implementation(project(":feature:characters:data")) implementation(project(":feature:characters:presentation")) implementation(project(":feature:characters:presentation-compose")) + implementation(project(":feature:characters:presentation-views")) + + // About feature (MVVM contrast). + implementation(project(":feature:about:presentation")) 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) + // Compose↔View interop: hosts a Fragment inside the Compose NavHost. + implementation(libs.androidx.fragment.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 6842088..f5fc05c 100644 --- a/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt +++ b/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt @@ -2,6 +2,7 @@ package com.example.architecture import android.app.Application import com.example.architecture.core.data.di.coreDataModule +import com.example.architecture.feature.about.presentation.di.aboutPresentationModule 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 @@ -31,6 +32,8 @@ class ArchitectureApp : Application() { // characters feature charactersDataModule, charactersPresentationModule, + // about feature (MVVM contrast) + aboutPresentationModule, ) } } diff --git a/app/src/main/kotlin/com/example/architecture/CharactersViewsRoute.kt b/app/src/main/kotlin/com/example/architecture/CharactersViewsRoute.kt new file mode 100644 index 0000000..6397732 --- /dev/null +++ b/app/src/main/kotlin/com/example/architecture/CharactersViewsRoute.kt @@ -0,0 +1,11 @@ +package com.example.architecture + +import kotlinx.serialization.Serializable + +/** + * Route for the characters list rendered with the classic **Views** toolkit. It lives in `:app` + * because `:app` owns Compose↔View interop — the `:feature:characters:presentation-views` module + * stays navigation-agnostic (it knows nothing about Compose Navigation or this route). + */ +@Serializable +data object CharactersViewsRoute diff --git a/app/src/main/kotlin/com/example/architecture/MainActivity.kt b/app/src/main/kotlin/com/example/architecture/MainActivity.kt index bbeab40..933f289 100644 --- a/app/src/main/kotlin/com/example/architecture/MainActivity.kt +++ b/app/src/main/kotlin/com/example/architecture/MainActivity.kt @@ -1,16 +1,30 @@ package com.example.architecture import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.FragmentActivity +import androidx.fragment.compose.AndroidFragment import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.example.architecture.core.design.system.theme.AppTheme +import com.example.architecture.feature.about.presentation.AboutRoute +import com.example.architecture.feature.about.presentation.aboutGraph +import com.example.architecture.feature.characters.presentation.compose.CharacterDetailRoute import com.example.architecture.feature.characters.presentation.compose.CharacterListRoute import com.example.architecture.feature.characters.presentation.compose.charactersGraph +import com.example.architecture.feature.characters.presentation.views.CharacterListFragment -class MainActivity : ComponentActivity() { +/** + * Hosts the single Compose NavHost and owns every cross-feature / cross-toolkit wiring: + * - the characters graph (Compose list + detail), + * - the About graph (MVVM contrast), + * - the Views renderer embedded via [AndroidFragment] (Compose↔View interop). + * + * Extends [FragmentActivity] (not plain ComponentActivity) so [AndroidFragment] has a FragmentManager. + */ +class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -22,8 +36,23 @@ class MainActivity : ComponentActivity() { startDestination = CharacterListRoute, ) { charactersGraph( - onCharacterClick = { /* Detail navigation is wired in the next milestone. */ }, + navController = navController, + onOpenAbout = { navController.navigate(AboutRoute) }, + onOpenViewsList = { navController.navigate(CharactersViewsRoute) }, ) + aboutGraph( + onNavigateBack = { navController.popBackStack() }, + ) + // Compose↔View interop: the same characters list, rendered by a Fragment. :app + // injects the navigation callbacks so the Views module stays nav-agnostic. + composable { + AndroidFragment { fragment -> + fragment.onCharacterClick = { id -> + navController.navigate(CharacterDetailRoute(id)) + } + fragment.onNavigateBack = { navController.popBackStack() } + } + } } } }