diff --git a/.gitignore b/.gitignore
index 73b4ff2..08afcb7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,5 @@
local.properties
/.kotlin/
/.idea/
+/app/kotzilla.json
+/app/src/main/assets/kotzilla.key
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b589d56..b86273d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ffdfd69..05d8449 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -8,6 +8,8 @@ plugins {
alias(libs.plugins.detekt)
alias(libs.plugins.google.secrets.gradle.plugin)
alias(libs.plugins.automattic.measure.builds)
+ alias(libs.plugins.allopen)
+ id("io.kotzilla.kotzilla-plugin")
}
apply("$rootDir/gradle/report.gradle")
@@ -113,6 +115,10 @@ ksp {
arg("KOIN_CONFIG_CHECK", "true")
}
+allOpen {
+ annotation("org.koin.core.annotation.Monitor")
+}
+
dependencies {
implementation(project(":core"))
implementation(libs.kotlin.stdlib)
@@ -147,6 +153,8 @@ dependencies {
implementation(libs.koin.android)
implementation(libs.koin.androidx.compose)
implementation(libs.koin.androidx.startup)
+ implementation("io.kotzilla:kotzilla-sdk:1.2.3")
+ //implementation("io.kotzilla:kotzilla-sdk-compose:1.2.3")
compileOnly(libs.koin.annotations.core)
ksp(libs.koin.annotations.compiler)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9374963..7ce5aaf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
-
+
@@ -24,8 +25,8 @@
+ android:exported="true"
+ android:label="@string/title_activity_home_view" />
\ No newline at end of file
diff --git a/app/src/main/java/com/santimattius/template/MainApplication.kt b/app/src/main/java/com/santimattius/template/MainApplication.kt
index 552e0c5..e247c95 100644
--- a/app/src/main/java/com/santimattius/template/MainApplication.kt
+++ b/app/src/main/java/com/santimattius/template/MainApplication.kt
@@ -1,26 +1,40 @@
package com.santimattius.template
import android.app.Application
+import android.util.Log
+import com.santimattius.core.CoreModule
+import com.santimattius.template.di.AppModule
+import com.santimattius.template.di.DataModule
+import io.kotzilla.sdk.analytics.koin.analytics
import org.koin.android.ext.koin.androidContext
-import org.koin.android.logger.AndroidLogger
import org.koin.androix.startup.KoinStartup
+import org.koin.core.annotation.KoinApplication
import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.dsl.KoinConfiguration
-import org.koin.ksp.generated.com_santimattius_template_di_AppModule
-import org.koin.ksp.generated.com_santimattius_template_di_DataModule
-import org.koin.ksp.generated.defaultModule
+import org.koin.dsl.module
+import org.koin.ksp.generated.defineComSantimattiusTemplateUiComposeHomeComposeViewModel
+import org.koin.ksp.generated.defineComSantimattiusTemplateUiXmlHomeHomeViewModel
+import org.koin.ksp.generated.koinConfiguration
+
+@KoinApplication(
+ configurations = ["default"],
+ modules = [CoreModule::class, DataModule::class, AppModule::class]
+)
+object MainKoinApplication
@OptIn(KoinExperimentalAPI::class)
class MainApplication : Application(), KoinStartup {
+
override fun onKoinStartup(): KoinConfiguration {
- return KoinConfiguration {
+ Log.d(this::class.simpleName, "onKoinStartup: ${Thread.currentThread().name}")
+ val configuration = MainKoinApplication.koinConfiguration {
androidContext(this@MainApplication)
- logger(AndroidLogger())
- modules(
- com_santimattius_template_di_DataModule,
- com_santimattius_template_di_AppModule,
- defaultModule
- )
+ analytics()
+ modules(modules = module {
+ defineComSantimattiusTemplateUiComposeHomeComposeViewModel()
+ defineComSantimattiusTemplateUiXmlHomeHomeViewModel()
+ })
}
+ return KoinConfiguration(configuration)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/santimattius/template/di/AppModule.kt b/app/src/main/java/com/santimattius/template/di/AppModule.kt
index 9280156..1553dc3 100644
--- a/app/src/main/java/com/santimattius/template/di/AppModule.kt
+++ b/app/src/main/java/com/santimattius/template/di/AppModule.kt
@@ -1,37 +1,21 @@
package com.santimattius.template.di
-import android.content.Context
-import com.santimattius.core.data.client.database.TheMovieDataBase
-import com.santimattius.core.data.client.network.RetrofitServiceCreator
-import com.santimattius.core.data.client.network.TheMovieDBService
import com.santimattius.template.BuildConfig
+import org.koin.core.annotation.Configuration
import org.koin.core.annotation.Module
import org.koin.core.annotation.Named
-import org.koin.core.annotation.Singleton
+import org.koin.core.annotation.Single
@Module
+@Configuration
class AppModule {
- @Singleton
- fun provideAppDatabase(context: Context): TheMovieDataBase =
- TheMovieDataBase.get(context)
-
- @Singleton
- fun provideMovieDBService(serviceCreator: RetrofitServiceCreator): TheMovieDBService =
- serviceCreator.createService(TheMovieDBService::class.java)
-
-
- @Singleton
- fun provideRetrofit(@Named("base_url") baseUrl: String): RetrofitServiceCreator {
- return RetrofitServiceCreator(
- baseUrl = baseUrl,
- apiKey = BuildConfig.apiKey
- )
- }
-
- @Named("base_url")
- @Singleton
- @Suppress("FunctionOnlyReturningConstant")
+ @Single(createdAtStart = true)
+ @Named("baseUrl")
fun provideBaseUrl() = "https://api.themoviedb.org"
+
+ @Single(createdAtStart = true)
+ @Named("apiKey")
+ fun provideApiKey() = BuildConfig.apiKey
}
\ No newline at end of file
diff --git a/app/src/main/java/com/santimattius/template/di/DataModule.kt b/app/src/main/java/com/santimattius/template/di/DataModule.kt
index 7661c2e..b5e60b5 100644
--- a/app/src/main/java/com/santimattius/template/di/DataModule.kt
+++ b/app/src/main/java/com/santimattius/template/di/DataModule.kt
@@ -8,13 +8,15 @@ import com.santimattius.core.data.datasources.implementation.RetrofitMovieNetwor
import com.santimattius.core.data.datasources.implementation.RoomMovieLocalDataSource
import com.santimattius.core.data.repositories.TMDbRepository
import com.santimattius.core.domain.repositories.MovieRepository
+import org.koin.core.annotation.Configuration
import org.koin.core.annotation.Module
-import org.koin.core.annotation.Singleton
+import org.koin.core.annotation.Single
@Module
+@Configuration
class DataModule {
- @Singleton
+ @Single
fun provideMovieRepository(
movieNetworkDataSource: MovieNetworkDataSource,
movieLocalDataSource: MovieLocalDataSource,
@@ -23,12 +25,12 @@ class DataModule {
movieLocalDataSource = movieLocalDataSource
)
- @Singleton
+ @Single
fun provideLocalDataSource(theMovieDataBase: TheMovieDataBase): MovieLocalDataSource {
return RoomMovieLocalDataSource(theMovieDataBase = theMovieDataBase)
}
- @Singleton
+ @Single
fun provideRemoteDataSource(service: TheMovieDBService): MovieNetworkDataSource {
return RetrofitMovieNetworkDataSource(service = service)
}
diff --git a/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeActivity.kt b/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeActivity.kt
index 746fa70..72f6e4f 100644
--- a/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeActivity.kt
+++ b/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeActivity.kt
@@ -40,6 +40,7 @@ import com.santimattius.template.ui.compose.ui.components.snackbar.SnackBarVisua
import com.santimattius.template.ui.compose.ui.theme.AndroidTestingTheme
import com.santimattius.template.ui.xml.home.HomeViewActivity
import org.koin.androidx.compose.koinViewModel
+import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.compose.LocalKoinApplication
import org.koin.compose.LocalKoinScope
import org.koin.core.annotation.KoinInternalApi
@@ -47,6 +48,7 @@ import org.koin.mp.KoinPlatformTools
class HomeComposeActivity : ComponentActivity() {
+ private val viewModel: HomeComposeViewModel by viewModel()
@OptIn(KoinInternalApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
@@ -54,18 +56,19 @@ class HomeComposeActivity : ComponentActivity() {
setContent {
// This shouldn't be needed, but allows robolectric tests to run successfully
// TODO remove once a solution is found or a fix in koin?
- CompositionLocalProvider(
- LocalKoinScope provides KoinPlatformTools.defaultContext()
- .get().scopeRegistry.rootScope,
- LocalKoinApplication provides KoinPlatformTools.defaultContext().get()
- ) {
- AndroidTestingTheme {
- Surface(
- modifier = Modifier.fillMaxSize(),
- color = MaterialTheme.colorScheme.background
- ) {
- HomeRoute()
- }
+// CompositionLocalProvider(
+// LocalKoinScope provides KoinPlatformTools.defaultContext()
+// .get().scopeRegistry.rootScope,
+// LocalKoinApplication provides KoinPlatformTools.defaultContext().get()
+// ) {
+//
+// }
+ AndroidTestingTheme {
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background
+ ) {
+ HomeRoute(viewModel)
}
}
}
@@ -74,7 +77,7 @@ class HomeComposeActivity : ComponentActivity() {
@Composable
fun HomeRoute(
- viewModel: HomeComposeViewModel = koinViewModel()
+ viewModel: HomeComposeViewModel
) {
val snackBarHostState = remember { SnackbarHostState() }
Scaffold(
diff --git a/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeViewModel.kt b/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeViewModel.kt
index 31e5d14..7970f6b 100644
--- a/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeViewModel.kt
+++ b/app/src/main/java/com/santimattius/template/ui/compose/HomeComposeViewModel.kt
@@ -17,8 +17,12 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.koin.android.annotation.KoinViewModel
+import org.koin.core.annotation.Configuration
+import org.koin.core.annotation.Monitor
@KoinViewModel
+@Monitor
+@Configuration
class HomeComposeViewModel(
private val movieRepository: MovieRepository,
) : ViewModel() {
@@ -29,7 +33,7 @@ class HomeComposeViewModel(
if (newMovies != state.movies) {
state.copy(movies = newMovies, isLoading = false)
} else {
- state // Si no ha cambiado nada, retorna el mismo estado
+ state
}
}.onStart {
movieRepository.refresh()
diff --git a/build.gradle.kts b/build.gradle.kts
index 7c820fd..033a144 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,11 +8,13 @@ plugins {
alias(libs.plugins.detekt) apply false
alias(libs.plugins.google.secrets.gradle.plugin) apply false
alias(libs.plugins.automattic.measure.builds) apply false
+ alias(libs.plugins.allopen) apply false
alias(libs.plugins.room) apply false
}
buildscript {
dependencies {
classpath(libs.dep.google.secrets.gradle.plugin)
+ classpath("io.kotzilla:kotzilla-plugin:1.2.3")
}
}
\ No newline at end of file
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 74f026a..c9f30c4 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -72,6 +72,8 @@ dependencies {
implementation(platform(libs.koin.bom))
implementation(libs.koin.android)
implementation(libs.koin.androidx.compose)
+ compileOnly(libs.koin.annotations.core)
+ ksp(libs.koin.annotations.compiler)
testImplementation(project(path = ":shared-test"))
testImplementation(libs.bundles.unitTesting)
diff --git a/core/src/main/java/com/santimattius/core/CoreModule.kt b/core/src/main/java/com/santimattius/core/CoreModule.kt
new file mode 100644
index 0000000..f3b84c4
--- /dev/null
+++ b/core/src/main/java/com/santimattius/core/CoreModule.kt
@@ -0,0 +1,46 @@
+package com.santimattius.core
+
+import android.content.Context
+import com.santimattius.core.data.client.database.TheMovieDataBase
+import com.santimattius.core.data.client.network.RequestInterceptor
+import com.santimattius.core.data.client.network.TheMovieDBService
+import okhttp3.OkHttpClient
+import org.koin.core.annotation.Configuration
+import org.koin.core.annotation.Module
+import org.koin.core.annotation.Named
+import org.koin.core.annotation.Single
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+@Module
+@Configuration
+class CoreModule {
+
+ @Single(createdAtStart = true)
+ fun provideOkHttpClient(@Named("apiKey") apiKey: String): OkHttpClient {
+ return OkHttpClient().newBuilder()
+ .addInterceptor(RequestInterceptor(apiKey))
+ .build()
+ }
+
+ @Single(createdAtStart = true)
+ fun provideRetrofit(
+ @Named("baseUrl") baseUrl: String,
+ client: OkHttpClient
+ ): Retrofit {
+ return Retrofit.Builder()
+ .baseUrl(baseUrl)
+ .client(client)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ }
+
+ @Single(createdAtStart = true)
+ fun provideService(retrofit: Retrofit): TheMovieDBService {
+ return retrofit.create(TheMovieDBService::class.java)
+ }
+
+ @Single(createdAtStart = true)
+ fun provideAppDatabase(context: Context): TheMovieDataBase =
+ TheMovieDataBase.get(context)
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/santimattius/core/data/client/database/TheMovieDataBase.kt b/core/src/main/java/com/santimattius/core/data/client/database/TheMovieDataBase.kt
index 4197c26..b04cf97 100644
--- a/core/src/main/java/com/santimattius/core/data/client/database/TheMovieDataBase.kt
+++ b/core/src/main/java/com/santimattius/core/data/client/database/TheMovieDataBase.kt
@@ -13,7 +13,17 @@ abstract class TheMovieDataBase : RoomDatabase() {
private const val DATABASE_NAME = "tmdb_database"
- fun get(context: Context) =
+ /*@Volatile
+ private var INSTANCE: TheMovieDataBase? = null
+
+ fun get(context: Context) = INSTANCE ?: synchronized(this) {
+ INSTANCE ?: create(context).also { INSTANCE = it }
+ }
+ */
+
+ fun get(context: Context) = create(context)
+
+ private fun create(context: Context) =
Room.databaseBuilder(context, TheMovieDataBase::class.java, DATABASE_NAME)
.build()
}
diff --git a/core/src/main/java/com/santimattius/core/data/client/network/RetrofitServiceCreator.kt b/core/src/main/java/com/santimattius/core/data/client/network/RetrofitServiceCreator.kt
index 4d1f1bd..0a2f173 100644
--- a/core/src/main/java/com/santimattius/core/data/client/network/RetrofitServiceCreator.kt
+++ b/core/src/main/java/com/santimattius/core/data/client/network/RetrofitServiceCreator.kt
@@ -4,16 +4,24 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
+//TODO:only for testing
class RetrofitServiceCreator(baseUrl: String, apiKey: String) {
- val client = OkHttpClient().newBuilder()
- .addInterceptor(RequestInterceptor(apiKey))
- .build()
- private val retrofit: Retrofit = Retrofit.Builder()
- .baseUrl(baseUrl)
- .client(client)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
+ // Use 'by lazy' for client
+ val client: OkHttpClient by lazy { // <--- Made lazy
+ OkHttpClient().newBuilder()
+ .addInterceptor(RequestInterceptor(apiKey))
+ .build()
+ }
+
+ // Use 'by lazy' for retrofit instance
+ private val retrofit: Retrofit by lazy { // <--- Made lazy
+ Retrofit.Builder()
+ .baseUrl(baseUrl)
+ .client(client) // 'client' will be initialized on first access here
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ }
fun createService(serviceClass: Class): T {
return retrofit.create(serviceClass)
diff --git a/gradle.properties b/gradle.properties
index f237e40..2be2282 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -27,5 +27,5 @@ android.nonFinalResIds=false
application_id=com.santimattius.entertainment
min_sdk_version=24
target_sdk_version=36
-version_code=1
-version_name=1.0
\ No newline at end of file
+version_code=2
+version_name=1.3.2
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8adc0f3..9006a85 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,10 +1,10 @@
[versions]
# Plugins
-androidGradlePlugin = "8.12.2"
+androidGradlePlugin = "8.13.0"
hamcrest = "3.0"
-kotlin = "2.2.10"
+kotlin = "2.2.20"
detektGradlePlugin = "1.23.8"
-ksp = "2.2.10-2.0.2"
+ksp = "2.2.20-2.0.2"
googleSecretsPlugin = "2.0.1"
automatticMeasureBuilds = "3.2.1"
@@ -15,23 +15,23 @@ appCompat = "1.7.1"
fragmentKtx = "1.8.9"
constraintLayout = "2.2.1"
recyclerView = "1.4.0"
-materialVersion = "1.12.0"
-lifecycle = "2.9.3"
+materialVersion = "1.13.0"
+lifecycle = "2.9.4"
retrofit = "3.0.0"
-okHttp = "5.1.0"
+okHttp = "5.2.0"
coroutine = "1.10.2"
-gson = "2.13.1"
-glide = "5.0.0-rc01"
+gson = "2.13.2"
+glide = "5.0.5"
coil = "2.7.0"
-room = "2.7.2"
+room = "2.8.1"
-androidxComposeBom = "2025.08.01"
-activityCompose = "1.10.1"
+androidxComposeBom = "2025.09.01"
+activityCompose = "1.11.0"
-koinBom = "4.1.0"
-koinAnnotations = "2.1.0"
+koinBom = "4.1.1"
+koinAnnotations = "2.2.0"
#Testing
junit = "4.13.2"
@@ -44,10 +44,10 @@ fragmentTesting = "1.8.9"
espressoCore = "3.7.0"
okhttp3IdlingResource = "1.0.0"
-mockk = "1.14.5"
+mockk = "1.14.6"
robolectric = "4.16"
turbine = "1.2.1"
-mockitoKotlin = "6.0.0"
+mockitoKotlin = "6.1.0"
[libraries]
# Define the libraries
@@ -153,6 +153,7 @@ kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
room = { id = "androidx.room", version.ref = "room"}
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "kotlin" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detektGradlePlugin" }
google-secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "googleSecretsPlugin" }
automattic-measure-builds = { id = "com.automattic.android.measure-builds", version.ref = "automatticMeasureBuilds" }