diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d7631ff..7aa875e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose.compiler)
+ alias(libs.plugins.ksp)
alias(libs.plugins.ktlint)
}
@@ -9,12 +10,25 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
+
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.navigation)
+ implementation(libs.androidx.compose.navigation.common)
+ implementation(libs.androidx.compose.navigation.common.ktx)
+ implementation(libs.androidx.compose.icons.extended)
+ implementation(libs.androidx.material3)
+
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
- implementation(libs.androidx.material3)
+
+ implementation(platform(libs.koin.bom))
+ implementation(libs.koin.core)
+ implementation(libs.koin.android)
+ implementation(libs.koin.android.compose)
+ implementation(libs.koin.annotations)
+ ksp(libs.koin.ksp)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -75,3 +89,7 @@ android {
}
}
}
+
+ksp {
+ arg("KOIN_CONFIG_CHECK", "true")
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7418bb0..d95c1f9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,11 +11,11 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NewsApp"
+ android:name=".main.MainApplication"
tools:targetApi="31">
@@ -25,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/germanautolabs/acaraus/MainActivity.kt b/app/src/main/java/com/germanautolabs/acaraus/main/MainActivity.kt
similarity index 51%
rename from app/src/main/java/com/germanautolabs/acaraus/MainActivity.kt
rename to app/src/main/java/com/germanautolabs/acaraus/main/MainActivity.kt
index f5575a0..155951c 100644
--- a/app/src/main/java/com/germanautolabs/acaraus/MainActivity.kt
+++ b/app/src/main/java/com/germanautolabs/acaraus/main/MainActivity.kt
@@ -1,47 +1,28 @@
-package com.germanautolabs.acaraus
+package com.germanautolabs.acaraus.main
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
import com.germanautolabs.acaraus.ui.theme.NewsAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ render()
+ }
+
+ private fun render() {
enableEdgeToEdge()
setContent {
NewsAppTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
- Greeting(
- name = "Android",
- modifier = Modifier.padding(innerPadding),
- )
+ MainNavHost(innerPadding = innerPadding)
}
}
}
}
}
-
-@Composable
-fun Greeting(name: String, modifier: Modifier = Modifier) {
- Text(
- text = "Hello $name!",
- modifier = modifier,
- )
-}
-
-@Preview(showBackground = true)
-@Composable
-fun GreetingPreview() {
- NewsAppTheme {
- Greeting("Android")
- }
-}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/main/MainApplication.kt b/app/src/main/java/com/germanautolabs/acaraus/main/MainApplication.kt
new file mode 100644
index 0000000..174c220
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/main/MainApplication.kt
@@ -0,0 +1,10 @@
+package com.germanautolabs.acaraus.main
+
+import android.app.Application
+
+class MainApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ setupMainDi(this@MainApplication)
+ }
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/main/MainDi.kt b/app/src/main/java/com/germanautolabs/acaraus/main/MainDi.kt
new file mode 100644
index 0000000..9d5735a
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/main/MainDi.kt
@@ -0,0 +1,22 @@
+package com.germanautolabs.acaraus.main
+
+import android.content.Context
+import org.koin.android.ext.koin.androidContext
+import org.koin.android.ext.koin.androidLogger
+import org.koin.core.annotation.ComponentScan
+import org.koin.core.annotation.Module
+import org.koin.core.context.startKoin
+import org.koin.core.logger.Level
+import org.koin.ksp.generated.module
+
+@ComponentScan(value = "com.germanautolabs.acaraus")
+@Module
+class MainDi
+
+fun setupMainDi(applicationContext: Context) = startKoin {
+ androidLogger(Level.WARNING)
+ androidContext(applicationContext)
+ modules(
+ MainDi().module,
+ )
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/main/MainNavHost.kt b/app/src/main/java/com/germanautolabs/acaraus/main/MainNavHost.kt
new file mode 100644
index 0000000..034918e
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/main/MainNavHost.kt
@@ -0,0 +1,35 @@
+package com.germanautolabs.acaraus.main
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.rememberNavController
+import com.germanautolabs.acaraus.screens.articles.details.articleDetailScreen
+import com.germanautolabs.acaraus.screens.articles.details.onNavigateToDetails
+import com.germanautolabs.acaraus.screens.articles.list.ARTICLE_LIST_ROUTE
+import com.germanautolabs.acaraus.screens.articles.list.articleListScreen
+
+@Composable
+fun MainNavHost(
+ modifier: Modifier = Modifier,
+ navController: NavHostController = rememberNavController(),
+ innerPadding: PaddingValues,
+ route: String = ARTICLE_LIST_ROUTE,
+) = NavHost(
+ navController = navController,
+ startDestination = route,
+ modifier = modifier.padding(paddingValues = innerPadding),
+) {
+ articleListScreen(
+ modifier = modifier,
+ onNavigateToDetails = navController::onNavigateToDetails,
+ )
+
+ articleDetailScreen(
+ modifier = modifier,
+ onNavigateBack = navController::popBackStack,
+ )
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/models/Article.kt b/app/src/main/java/com/germanautolabs/acaraus/models/Article.kt
new file mode 100644
index 0000000..647c6e1
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/models/Article.kt
@@ -0,0 +1,9 @@
+package com.germanautolabs.acaraus.models
+
+data class Article(
+ val id: String,
+ val source: String,
+ val title: String,
+ val description: String?,
+ val imageURL: String?,
+)
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailNav.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailNav.kt
new file mode 100644
index 0000000..f1f9cb3
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailNav.kt
@@ -0,0 +1,30 @@
+package com.germanautolabs.acaraus.screens.articles.details
+
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import com.germanautolabs.acaraus.models.Article
+import org.koin.androidx.compose.koinViewModel
+import org.koin.core.parameter.parametersOf
+
+const val ARTICLE_DETAIL_ROUTE = "article-detail"
+private const val ARTICLE_ID_PARAM = "article-id"
+
+fun NavController.onNavigateToDetails(article: Article) =
+ navigate("$ARTICLE_DETAIL_ROUTE/?$ARTICLE_ID_PARAM=${article.id}")
+
+fun NavGraphBuilder.articleDetailScreen(
+ modifier: Modifier = Modifier,
+ onNavigateBack: () -> Unit,
+) {
+ composable(route = "$ARTICLE_DETAIL_ROUTE/?$ARTICLE_ID_PARAM={$ARTICLE_ID_PARAM}") { backStack ->
+ val articleId = backStack.arguments?.getString(ARTICLE_ID_PARAM)
+ val vm = koinViewModel { parametersOf(articleId) }
+ ArticleDetailScreen(
+ modifier = modifier,
+ vm = vm,
+ onNavigateBack = onNavigateBack,
+ )
+ }
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailScreen.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailScreen.kt
new file mode 100644
index 0000000..66a6abb
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailScreen.kt
@@ -0,0 +1,19 @@
+package com.germanautolabs.acaraus.screens.articles.details
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+@Composable
+fun ArticleDetailScreen(
+ modifier: Modifier = Modifier,
+ vm: ArticleDetailViewModel,
+ onNavigateBack: () -> Unit,
+) = Column(modifier = modifier) {
+ Text("Article Detail Screen article: ${vm.articleId}")
+ Button(onClick = onNavigateBack) {
+ Text("Close")
+ }
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailViewModel.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailViewModel.kt
new file mode 100644
index 0000000..2af2427
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/details/ArticleDetailViewModel.kt
@@ -0,0 +1,10 @@
+package com.germanautolabs.acaraus.screens.articles.details
+
+import androidx.lifecycle.ViewModel
+import org.koin.android.annotation.KoinViewModel
+import org.koin.core.annotation.InjectedParam
+
+@KoinViewModel
+class ArticleDetailViewModel(
+ @InjectedParam val articleId: String,
+) : ViewModel()
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListNav.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListNav.kt
new file mode 100644
index 0000000..4fc6d8c
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListNav.kt
@@ -0,0 +1,23 @@
+package com.germanautolabs.acaraus.screens.articles.list
+
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import com.germanautolabs.acaraus.models.Article
+import org.koin.androidx.compose.koinViewModel
+
+const val ARTICLE_LIST_ROUTE = "article-list"
+
+fun NavGraphBuilder.articleListScreen(
+ modifier: Modifier = Modifier,
+ onNavigateToDetails: (article: Article) -> Unit,
+) {
+ composable(route = ARTICLE_LIST_ROUTE) {
+ val vm = koinViewModel()
+ ArticleListScreen(
+ modifier = modifier,
+ vm = vm,
+ onNavigateToDetails = onNavigateToDetails,
+ )
+ }
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListScreen.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListScreen.kt
new file mode 100644
index 0000000..7abafcd
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListScreen.kt
@@ -0,0 +1,45 @@
+package com.germanautolabs.acaraus.screens.articles.list
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.germanautolabs.acaraus.models.Article
+
+@Composable
+fun ArticleListScreen(
+ modifier: Modifier = Modifier,
+ vm: ArticleListViewModel,
+ onNavigateToDetails: (article: Article) -> Unit,
+) = Column(modifier = modifier) {
+ Text("Article List Screen")
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ items(vm.list) { article ->
+ ArticleListItem(
+ article = article,
+ onNavigateToDetails = onNavigateToDetails,
+ )
+ }
+ }
+}
+
+@Composable
+fun ArticleListItem(
+ modifier: Modifier = Modifier,
+ article: Article,
+ onNavigateToDetails: (article: Article) -> Unit,
+) = Card(onClick = { onNavigateToDetails(article) }) {
+ Column(modifier = modifier.padding(8.dp)) {
+ Text("Title: ${article.title}")
+ Text("Description: ${article.description}")
+ Text("Source: ${article.source}")
+ }
+}
diff --git a/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListViewModel.kt b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListViewModel.kt
new file mode 100644
index 0000000..3d65e3c
--- /dev/null
+++ b/app/src/main/java/com/germanautolabs/acaraus/screens/articles/list/ArticleListViewModel.kt
@@ -0,0 +1,25 @@
+package com.germanautolabs.acaraus.screens.articles.list
+
+import androidx.lifecycle.ViewModel
+import com.germanautolabs.acaraus.models.Article
+import org.koin.android.annotation.KoinViewModel
+
+@KoinViewModel
+class ArticleListViewModel : ViewModel() {
+
+ private val defaultArticle = Article(
+ id = "0",
+ title = "Old news",
+ description = "Everything new is a well-forgotten old",
+ source = "Internet",
+ imageURL = null,
+ )
+
+ val list = listOf(
+ defaultArticle,
+ defaultArticle.copy(id = "1", title = "New news 1"),
+ defaultArticle.copy(id = "2", title = "New news 2"),
+ defaultArticle.copy(id = "3", title = "New news 3"),
+ defaultArticle.copy(id = "4", title = "New news 4"),
+ )
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 70d1268..c2d95f1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,4 +1,6 @@
+#################################
[versions]
+#################################
# Android
appPackageId = "com.germanautolabs.acaraus"
@@ -12,17 +14,25 @@ coreKtx = "1.13.1"
lifecycleRuntimeKtx = "2.8.4"
activityCompose = "1.9.1"
composeBom = "2024.06.00"
+compose = "1.7.0-beta06"
+compose-nav = "2.8.0-beta06"
+
+koin-bom = "4.0.0-RC1"
+koin-ksp = "1.4.0-RC3"
espressoCore = "3.6.1"
junit = "4.13.2"
junitVersion = "1.2.1"
# Plugins
-ktlint = "12.1.1"
agp = "8.6.0-beta02"
+ksp = "2.0.0-1.0.24"
+ktlint = "12.1.1"
test-coverage-plugin = "1.8.0"
+###################################
[libraries]
+###################################
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
@@ -34,18 +44,39 @@ androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-compose-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }
+
+androidx-compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "compose-nav" }
+androidx-compose-navigation-common = { module = "androidx.navigation:navigation-common", version.ref = "compose-nav" }
+androidx-compose-navigation-common-ktx = { module = "androidx.navigation:navigation-common-ktx", version.ref = "compose-nav" }
+
+koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
+koin-core = { module = "io.insert-koin:koin-core" }
+koin-android = { module = "io.insert-koin:koin-android" }
+koin-android-compose = { module = "io.insert-koin:koin-androidx-compose" }
+koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin-ksp" }
+koin-ksp = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koin-ksp" }
+
+
# Instrumented tests
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+
# Unit tests
junit = { group = "junit", name = "junit", version.ref = "junit" }
+#######################
[plugins]
+######################
+
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
test-coverage = { id = "nl.neotech.plugin.rootcoverage", version.ref = "test-coverage-plugin" }
+