diff --git a/.github/workflows/check_pr.yml b/.github/workflows/check_pr.yml
index 52399736b..268149ec1 100644
--- a/.github/workflows/check_pr.yml
+++ b/.github/workflows/check_pr.yml
@@ -40,11 +40,11 @@ jobs:
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- - name: Set up JDK 11
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'zulu'
- java-version: 11
+ java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
diff --git a/.gitignore b/.gitignore
index daeda5d1d..513558f29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -708,3 +708,7 @@
/.gradle/7.6/checksums/sha1-checksums.bin
/buildSrc/.gradle/7.6/checksums/sha1-checksums.bin
/config/movies.jks
+/.gradle/
+/.idea/
+/buildSrc/build/classes/kotlin/main/ktx/
+/buildSrc/.gradle/
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index dc74f9e80..95d799a33 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,17 +1,18 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
-import java.io.FileInputStream
import org.apache.commons.io.output.ByteArrayOutputStream
import org.jetbrains.kotlin.konan.properties.Properties
+import java.io.FileInputStream
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-application")
- id("movies-android-application-compose")
+ alias(libs.plugins.application)
+ alias(libs.plugins.kotlin)
+ alias(libs.plugins.androidx.navigation.safeargs)
+ alias(libs.plugins.google.services)
+ alias(libs.plugins.firebase.appdistribution)
+ alias(libs.plugins.firebase.crashlytics)
+ alias(libs.plugins.palantir.git)
id("movies-android-hilt")
- id("androidx.navigation.safeargs")
- id("com.google.gms.google-services")
- id("com.google.firebase.appdistribution")
- id("com.google.firebase.crashlytics")
- id("com.palantir.git-version")
}
val gitCommitsCount: Int by lazy {
@@ -27,11 +28,11 @@ val currentTime: Long by lazy {
System.currentTimeMillis()
}
-val admobAppId: String by lazy {
+val admobAppId: String? by lazy {
gradleLocalProperties(rootDir).getProperty("ADMOB_APP_ID")
}
-val admobBannerId: String by lazy {
+val admobBannerId: String? by lazy {
gradleLocalProperties(rootDir).getProperty("ADMOB_BANNER_ID")
}
@@ -129,6 +130,7 @@ android {
}
buildFeatures {
+ buildConfig = true
compose = true
}
@@ -136,6 +138,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -165,4 +172,6 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.benchmark.junit)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fa5ae7e48..a5ba5e8e8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -95,9 +95,9 @@
-
+ android:value="@string/admobAppId" />-->
()
- .setInputData(workDataOf(MoviesDatabaseWorker.KEY_FILENAME to MOVIES_DATA_FILENAME))
- .build()
- WorkManager.getInstance(this).enqueue(request)
- }
-
- private companion object {
- private const val MOVIES_DATA_FILENAME = "movies.json"
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/org/michaelbel/movies/MainViewModel.kt b/app/src/main/kotlin/org/michaelbel/movies/MainViewModel.kt
index d102e541a..29e61d73c 100644
--- a/app/src/main/kotlin/org/michaelbel/movies/MainViewModel.kt
+++ b/app/src/main/kotlin/org/michaelbel/movies/MainViewModel.kt
@@ -3,7 +3,11 @@ package org.michaelbel.movies
import android.os.Bundle
import androidx.compose.ui.unit.LayoutDirection
import androidx.navigation.NavDestination
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.workDataOf
import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
@@ -12,11 +16,13 @@ import org.michaelbel.movies.analytics.MoviesAnalytics
import org.michaelbel.movies.common.theme.AppTheme
import org.michaelbel.movies.common.viewmodel.BaseViewModel
import org.michaelbel.movies.domain.interactor.settings.SettingsInteractor
-import javax.inject.Inject
+import org.michaelbel.movies.domain.workers.AccountUpdateWorker
+import org.michaelbel.movies.domain.workers.MoviesDatabaseWorker
@HiltViewModel
internal class MainViewModel @Inject constructor(
private val settingsInteractor: SettingsInteractor,
+ private val workManager: WorkManager,
private val analytics: MoviesAnalytics
): BaseViewModel() {
@@ -43,6 +49,8 @@ internal class MainViewModel @Inject constructor(
init {
fetchRemoteConfig()
+ prepopulateDatabase()
+ updateAccountDetails()
}
fun analyticsTrackDestination(destination: NavDestination, arguments: Bundle?) {
@@ -52,4 +60,21 @@ internal class MainViewModel @Inject constructor(
private fun fetchRemoteConfig() = launch {
settingsInteractor.fetchRemoteConfig()
}
+
+ private fun prepopulateDatabase() {
+ val request = OneTimeWorkRequestBuilder()
+ .setInputData(workDataOf(MoviesDatabaseWorker.KEY_FILENAME to MOVIES_DATA_FILENAME))
+ .build()
+ workManager.enqueue(request)
+ }
+
+ private fun updateAccountDetails() {
+ val request = OneTimeWorkRequestBuilder()
+ .build()
+ workManager.enqueue(request)
+ }
+
+ private companion object {
+ private const val MOVIES_DATA_FILENAME = "movies.json"
+ }
}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 80a98f15d..4a6ea330e 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,3 +1,4 @@
+@Suppress("dsl_scope_violation")
plugins {
alias(libs.plugins.application) apply false
alias(libs.plugins.library) apply false
@@ -12,10 +13,9 @@ plugins {
alias(libs.plugins.hilt) apply false
alias(libs.plugins.spotless)
alias(libs.plugins.detekt)
-
- id("com.github.ben-manes.versions") version "0.46.0"
- id("nl.littlerobots.version-catalog-update") version "0.8.0"
- id("com.palantir.git-version") version "2.0.0"
+ alias(libs.plugins.palantir.git)
+ alias(libs.plugins.ben.manes.versions)
+ alias(libs.plugins.littlerobots.version.catalog.update)
}
/**
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 8a68b018b..7a034a208 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -10,22 +10,6 @@ dependencies {
gradlePlugin {
plugins {
- register("androidApplication") {
- id = "movies-android-application"
- implementationClass = "plugins.AndroidApplicationConventionPlugin"
- }
- register("androidLibrary") {
- id = "movies-android-library"
- implementationClass = "plugins.AndroidLibraryConventionPlugin"
- }
- register("androidComposeApplication") {
- id = "movies-android-application-compose"
- implementationClass = "plugins.AndroidComposeApplicationConventionPlugin"
- }
- register("androidComposeLibrary") {
- id = "movies-android-library-compose"
- implementationClass = "plugins.AndroidComposeLibraryConventionPlugin"
- }
register("androidHilt") {
id = "movies-android-hilt"
implementationClass = "plugins.AndroidHiltConventionPlugin"
diff --git a/buildSrc/src/main/kotlin/plugins/AndroidApplicationConventionPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidApplicationConventionPlugin.kt
deleted file mode 100644
index a161e33f6..000000000
--- a/buildSrc/src/main/kotlin/plugins/AndroidApplicationConventionPlugin.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package plugins
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-internal class AndroidApplicationConventionPlugin: Plugin {
-
- override fun apply(target: Project) {
- target.run {
- pluginManager.run {
- apply("com.android.application")
- apply("org.jetbrains.kotlin.android")
- }
-
- /*val libs: VersionCatalog = extensions.getByType().named("libs")
- extensions.configure {
- defaultConfig.targetSdk = libs.findVersion("target-sdk").get().requiredVersion.toInt()
- configureKotlinAndroid(this)
- configureLintCheck(this)
- }*/
- }
- }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/plugins/AndroidComposeApplicationConventionPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidComposeApplicationConventionPlugin.kt
deleted file mode 100644
index 2f8359cdf..000000000
--- a/buildSrc/src/main/kotlin/plugins/AndroidComposeApplicationConventionPlugin.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package plugins
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-internal class AndroidComposeApplicationConventionPlugin: Plugin {
-
- override fun apply(target: Project) {
- target.run {
- pluginManager.run {
- apply("com.android.application")
- }
-
- /*val extension: ApplicationExtension = extensions.getByType()
- configureAndroidCompose(extension)*/
- }
- }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/plugins/AndroidComposeLibraryConventionPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidComposeLibraryConventionPlugin.kt
deleted file mode 100644
index 60265dd9c..000000000
--- a/buildSrc/src/main/kotlin/plugins/AndroidComposeLibraryConventionPlugin.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package plugins
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-internal class AndroidComposeLibraryConventionPlugin: Plugin {
-
- override fun apply(target: Project) {
- target.run {
- pluginManager.run {
- apply("com.android.library")
- }
-
- /*val extension: LibraryExtension = extensions.getByType()
- configureAndroidCompose(extension)*/
- }
- }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/plugins/AndroidLibraryConventionPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidLibraryConventionPlugin.kt
deleted file mode 100644
index cb53208f0..000000000
--- a/buildSrc/src/main/kotlin/plugins/AndroidLibraryConventionPlugin.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package plugins
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-internal class AndroidLibraryConventionPlugin: Plugin {
-
- override fun apply(target: Project) {
- target.run {
- pluginManager.run {
- apply("com.android.library")
- apply("org.jetbrains.kotlin.android")
- }
-
- /*val libs: VersionCatalog = extensions.getByType().named("libs")
- extensions.configure {
- defaultConfig.targetSdk = libs.findVersion("target-sdk").get().requiredVersion.toInt()
- configureKotlinAndroid(this)
- configureLintCheck(this)
- }*/
- }
- }
-}
\ No newline at end of file
diff --git a/core/ads/build.gradle.kts b/core/ads/build.gradle.kts
index 3ba6212f7..86b545ec5 100644
--- a/core/ads/build.gradle.kts
+++ b/core/ads/build.gradle.kts
@@ -1,5 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -18,6 +20,11 @@ android {
}
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -28,5 +35,5 @@ android {
}
dependencies {
- api(libs.play.services.ads)
+ /*api(libs.play.services.ads)*/
}
\ No newline at end of file
diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts
index c07323583..5f0b79d1f 100644
--- a/core/analytics/build.gradle.kts
+++ b/core/analytics/build.gradle.kts
@@ -1,5 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -19,6 +21,11 @@ android {
}
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
index f94e4f573..2a7c77555 100644
--- a/core/common/build.gradle.kts
+++ b/core/common/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -29,6 +30,7 @@ android {
}
buildFeatures {
+ buildConfig = true
compose = true
}
@@ -36,6 +38,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -60,4 +67,6 @@ dependencies {
implementation(libs.firebase.crashlytics)
implementation(libs.androidx.startup.runtime)
implementation(libs.androidx.browser)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml
index 1a9db05ab..07a9a6fc2 100644
--- a/core/common/src/main/AndroidManifest.xml
+++ b/core/common/src/main/AndroidManifest.xml
@@ -11,9 +11,9 @@
android:exported="false"
tools:node="merge">
-
+ android:value="androidx.startup"/>-->
{
override fun create(context: Context): FirebaseCrashlytics {
@@ -18,4 +11,4 @@ internal class FirebaseCrashlyticsInitializer: Initializer
}
override fun dependencies(): List>> = emptyList()
-}
\ No newline at end of file
+}*/
\ No newline at end of file
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/MoviesDispatchers.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/MoviesDispatchers.kt
new file mode 100644
index 000000000..acab66710
--- /dev/null
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/MoviesDispatchers.kt
@@ -0,0 +1,10 @@
+package org.michaelbel.movies.common.dispatchers
+
+import kotlinx.coroutines.CoroutineDispatcher
+
+interface MoviesDispatchers {
+ val default: CoroutineDispatcher
+ val io: CoroutineDispatcher
+ val main: CoroutineDispatcher
+ val immediate: CoroutineDispatcher
+}
\ No newline at end of file
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/di/DispatchersModule.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/di/DispatchersModule.kt
new file mode 100644
index 000000000..7ebc76d0c
--- /dev/null
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/di/DispatchersModule.kt
@@ -0,0 +1,18 @@
+package org.michaelbel.movies.common.dispatchers.di
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.impl.MoviesDispatchersImpl
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface DispatchersModule {
+
+ @Binds
+ fun provideDispatchers(
+ dispatchers: MoviesDispatchersImpl
+ ): MoviesDispatchers
+}
\ No newline at end of file
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/impl/MoviesDispatchersImpl.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/impl/MoviesDispatchersImpl.kt
new file mode 100644
index 000000000..5ac749b12
--- /dev/null
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/dispatchers/impl/MoviesDispatchersImpl.kt
@@ -0,0 +1,21 @@
+package org.michaelbel.movies.common.dispatchers.impl
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
+import javax.inject.Inject
+
+internal class MoviesDispatchersImpl @Inject constructor(): MoviesDispatchers {
+
+ override val default: CoroutineDispatcher
+ get() = Dispatchers.Default
+
+ override val io: CoroutineDispatcher
+ get() = Dispatchers.IO
+
+ override val main: CoroutineDispatcher
+ get() = Dispatchers.Main
+
+ override val immediate: CoroutineDispatcher
+ get() = Dispatchers.Main.immediate
+}
\ No newline at end of file
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/ktx/TimeKtx.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/ktx/TimeKtx.kt
new file mode 100644
index 000000000..2f07f62ef
--- /dev/null
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/ktx/TimeKtx.kt
@@ -0,0 +1,5 @@
+package org.michaelbel.movies.common.ktx
+
+fun isTimePasses(interval: Long, expireTime: Long, currentTime: Long): Boolean {
+ return (currentTime - expireTime) >= interval
+}
\ No newline at end of file
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/localization/impl/LocaleControllerImpl.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/localization/impl/LocaleControllerImpl.kt
index e40612a02..4968d5c4f 100644
--- a/core/common/src/main/kotlin/org/michaelbel/movies/common/localization/impl/LocaleControllerImpl.kt
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/localization/impl/LocaleControllerImpl.kt
@@ -2,20 +2,18 @@ package org.michaelbel.movies.common.localization.impl
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.withContext
import org.michaelbel.movies.analytics.MoviesAnalytics
import org.michaelbel.movies.analytics.event.SelectLanguageEvent
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.common.localization.LocaleController
import org.michaelbel.movies.common.localization.model.AppLanguage
import javax.inject.Inject
internal class LocaleControllerImpl @Inject constructor(
- @Dispatcher(MoviesDispatchers.Main) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val analytics: MoviesAnalytics
): LocaleController {
@@ -24,7 +22,7 @@ internal class LocaleControllerImpl @Inject constructor(
override val appLanguage: Flow = flowOf(AppLanguage.transform(language))
- override suspend fun selectLanguage(language: AppLanguage) = withContext(dispatcher) {
+ override suspend fun selectLanguage(language: AppLanguage) = withContext(dispatchers.io) {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(language.code))
analytics.logEvent(SelectLanguageEvent(language.toString()))
}
diff --git a/core/common/src/main/kotlin/org/michaelbel/movies/common/timber/TimberInitializer.kt b/core/common/src/main/kotlin/org/michaelbel/movies/common/timber/TimberInitializer.kt
index e4b71108a..07fdcbcdc 100644
--- a/core/common/src/main/kotlin/org/michaelbel/movies/common/timber/TimberInitializer.kt
+++ b/core/common/src/main/kotlin/org/michaelbel/movies/common/timber/TimberInitializer.kt
@@ -4,7 +4,6 @@ import android.content.Context
import androidx.startup.Initializer
import org.michaelbel.movies.common.BuildConfig
import org.michaelbel.movies.common.crashlytics.CrashlyticsTree
-import org.michaelbel.movies.common.crashlytics.FirebaseCrashlyticsInitializer
import timber.log.Timber
@Suppress("unused")
@@ -15,6 +14,6 @@ internal class TimberInitializer: Initializer {
}
override fun dependencies(): List>> {
- return listOf(FirebaseCrashlyticsInitializer::class.java)
+ return emptyList() /*listOf(FirebaseCrashlyticsInitializer::class.java)*/
}
}
\ No newline at end of file
diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts
index 0655170e6..ad78eb065 100644
--- a/core/domain/build.gradle.kts
+++ b/core/domain/build.gradle.kts
@@ -1,7 +1,9 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
+ alias(libs.plugins.kotlin.ksp)
id("movies-android-hilt")
- id("com.google.devtools.ksp")
}
android {
@@ -28,6 +30,11 @@ android {
)
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -45,7 +52,6 @@ dependencies {
implementation(libs.bundles.datastore)
implementation(libs.bundles.room)
api(libs.androidx.hilt.work)
- kapt(libs.androidx.hilt.compiler)
api(libs.androidx.work.runtime.ktx)
ksp(libs.androidx.room.compiler)
}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/AppDatabase.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/AppDatabase.kt
index bb4702c9e..82fcb9be1 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/AppDatabase.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/AppDatabase.kt
@@ -26,7 +26,7 @@ import org.michaelbel.movies.domain.data.entity.PagingKeyDb
exportSchema = false
)
@TypeConverters(CalendarConverter::class)
-abstract class AppDatabase: RoomDatabase() {
+internal abstract class AppDatabase: RoomDatabase() {
abstract fun movieDao(): MovieDao
abstract fun accountDao(): AccountDao
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/AccountDao.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/AccountDao.kt
index 34f27b533..42266d15a 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/AccountDao.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/AccountDao.kt
@@ -11,7 +11,7 @@ import org.michaelbel.movies.domain.data.entity.AccountDb
* The Data Access Object for the [AccountDb] class.
*/
@Dao
-interface AccountDao {
+internal interface AccountDao {
@Query("SELECT * FROM accounts WHERE id = :accountId")
fun accountById(accountId: Int): Flow
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/MovieDao.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/MovieDao.kt
index 033ac26a0..398fa5752 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/MovieDao.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/MovieDao.kt
@@ -25,7 +25,7 @@ interface MovieDao {
@Query("SELECT * FROM movies WHERE id = :movieId")
suspend fun movieById(movieId: Int): MovieDb?
- @Query("SELECT MAX(position) from movies WHERE movieList = :movieList")
+ @Query("SELECT MAX(position) FROM movies WHERE movieList = :movieList")
suspend fun maxPosition(movieList: String): Int?
@Query("SELECT COUNT(*) FROM movies WHERE movieList = :movieList")
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/PagingKeyDao.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/PagingKeyDao.kt
index 3ee023d4f..7ba0c5392 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/PagingKeyDao.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/data/dao/PagingKeyDao.kt
@@ -10,7 +10,7 @@ import org.michaelbel.movies.domain.data.entity.PagingKeyDb
* The Data Access Object for the [PagingKeyDb] class.
*/
@Dao
-interface PagingKeyDao {
+internal interface PagingKeyDao {
@Query("SELECT * FROM pagingkeys WHERE movieList = :movieList")
suspend fun pagingKey(movieList: String): PagingKeyDb?
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/account/impl/AccountInteractorImpl.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/account/impl/AccountInteractorImpl.kt
index 42e5e731e..d1d60174b 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/account/impl/AccountInteractorImpl.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/account/impl/AccountInteractorImpl.kt
@@ -1,21 +1,19 @@
package org.michaelbel.movies.domain.interactor.account.impl
-import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.domain.data.entity.AccountDb
import org.michaelbel.movies.domain.interactor.account.AccountInteractor
import org.michaelbel.movies.domain.repository.account.AccountRepository
+import org.michaelbel.movies.domain.usecase.DelayUseCase
import javax.inject.Inject
import javax.inject.Singleton
-import kotlinx.coroutines.delay
-import org.michaelbel.movies.domain.usecase.DelayUseCase
@Singleton
internal class AccountInteractorImpl @Inject constructor(
- @Dispatcher(MoviesDispatchers.IO) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val accountRepository: AccountRepository,
private val delayUseCase: DelayUseCase
): AccountInteractor {
@@ -25,6 +23,6 @@ internal class AccountInteractorImpl @Inject constructor(
override suspend fun accountDetails() {
delay(delayUseCase.networkRequestDelay())
- return withContext(dispatcher) { accountRepository.accountDetails() }
+ return withContext(dispatchers.io) { accountRepository.accountDetails() }
}
}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/authentication/impl/AuthenticationInteractorImpl.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/authentication/impl/AuthenticationInteractorImpl.kt
index 0ddf186cb..c52e8c803 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/authentication/impl/AuthenticationInteractorImpl.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/authentication/impl/AuthenticationInteractorImpl.kt
@@ -1,27 +1,25 @@
package org.michaelbel.movies.domain.interactor.authentication.impl
-import javax.inject.Inject
-import javax.inject.Singleton
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.domain.interactor.authentication.AuthenticationInteractor
import org.michaelbel.movies.domain.repository.authentication.AuthenticationRepository
import org.michaelbel.movies.domain.usecase.DelayUseCase
import org.michaelbel.movies.network.model.Session
import org.michaelbel.movies.network.model.Token
+import javax.inject.Inject
+import javax.inject.Singleton
@Singleton
internal class AuthenticationInteractorImpl @Inject constructor(
- @Dispatcher(MoviesDispatchers.IO) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val authenticationRepository: AuthenticationRepository,
private val delayUseCase: DelayUseCase
): AuthenticationInteractor {
override suspend fun createRequestToken(): Token {
- return withContext(dispatcher) { authenticationRepository.createRequestToken() }
+ return withContext(dispatchers.io) { authenticationRepository.createRequestToken() }
}
override suspend fun createSessionWithLogin(
@@ -29,13 +27,13 @@ internal class AuthenticationInteractorImpl @Inject constructor(
password: String,
requestToken: String
): Token {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
authenticationRepository.createSessionWithLogin(username, password, requestToken)
}
}
override suspend fun createSession(token: String): Session {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
authenticationRepository.createSession(token)
}
}
@@ -43,7 +41,7 @@ internal class AuthenticationInteractorImpl @Inject constructor(
override suspend fun deleteSession() {
delay(delayUseCase.networkRequestDelay())
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
authenticationRepository.deleteSession()
}
}
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/movie/impl/MovieInteractorImpl.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/movie/impl/MovieInteractorImpl.kt
index 6e7c0c074..5771b2351 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/movie/impl/MovieInteractorImpl.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/movie/impl/MovieInteractorImpl.kt
@@ -1,13 +1,9 @@
package org.michaelbel.movies.domain.interactor.movie.impl
import androidx.paging.PagingSource
-import javax.inject.Inject
-import javax.inject.Singleton
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.domain.data.entity.MovieDb
import org.michaelbel.movies.domain.interactor.movie.MovieInteractor
import org.michaelbel.movies.domain.repository.movie.MovieRepository
@@ -15,10 +11,12 @@ import org.michaelbel.movies.domain.usecase.DelayUseCase
import org.michaelbel.movies.entities.Either
import org.michaelbel.movies.network.model.MovieResponse
import org.michaelbel.movies.network.model.Result
+import javax.inject.Inject
+import javax.inject.Singleton
@Singleton
internal class MovieInteractorImpl @Inject constructor(
- @Dispatcher(MoviesDispatchers.IO) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val movieRepository: MovieRepository,
private val delayUseCase: DelayUseCase
): MovieInteractor {
@@ -30,7 +28,7 @@ internal class MovieInteractorImpl @Inject constructor(
override suspend fun moviesResult(movieList: String, page: Int): Result {
delay(delayUseCase.networkRequestDelay())
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.moviesResult(movieList, page)
}
}
@@ -38,37 +36,37 @@ internal class MovieInteractorImpl @Inject constructor(
override suspend fun movieDetails(movieId: Int): Either {
delay(delayUseCase.networkRequestDelay())
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.movieDetails(movieId)
}
}
override suspend fun removeAllMovies(movieList: String) {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.removeAllMovies(movieList)
}
}
override suspend fun insertAllMovies(movieList: String, movies: List) {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.insertAllMovies(movieList, movies)
}
}
override suspend fun page(movieList: String): Int? {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.page(movieList)
}
}
override suspend fun removePagingKey(movieList: String) {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.removePagingKey(movieList)
}
}
override suspend fun insertPagingKey(movieList: String, page: Int) {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
movieRepository.insertPagingKey(movieList, page)
}
}
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/settings/impl/SettingsInteractorImpl.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/settings/impl/SettingsInteractorImpl.kt
index f133b9d7c..4f6585f93 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/settings/impl/SettingsInteractorImpl.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/interactor/settings/impl/SettingsInteractorImpl.kt
@@ -2,7 +2,6 @@ package org.michaelbel.movies.domain.interactor.settings.impl
import androidx.compose.ui.unit.LayoutDirection
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.withContext
@@ -11,8 +10,7 @@ import org.michaelbel.movies.analytics.event.ChangeDynamicColorsEvent
import org.michaelbel.movies.analytics.event.ChangeRtlEnabledEvent
import org.michaelbel.movies.analytics.event.SelectThemeEvent
import org.michaelbel.movies.common.config.RemoteParams
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.common.googleapi.GoogleApi
import org.michaelbel.movies.common.theme.AppTheme
import org.michaelbel.movies.common.version.AppVersionData
@@ -24,7 +22,7 @@ import javax.inject.Singleton
@Singleton
internal class SettingsInteractorImpl @Inject constructor(
- @Dispatcher(MoviesDispatchers.Main) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val settingsRepository: SettingsRepository,
private val firebaseRemoteConfig: FirebaseRemoteConfig,
googleApi: GoogleApi,
@@ -47,23 +45,23 @@ internal class SettingsInteractorImpl @Inject constructor(
override val appVersionData: Flow = settingsRepository.appVersionData
- override suspend fun selectTheme(theme: AppTheme) = withContext(dispatcher) {
+ override suspend fun selectTheme(theme: AppTheme) = withContext(dispatchers.main) {
settingsRepository.selectTheme(theme)
analytics.logEvent(SelectThemeEvent(theme.toString()))
}
- override suspend fun setDynamicColors(value: Boolean) = withContext(dispatcher) {
+ override suspend fun setDynamicColors(value: Boolean) = withContext(dispatchers.main) {
settingsRepository.setDynamicColors(value)
analytics.logEvent(ChangeDynamicColorsEvent(value))
}
- override suspend fun setRtlEnabled(value: Boolean) = withContext(dispatcher) {
+ override suspend fun setRtlEnabled(value: Boolean) = withContext(dispatchers.main) {
settingsRepository.setRtlEnabled(value)
analytics.logEvent(ChangeRtlEnabledEvent(value))
}
override suspend fun fetchRemoteConfig() {
- withContext(dispatcher) {
+ withContext(dispatchers.main) {
firebaseRemoteConfig
.fetchAndActivate()
.addOnFailureListener(Timber::e)
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/preferences/constants/Constants.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/preferences/constants/Constants.kt
index 9da94aa91..705cf49a4 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/preferences/constants/Constants.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/preferences/constants/Constants.kt
@@ -3,6 +3,7 @@ package org.michaelbel.movies.domain.preferences.constants
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
internal const val USER_PREFERENCES_NAME = "user_preferences"
@@ -12,4 +13,5 @@ internal val PREFERENCE_DYNAMIC_COLORS_KEY: Preferences.Key = booleanPr
internal val PREFERENCE_RTL_ENABLED_KEY: Preferences.Key = booleanPreferencesKey("rtl_enabled")
internal val PREFERENCE_NETWORK_REQUEST_DELAY_KEY: Preferences.Key = intPreferencesKey("network_request_delay")
internal val PREFERENCE_SESSION_ID_KEY: Preferences.Key = stringPreferencesKey("session_id")
-internal val PREFERENCE_ACCOUNT_ID_KEY: Preferences.Key = intPreferencesKey("account_id")
\ No newline at end of file
+internal val PREFERENCE_ACCOUNT_ID_KEY: Preferences.Key = intPreferencesKey("account_id")
+internal val PREFERENCE_ACCOUNT_EXPIRE_TIME_KEY: Preferences.Key = longPreferencesKey("account_expire_time")
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/repository/account/impl/AccountRepositoryImpl.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/repository/account/impl/AccountRepositoryImpl.kt
index ebcefa75d..114c52bfa 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/repository/account/impl/AccountRepositoryImpl.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/repository/account/impl/AccountRepositoryImpl.kt
@@ -3,6 +3,8 @@ package org.michaelbel.movies.domain.repository.account.impl
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
+import javax.inject.Inject
+import javax.inject.Singleton
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
@@ -11,14 +13,13 @@ import org.michaelbel.movies.domain.data.dao.AccountDao
import org.michaelbel.movies.domain.data.entity.AccountDb
import org.michaelbel.movies.domain.exceptions.AccountDetailsException
import org.michaelbel.movies.domain.ktx.mapToAccountDb
+import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_ACCOUNT_EXPIRE_TIME_KEY
import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_ACCOUNT_ID_KEY
import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_SESSION_ID_KEY
import org.michaelbel.movies.domain.repository.account.AccountRepository
import org.michaelbel.movies.domain.service.account.AccountService
import org.michaelbel.movies.entities.tmdbApiKey
import org.michaelbel.movies.network.model.Account
-import javax.inject.Inject
-import javax.inject.Singleton
@Singleton
internal class AccountRepositoryImpl @Inject constructor(
@@ -40,6 +41,7 @@ internal class AccountRepositoryImpl @Inject constructor(
)
dataStore.edit { preferences ->
preferences[PREFERENCE_ACCOUNT_ID_KEY] = account.id
+ preferences[PREFERENCE_ACCOUNT_EXPIRE_TIME_KEY] = System.currentTimeMillis()
}
accountDao.insert(account.mapToAccountDb)
} catch (e: Exception) {
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/usecase/DelayUseCase.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/usecase/DelayUseCase.kt
index d2d6e1094..a09b0e033 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/usecase/DelayUseCase.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/usecase/DelayUseCase.kt
@@ -3,20 +3,18 @@ package org.michaelbel.movies.domain.usecase
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.common.usecase.UseCase
import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_NETWORK_REQUEST_DELAY_KEY
import javax.inject.Inject
class DelayUseCase @Inject constructor(
- @Dispatcher(MoviesDispatchers.IO) private val dispatcher: CoroutineDispatcher,
+ dispatchers: MoviesDispatchers,
private val dataStore: DataStore
-): UseCase(dispatcher) {
+): UseCase(dispatchers.io) {
val networkRequestDelay: Flow = dataStore.data.map { preferences ->
preferences[PREFERENCE_NETWORK_REQUEST_DELAY_KEY] ?: 0
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/AccountUpdateWorker.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/AccountUpdateWorker.kt
new file mode 100644
index 000000000..8ecc86a07
--- /dev/null
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/AccountUpdateWorker.kt
@@ -0,0 +1,48 @@
+package org.michaelbel.movies.domain.workers
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.hilt.work.HiltWorker
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.flow.first
+import org.michaelbel.movies.common.ktx.isTimePasses
+import org.michaelbel.movies.domain.interactor.account.AccountInteractor
+import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_ACCOUNT_EXPIRE_TIME_KEY
+import org.michaelbel.movies.domain.preferences.constants.PREFERENCE_ACCOUNT_ID_KEY
+import org.michaelbel.movies.entities.isTmdbApiKeyEmpty
+
+@HiltWorker
+class AccountUpdateWorker @AssistedInject constructor(
+ @Assisted context: Context,
+ @Assisted workerParams: WorkerParameters,
+ private val dataStore: DataStore,
+ private val accountInteractor: AccountInteractor
+): CoroutineWorker(context, workerParams) {
+
+ override suspend fun doWork(): Result {
+ return try {
+ val accountId: Int? = dataStore.data.first()[PREFERENCE_ACCOUNT_ID_KEY]
+ if (isTmdbApiKeyEmpty || accountId == null) {
+ return Result.success()
+ }
+
+ val expireTime: Long = dataStore.data.first()[PREFERENCE_ACCOUNT_EXPIRE_TIME_KEY] ?: 0L
+ val currentTime: Long = System.currentTimeMillis()
+ if (isTimePasses(ONE_DAY_MILLS, expireTime, currentTime)) {
+ accountInteractor.accountDetails()
+ }
+ Result.success()
+ } catch (e: Exception) {
+ Result.failure()
+ }
+ }
+
+ private companion object {
+ private val ONE_DAY_MILLS: Long = TimeUnit.DAYS.toMillis(1)
+ }
+}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/MoviesDatabaseWorker.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/MoviesDatabaseWorker.kt
index 727c4aebe..891576bc8 100644
--- a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/MoviesDatabaseWorker.kt
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/MoviesDatabaseWorker.kt
@@ -6,13 +6,12 @@ import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
-import org.michaelbel.movies.common.coroutines.Dispatcher
-import org.michaelbel.movies.common.coroutines.MoviesDispatchers
+import org.michaelbel.movies.common.dispatchers.MoviesDispatchers
import org.michaelbel.movies.domain.data.dao.MovieDao
+import org.michaelbel.movies.domain.data.dao.ktx.isEmpty
import org.michaelbel.movies.domain.data.entity.MovieDb
import org.michaelbel.movies.domain.ktx.mapToMovieDb
import org.michaelbel.movies.network.model.MovieResponse
@@ -21,15 +20,15 @@ import org.michaelbel.movies.network.model.MovieResponse
class MoviesDatabaseWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
- @Dispatcher(MoviesDispatchers.IO) private val dispatcher: CoroutineDispatcher,
+ private val dispatchers: MoviesDispatchers,
private val movieDao: MovieDao
): CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
- return withContext(dispatcher) {
+ return withContext(dispatchers.io) {
try {
val filename: String? = inputData.getString(KEY_FILENAME)
- if (filename != null) {
+ if (filename != null && movieDao.isEmpty(MovieDb.MOVIES_LOCAL_LIST)) {
applicationContext.assets.open(filename).use { inputStream ->
val format = Json { ignoreUnknownKeys = true }
val moviesJsonData: List = format.decodeFromStream(inputStream)
@@ -40,11 +39,9 @@ class MoviesDatabaseWorker @AssistedInject constructor(
)
}
movieDao.insertAllMovies(moviesDb)
- Result.success()
}
- } else {
- Result.failure()
}
+ Result.success()
} catch (e: Exception) {
Result.failure()
}
diff --git a/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/di/WorkManagerModule.kt b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/di/WorkManagerModule.kt
new file mode 100644
index 000000000..c7b5fb4e3
--- /dev/null
+++ b/core/domain/src/main/kotlin/org/michaelbel/movies/domain/workers/di/WorkManagerModule.kt
@@ -0,0 +1,21 @@
+package org.michaelbel.movies.domain.workers.di
+
+import android.content.Context
+import androidx.work.WorkManager
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal object WorkManagerModule {
+
+ @Provides
+ @Singleton
+ fun provideWorkManager(
+ @ApplicationContext context: Context
+ ): WorkManager = WorkManager.getInstance(context)
+}
\ No newline at end of file
diff --git a/core/entities/build.gradle.kts b/core/entities/build.gradle.kts
index f323c8708..6ba521759 100644
--- a/core/entities/build.gradle.kts
+++ b/core/entities/build.gradle.kts
@@ -1,7 +1,9 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -28,6 +30,15 @@ android {
}
}
+ buildFeatures {
+ buildConfig = true
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts
index 6ff42b9bb..9e87661fb 100644
--- a/core/navigation/build.gradle.kts
+++ b/core/navigation/build.gradle.kts
@@ -1,5 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -18,6 +20,11 @@ android {
}
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
index b05fb442e..4c6e4ede1 100644
--- a/core/network/build.gradle.kts
+++ b/core/network/build.gradle.kts
@@ -1,6 +1,8 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("kotlinx-serialization")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
+ alias(libs.plugins.kotlin.serialization)
id("movies-android-hilt")
}
@@ -20,12 +22,21 @@ android {
}
}
+ buildFeatures {
+ buildConfig = true
+ }
+
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + listOf(
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
)
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
diff --git a/core/network/src/main/kotlin/org/michaelbel/movies/network/okhttp/OkhttpModule.kt b/core/network/src/main/kotlin/org/michaelbel/movies/network/okhttp/OkhttpModule.kt
index 8f0b9e24e..bd8d1fc0a 100644
--- a/core/network/src/main/kotlin/org/michaelbel/movies/network/okhttp/OkhttpModule.kt
+++ b/core/network/src/main/kotlin/org/michaelbel/movies/network/okhttp/OkhttpModule.kt
@@ -1,18 +1,25 @@
package org.michaelbel.movies.network.okhttp
+import android.content.Context
import com.chuckerteam.chucker.api.ChuckerInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
+import okhttp3.Cache
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import org.michaelbel.movies.network.BuildConfig
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
-import okhttp3.OkHttpClient
@Module
@InstallIn(SingletonComponent::class)
internal object OkhttpModule {
+ private const val HTTP_CACHE_SIZE_BYTES = 1024 * 1024 * 50L
+
/**
* Суммарное время на выполнение запроса (0 - нет ограничений).
*/
@@ -33,17 +40,40 @@ internal object OkhttpModule {
*/
private const val WRITE_TIMEOUT_SECONDS = 10L
+ @Provides
+ @Singleton
+ fun httpCache(
+ @ApplicationContext context: Context
+ ): Cache {
+ return Cache(context.cacheDir, HTTP_CACHE_SIZE_BYTES)
+ }
+
+ @Provides
+ @Singleton
+ fun provideLoggingInterceptor(): HttpLoggingInterceptor {
+ return HttpLoggingInterceptor().apply {
+ level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else level
+ }
+ }
+
@Provides
@Singleton
fun provideOkHttp(
- chuckerInterceptor: ChuckerInterceptor
+ chuckerInterceptor: ChuckerInterceptor,
+ httpLoggingInterceptor: HttpLoggingInterceptor,
+ cache: Cache
): OkHttpClient {
val builder = OkHttpClient.Builder().apply {
addInterceptor(chuckerInterceptor)
+ addInterceptor(httpLoggingInterceptor)
callTimeout(CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
connectTimeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)
writeTimeout(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ retryOnConnectionFailure(true)
+ followRedirects(true)
+ followSslRedirects(true)
+ cache(cache)
}
return builder.build()
}
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index cf89ac623..f81c8e75c 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -27,6 +28,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -47,4 +53,6 @@ dependencies {
api(libs.bundles.accompanist)
api(libs.bundles.compose)
api(libs.androidx.paging.compose)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/core/ui/src/main/kotlin/org/michaelbel/movies/ui/compose/AccountAvatar.kt b/core/ui/src/main/kotlin/org/michaelbel/movies/ui/compose/AccountAvatar.kt
index 66e8ab98a..ce963f38e 100644
--- a/core/ui/src/main/kotlin/org/michaelbel/movies/ui/compose/AccountAvatar.kt
+++ b/core/ui/src/main/kotlin/org/michaelbel/movies/ui/compose/AccountAvatar.kt
@@ -26,9 +26,9 @@ import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
fun AccountAvatar(
- modifier: Modifier = Modifier,
account: AccountDb,
- fontSize: TextUnit
+ fontSize: TextUnit,
+ modifier: Modifier = Modifier
) {
if (account.avatarUrl.isNotEmpty()) {
AsyncImage(
@@ -68,10 +68,9 @@ private fun AccountAvatarPreview(
) {
MoviesTheme {
AccountAvatar(
- modifier = Modifier
- .size(32.dp),
account = account,
- fontSize = account.lettersTextFontSizeSmall
+ fontSize = account.lettersTextFontSizeSmall,
+ modifier = Modifier.size(32.dp),
)
}
}
\ No newline at end of file
diff --git a/feature/account-impl/build.gradle.kts b/feature/account-impl/build.gradle.kts
index 98a476107..316ec71b9 100644
--- a/feature/account-impl/build.gradle.kts
+++ b/feature/account-impl/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -10,6 +11,7 @@ android {
defaultConfig {
minSdk = libs.versions.min.sdk.get().toInt()
compileSdk = libs.versions.compile.sdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -37,6 +39,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -52,4 +59,13 @@ dependencies {
implementation(project(":core:common"))
implementation(project(":core:domain"))
implementation(project(":core:network"))
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit.ktx)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.benchmark.junit)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/AccountViewModel.kt b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/AccountViewModel.kt
similarity index 75%
rename from feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/AccountViewModel.kt
rename to feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/AccountViewModel.kt
index c50582226..1488b427a 100644
--- a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/AccountViewModel.kt
+++ b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/AccountViewModel.kt
@@ -1,11 +1,11 @@
-package org.michaelbel.movies.auth
+package org.michaelbel.movies.account
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.michaelbel.movies.common.viewmodel.BaseViewModel
@@ -20,8 +20,7 @@ class AccountViewModel @Inject constructor(
accountInteractor: AccountInteractor,
): BaseViewModel() {
- private val _loading: MutableStateFlow = MutableStateFlow(false)
- val loading: StateFlow = _loading.asStateFlow()
+ var loading: Boolean by mutableStateOf(false)
val account: StateFlow = accountInteractor.account
.stateIn(
@@ -31,14 +30,13 @@ class AccountViewModel @Inject constructor(
)
override fun handleError(throwable: Throwable) {
- _loading.value = false
+ loading = false
super.handleError(throwable)
}
fun onLogoutClick(onResult: () -> Unit) = launch {
- _loading.value = true
+ loading = true
- delay(5000)
authenticationInteractor.deleteSession()
onResult()
}
diff --git a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountCountryBox.kt b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountCountryBox.kt
similarity index 76%
rename from feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountCountryBox.kt
rename to feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountCountryBox.kt
index 4d14d13ed..02ac0af48 100644
--- a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountCountryBox.kt
+++ b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountCountryBox.kt
@@ -1,4 +1,4 @@
-package org.michaelbel.movies.auth.ui
+package org.michaelbel.movies.account.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@@ -19,9 +19,9 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun AccountCountryBox(
- modifier: Modifier = Modifier,
- country: String
+fun AccountCountryBox(
+ country: String,
+ modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
@@ -31,21 +31,18 @@ internal fun AccountCountryBox(
Icon(
imageVector = MoviesIcons.LocationOn,
contentDescription = null,
- modifier = Modifier
- .size(24.dp),
+ modifier = Modifier.size(24.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
Text(
text = country,
- modifier = Modifier
- .padding(
- start = 4.dp
- ),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
+ modifier = Modifier.padding(start = 4.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.bodySmall
+ style = MaterialTheme.typography.bodySmall.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
}
}
@@ -55,10 +52,10 @@ internal fun AccountCountryBox(
private fun AccountCountryBoxPreview() {
MoviesTheme {
AccountCountryBox(
+ country = "Finland",
modifier = Modifier
.fillMaxWidth()
- .background(MaterialTheme.colorScheme.primaryContainer),
- country = "Finland"
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountScreenContent.kt b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountScreenContent.kt
similarity index 88%
rename from feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountScreenContent.kt
rename to feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountScreenContent.kt
index cedf60ee9..320125e33 100644
--- a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountScreenContent.kt
+++ b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountScreenContent.kt
@@ -1,4 +1,4 @@
-package org.michaelbel.movies.auth.ui
+package org.michaelbel.movies.account.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@@ -23,8 +23,8 @@ import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.michaelbel.movies.account.AccountViewModel
import org.michaelbel.movies.account_impl.R
-import org.michaelbel.movies.auth.AccountViewModel
import org.michaelbel.movies.domain.data.entity.AccountDb
import org.michaelbel.movies.domain.data.ktx.orEmpty
import org.michaelbel.movies.ui.compose.AccountAvatar
@@ -37,34 +37,31 @@ fun AccountRoute(
viewModel: AccountViewModel = hiltViewModel()
) {
val account: AccountDb? by viewModel.account.collectAsStateWithLifecycle()
- val loading: Boolean by viewModel.loading.collectAsStateWithLifecycle()
AccountScreenContent(
- modifier = modifier,
account = account.orEmpty,
- loading = loading,
+ loading = viewModel.loading,
onBackClick = onBackClick,
onLogoutClick = {
viewModel.onLogoutClick {
onBackClick()
}
- }
+ },
+ modifier = modifier
)
}
@Composable
internal fun AccountScreenContent(
- modifier: Modifier = Modifier,
account: AccountDb,
loading: Boolean,
onBackClick: () -> Unit,
- onLogoutClick: () -> Unit
+ onLogoutClick: () -> Unit,
+ modifier: Modifier = Modifier
) {
ConstraintLayout(
modifier
- .padding(
- horizontal = 16.dp
- )
+ .padding(horizontal = 16.dp)
.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.primaryContainer,
@@ -95,15 +92,15 @@ internal fun AccountScreenContent(
)
AccountAvatar(
+ account = account,
+ fontSize = account.lettersTextFontSizeLarge,
modifier = Modifier
.constrainAs(accountAvatar) {
width = Dimension.value(64.dp)
height = Dimension.value(64.dp)
start.linkTo(parent.start, 16.dp)
top.linkTo(toolbar.bottom)
- },
- account = account,
- fontSize = account.lettersTextFontSizeLarge
+ }
)
Column(
@@ -122,26 +119,29 @@ internal fun AccountScreenContent(
if (account.name.isNotEmpty()) {
Text(
text = account.name,
- color = MaterialTheme.colorScheme.primary,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
if (account.username.isNotEmpty()) {
Text(
text = account.username,
- color = MaterialTheme.colorScheme.secondary,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.secondary
+ )
)
}
}
if (account.country.isNotEmpty()) {
AccountCountryBox(
+ country = account.country,
modifier = Modifier
.constrainAs(countryBox) {
width = Dimension.fillToConstraints
@@ -149,8 +149,7 @@ internal fun AccountScreenContent(
start.linkTo(parent.start, 16.dp)
top.linkTo(accountAvatar.bottom, 8.dp)
end.linkTo(parent.end, 16.dp)
- },
- country = account.country
+ }
)
}
diff --git a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountToolbar.kt b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountToolbar.kt
similarity index 89%
rename from feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountToolbar.kt
rename to feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountToolbar.kt
index 9abb1b249..5662cb571 100644
--- a/feature/account-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AccountToolbar.kt
+++ b/feature/account-impl/src/main/kotlin/org/michaelbel/movies/account/ui/AccountToolbar.kt
@@ -1,4 +1,4 @@
-package org.michaelbel.movies.auth.ui
+package org.michaelbel.movies.account.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -19,7 +19,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun AccountToolbar(
+fun AccountToolbar(
modifier: Modifier = Modifier,
onNavigationIconClick: () -> Unit
) {
@@ -27,10 +27,11 @@ internal fun AccountToolbar(
title = {
Text(
text = stringResource(R.string.account_title),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
},
modifier = modifier,
diff --git a/feature/account/build.gradle.kts b/feature/account/build.gradle.kts
index cea305925..8f07a9766 100644
--- a/feature/account/build.gradle.kts
+++ b/feature/account/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -33,6 +34,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -44,4 +50,6 @@ android {
dependencies {
implementation(project(":feature:account-impl"))
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/account/src/main/kotlin/org/michaelbel/movies/auth/AccountNavigation.kt b/feature/account/src/main/kotlin/org/michaelbel/movies/auth/AccountNavigation.kt
index cb86adb78..364c8ce89 100644
--- a/feature/account/src/main/kotlin/org/michaelbel/movies/auth/AccountNavigation.kt
+++ b/feature/account/src/main/kotlin/org/michaelbel/movies/auth/AccountNavigation.kt
@@ -3,7 +3,7 @@ package org.michaelbel.movies.auth
import androidx.compose.ui.window.DialogProperties
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.dialog
-import org.michaelbel.movies.auth.ui.AccountRoute
+import org.michaelbel.movies.account.ui.AccountRoute
import org.michaelbel.movies.navigation.MoviesNavigationDestination
object AccountDestination: MoviesNavigationDestination {
diff --git a/feature/auth-impl/build.gradle.kts b/feature/auth-impl/build.gradle.kts
index cfbe35aa3..e2273d0cc 100644
--- a/feature/auth-impl/build.gradle.kts
+++ b/feature/auth-impl/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -10,6 +11,7 @@ android {
defaultConfig {
minSdk = libs.versions.min.sdk.get().toInt()
compileSdk = libs.versions.compile.sdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -37,6 +39,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -52,4 +59,13 @@ dependencies {
implementation(project(":core:common"))
implementation(project(":core:domain"))
implementation(project(":core:network"))
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit.ktx)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.benchmark.junit)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/AuthViewModel.kt b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/AuthViewModel.kt
index 4e6ecb2e7..ff05731c2 100644
--- a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/AuthViewModel.kt
+++ b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/AuthViewModel.kt
@@ -1,9 +1,9 @@
package org.michaelbel.movies.auth
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.michaelbel.movies.common.viewmodel.BaseViewModel
import org.michaelbel.movies.domain.exceptions.AccountDetailsException
@@ -21,35 +21,32 @@ class AuthViewModel @Inject constructor(
private val accountInteractor: AccountInteractor
): BaseViewModel() {
- private val _error: MutableStateFlow = MutableStateFlow(null)
- val error: StateFlow = _error.asStateFlow()
-
- private val _loading: MutableStateFlow = MutableStateFlow(false)
- val loading: StateFlow = _loading.asStateFlow()
+ var loading: Boolean by mutableStateOf(false)
+ var error: Throwable? by mutableStateOf(null)
override fun handleError(throwable: Throwable) {
- _loading.value = false
+ loading = false
when (throwable) {
is CreateRequestTokenException -> {
- _error.value = CreateRequestTokenException
+ error = CreateRequestTokenException
}
is CreateSessionWithLoginException -> {
- _error.value = CreateSessionWithLoginException
+ error = CreateSessionWithLoginException
}
is CreateSessionException -> {
- _error.value = CreateSessionException
+ error = CreateSessionException
}
is AccountDetailsException -> {
- _error.value = AccountDetailsException
+ error = AccountDetailsException
}
else -> super.handleError(throwable)
}
}
fun onSignInClick(username: String, password: String, onResult: () -> Unit) = launch {
- _error.value = null
- _loading.value = true
+ error = null
+ loading = true
val token: Token = authenticationInteractor.createRequestToken()
val sessionToken: Token = authenticationInteractor.createSessionWithLogin(
diff --git a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthErrorBox.kt b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthErrorBox.kt
index 00814fd89..1616d291f 100644
--- a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthErrorBox.kt
+++ b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthErrorBox.kt
@@ -22,9 +22,9 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun AuthErrorBox(
- modifier: Modifier = Modifier,
- error: Throwable?
+fun AuthErrorBox(
+ error: Throwable?,
+ modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
@@ -34,17 +34,13 @@ internal fun AuthErrorBox(
Icon(
imageVector = MoviesIcons.Error,
contentDescription = null,
- modifier = Modifier
- .size(24.dp),
+ modifier = Modifier.size(24.dp),
tint = MaterialTheme.colorScheme.error
)
Text(
text = error.text,
- modifier = Modifier
- .padding(
- start = 4.dp
- ),
+ modifier = Modifier.padding(start = 4.dp),
color = MaterialTheme.colorScheme.error,
fontSize = 13.sp,
textAlign = TextAlign.Start,
@@ -60,9 +56,9 @@ private fun AuthErrorBoxPreview(
) {
MoviesTheme {
AuthErrorBox(
+ error = error,
modifier = Modifier
- .background(MaterialTheme.colorScheme.primaryContainer),
- error = error
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthLinksBox.kt b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthLinksBox.kt
index 7857816f0..12090b7bc 100644
--- a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthLinksBox.kt
+++ b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthLinksBox.kt
@@ -29,7 +29,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun AuthLinksBox(
+fun AuthLinksBox(
modifier: Modifier = Modifier
) {
val toolbarColor: Int = MaterialTheme.colorScheme.primary.toArgb()
@@ -56,22 +56,19 @@ internal fun AuthLinksBox(
Text(
text = stringResource(R.string.auth_terms_of_use),
modifier = Modifier
- .padding(
- vertical = 16.dp
- )
+ .padding(vertical = 16.dp)
.clickableWithoutRipple {
openUrl(resultContract, toolbarColor, TMDB_TERMS_OF_USE)
},
- color = MaterialTheme.colorScheme.primary,
maxLines = 1,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
Box(
modifier = Modifier
- .padding(
- horizontal = 8.dp
- )
+ .padding(horizontal = 8.dp)
.size(3.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
@@ -80,15 +77,14 @@ internal fun AuthLinksBox(
Text(
text = stringResource(R.string.auth_privacy_policy),
modifier = Modifier
- .padding(
- vertical = 16.dp
- )
+ .padding(vertical = 16.dp)
.clickableWithoutRipple {
openUrl(resultContract, toolbarColor, TMDB_PRIVACY_POLICY)
},
- color = MaterialTheme.colorScheme.primary,
maxLines = 1,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
}
diff --git a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthScreenContent.kt b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthScreenContent.kt
index ba9dac80a..fb190124d 100644
--- a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthScreenContent.kt
+++ b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthScreenContent.kt
@@ -41,7 +41,6 @@ import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.michaelbel.movies.auth.AuthViewModel
import org.michaelbel.movies.auth_impl.R
import org.michaelbel.movies.ui.icons.MoviesIcons
@@ -52,29 +51,26 @@ fun AuthRoute(
modifier: Modifier = Modifier,
viewModel: AuthViewModel = hiltViewModel()
) {
- val error: Throwable? by viewModel.error.collectAsStateWithLifecycle()
- val loading: Boolean by viewModel.loading.collectAsStateWithLifecycle()
-
AuthScreenContent(
- modifier = modifier,
- error = error,
- loading = loading,
+ error = viewModel.error,
+ loading = viewModel.loading,
onBackClick = onBackClick,
onSignInClick = { username, password ->
viewModel.onSignInClick(username, password) {
onBackClick()
}
- }
+ },
+ modifier = modifier
)
}
@Composable
internal fun AuthScreenContent(
- modifier: Modifier = Modifier,
error: Throwable?,
loading: Boolean,
onBackClick: () -> Unit,
- onSignInClick: (String, String) -> Unit
+ onSignInClick: (String, String) -> Unit,
+ modifier: Modifier = Modifier
) {
val focusManager: FocusManager = LocalFocusManager.current
val focusRequester: FocusRequester = remember { FocusRequester() }
@@ -86,9 +82,7 @@ internal fun AuthScreenContent(
ConstraintLayout(
modifier
- .padding(
- horizontal = 16.dp
- )
+ .padding(horizontal = 16.dp)
.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.primaryContainer,
diff --git a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthToolbar.kt b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthToolbar.kt
index e99024412..cb20736a5 100644
--- a/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthToolbar.kt
+++ b/feature/auth-impl/src/main/kotlin/org/michaelbel/movies/auth/ui/AuthToolbar.kt
@@ -19,7 +19,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun AuthToolbar(
+fun AuthToolbar(
modifier: Modifier = Modifier,
onNavigationIconClick: () -> Unit
) {
@@ -27,10 +27,11 @@ internal fun AuthToolbar(
title = {
Text(
text = stringResource(R.string.auth_title),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
},
modifier = modifier,
diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts
index 6f75a81b5..428bbf2c3 100644
--- a/feature/auth/build.gradle.kts
+++ b/feature/auth/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -33,6 +34,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -44,4 +50,6 @@ android {
dependencies {
implementation(project(":feature:auth-impl"))
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/details-impl/build.gradle.kts b/feature/details-impl/build.gradle.kts
index 69355e997..280ed4544 100644
--- a/feature/details-impl/build.gradle.kts
+++ b/feature/details-impl/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -10,6 +11,7 @@ android {
defaultConfig {
minSdk = libs.versions.min.sdk.get().toInt()
compileSdk = libs.versions.compile.sdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -36,6 +38,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -52,4 +59,13 @@ dependencies {
implementation(project(":core:common"))
implementation(project(":core:domain"))
implementation(project(":core:network"))
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit.ktx)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.benchmark.junit)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsAdvert.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsAdvert.kt
index 08812dd5b..f411b74ba 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsAdvert.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsAdvert.kt
@@ -1,22 +1,6 @@
package org.michaelbel.movies.details.ui
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import com.google.android.gms.ads.AdRequest
-import com.google.android.gms.ads.AdSize
-import com.google.android.gms.ads.AdView
-import org.michaelbel.movies.ads.R
-import org.michaelbel.movies.ui.preview.DevicePreviews
-import org.michaelbel.movies.ui.theme.MoviesTheme
-
-@Composable
+/*@Composable
internal fun DetailsAdvert(
modifier: Modifier = Modifier,
adRequest: AdRequest
@@ -50,4 +34,4 @@ private fun DetailsAdvertPreview() {
adRequest = AdRequest.Builder().build()
)
}
-}
\ No newline at end of file
+}*/
\ No newline at end of file
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsContent.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsContent.kt
index bcc7fd8c9..affaed5b1 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsContent.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsContent.kt
@@ -38,7 +38,7 @@ import org.michaelbel.movies.ui.preview.provider.MovieDbPreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun DetailsContent(
+fun DetailsContent(
movie: MovieDb,
modifier: Modifier = Modifier,
placeholder: Boolean = false
@@ -113,8 +113,9 @@ internal fun DetailsContent(
) {
Text(
text = stringResource(R.string.details_no_image),
- color = MaterialTheme.colorScheme.secondary,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.secondary
+ )
)
}
@@ -147,10 +148,11 @@ internal fun DetailsContent(
Text(
text = movie.title,
modifier = titleModifier,
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 3,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
val overviewModifier: Modifier = if (placeholder) {
@@ -182,10 +184,11 @@ internal fun DetailsContent(
Text(
text = movie.overview,
modifier = overviewModifier,
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 10,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
}
}
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsFailure.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsFailure.kt
index fecde536c..a5ed74bce 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsFailure.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsFailure.kt
@@ -18,7 +18,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun DetailsFailure(
+fun DetailsFailure(
modifier: Modifier = Modifier
) {
ConstraintLayout(
@@ -51,9 +51,10 @@ internal fun DetailsFailure(
top.linkTo(image.bottom, 8.dp)
end.linkTo(parent.end, 16.dp)
},
- color = MaterialTheme.colorScheme.onBackground,
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
}
}
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsLoading.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsLoading.kt
index 4963df8a6..dbb2fed67 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsLoading.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsLoading.kt
@@ -10,7 +10,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun DetailsLoading(
+fun DetailsLoading(
modifier: Modifier = Modifier
) {
DetailsContent(
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsScreenContent.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsScreenContent.kt
index a39661287..fe17ed015 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsScreenContent.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsScreenContent.kt
@@ -33,20 +33,20 @@ fun DetailsRoute(
DetailsScreenContent(
onBackClick = onBackClick,
- modifier = modifier,
detailsState = detailsState,
networkStatus = networkStatus,
- onRetry = viewModel::retry
+ onRetry = viewModel::retry,
+ modifier = modifier
)
}
@Composable
private fun DetailsScreenContent(
onBackClick: () -> Unit,
- modifier: Modifier = Modifier,
detailsState: ScreenState,
networkStatus: NetworkStatus,
- onRetry: () -> Unit
+ onRetry: () -> Unit,
+ modifier: Modifier = Modifier
) {
if (networkStatus.isAvailable && detailsState.isFailure && detailsState.throwable is UnknownHostException) {
onRetry()
@@ -56,11 +56,10 @@ private fun DetailsScreenContent(
modifier = modifier,
topBar = {
DetailsToolbar(
- modifier = Modifier
- .statusBarsPadding(),
- onNavigationIconClick = onBackClick,
movieTitle = detailsState.toolbarTitle,
- movieUrl = detailsState.movieUrl
+ movieUrl = detailsState.movieUrl,
+ onNavigationIconClick = onBackClick,
+ modifier = Modifier.statusBarsPadding()
)
},
containerColor = MaterialTheme.colorScheme.primaryContainer
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/ShareButton.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsShareButton.kt
similarity index 91%
rename from feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/ShareButton.kt
rename to feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsShareButton.kt
index 190daa4c7..fe28093d6 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/ShareButton.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsShareButton.kt
@@ -8,6 +8,7 @@ import androidx.compose.foundation.Image
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -20,8 +21,9 @@ import org.michaelbel.movies.ui.preview.provider.MovieDbPreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun ShareButton(
- movieUrl: String
+fun DetailsShareButton(
+ movieUrl: String,
+ modifier: Modifier = Modifier
) {
val context: Context = LocalContext.current
val resultContract = rememberLauncherForActivityResult(
@@ -39,7 +41,8 @@ internal fun ShareButton(
Intent.createChooser(intent, context.getString(R.string.details_share_via))
)
}
- }
+ },
+ modifier = modifier
) {
Image(
imageVector = MoviesIcons.Share,
@@ -55,7 +58,7 @@ private fun ShareButtonPreview(
@PreviewParameter(MovieDbPreviewParameterProvider::class) movie: MovieDb
) {
MoviesTheme {
- ShareButton(
+ DetailsShareButton(
movieUrl = movie.url
)
}
diff --git a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsToolbar.kt b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsToolbar.kt
index 791a16adb..aa798e787 100644
--- a/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsToolbar.kt
+++ b/feature/details-impl/src/main/kotlin/org/michaelbel/movies/details/ui/DetailsToolbar.kt
@@ -21,20 +21,21 @@ import org.michaelbel.movies.ui.preview.provider.TitlePreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun DetailsToolbar(
- modifier: Modifier = Modifier,
- onNavigationIconClick: () -> Unit,
+fun DetailsToolbar(
movieTitle: String,
- movieUrl: String?
+ movieUrl: String?,
+ onNavigationIconClick: () -> Unit,
+ modifier: Modifier = Modifier
) {
TopAppBar(
title = {
Text(
text = movieTitle,
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
},
modifier = modifier,
@@ -44,7 +45,7 @@ internal fun DetailsToolbar(
enter = fadeIn()
) {
if (movieUrl != null) {
- ShareButton(
+ DetailsShareButton(
movieUrl = movieUrl
)
}
@@ -76,11 +77,10 @@ private fun DetailsToolbarPreview(
) {
MoviesTheme {
DetailsToolbar(
- modifier = Modifier
- .statusBarsPadding(),
- onNavigationIconClick = {},
movieTitle = title,
- movieUrl = null
+ movieUrl = null,
+ onNavigationIconClick = {},
+ modifier = Modifier.statusBarsPadding()
)
}
}
\ No newline at end of file
diff --git a/feature/details/build.gradle.kts b/feature/details/build.gradle.kts
index 4ba87df58..f51e4fdeb 100644
--- a/feature/details/build.gradle.kts
+++ b/feature/details/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -27,6 +28,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -38,4 +44,6 @@ android {
dependencies {
implementation(project(":feature:details-impl"))
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/feed-impl/build.gradle.kts b/feature/feed-impl/build.gradle.kts
index 10562cf19..f6b1d3328 100644
--- a/feature/feed-impl/build.gradle.kts
+++ b/feature/feed-impl/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -10,6 +11,7 @@ android {
defaultConfig {
minSdk = libs.versions.min.sdk.get().toInt()
compileSdk = libs.versions.compile.sdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -37,6 +39,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -52,4 +59,13 @@ dependencies {
implementation(project(":core:common"))
implementation(project(":core:domain"))
implementation(project(":core:network"))
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit.ktx)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.benchmark.junit)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedApiKeyBox.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedApiKeyBox.kt
index 3f4a97918..d0ae1d58c 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedApiKeyBox.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedApiKeyBox.kt
@@ -17,7 +17,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedApiKeyBox(
+fun FeedApiKeyBox(
modifier: Modifier = Modifier
) {
Column(
@@ -27,8 +27,9 @@ internal fun FeedApiKeyBox(
) {
Text(
text = stringResource(R.string.feed_error_api_key_null),
- color = MaterialTheme.colorScheme.onBackground,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
}
}
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedContent.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedContent.kt
index a15957c68..de4eff286 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedContent.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedContent.kt
@@ -22,21 +22,22 @@ import org.michaelbel.movies.ui.ktx.isPagingFailure
import org.michaelbel.movies.ui.ktx.isPagingLoading
@Composable
-internal fun FeedContent(
- modifier: Modifier = Modifier,
- paddingValues: PaddingValues = PaddingValues(),
+fun FeedContent(
listState: LazyListState,
pagingItems: LazyPagingItems,
- onMovieClick: (Int) -> Unit
+ onMovieClick: (Int) -> Unit,
+ contentPadding: PaddingValues,
+ modifier: Modifier = Modifier,
) {
LazyColumn(
modifier = modifier,
state = listState,
- contentPadding = paddingValues
+ contentPadding = contentPadding
) {
items(pagingItems) { movieItem ->
movieItem?.let { movie ->
FeedMovieBox(
+ movie = movie,
modifier = Modifier
.fillMaxWidth()
.padding(
@@ -47,8 +48,7 @@ internal fun FeedContent(
.background(MaterialTheme.colorScheme.inversePrimary)
.clickable {
onMovieClick(movie.movieId)
- },
- movie = movie
+ }
)
}
}
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedErrorBox.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedErrorBox.kt
index e4c78376e..03db1ae03 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedErrorBox.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedErrorBox.kt
@@ -17,7 +17,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedErrorBox(
+fun FeedErrorBox(
modifier: Modifier = Modifier
) {
Column(
@@ -27,8 +27,9 @@ internal fun FeedErrorBox(
) {
Text(
text = stringResource(R.string.feed_retry),
- color = MaterialTheme.colorScheme.onBackground,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
}
}
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedFailure.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedFailure.kt
index 4e0c878d6..56918c5e9 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedFailure.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedFailure.kt
@@ -20,7 +20,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedFailure(
+fun FeedFailure(
modifier: Modifier = Modifier,
onCheckConnectivityClick: () -> Unit
) {
@@ -54,9 +54,10 @@ internal fun FeedFailure(
top.linkTo(image.bottom, 8.dp)
end.linkTo(parent.end, 16.dp)
},
- color = MaterialTheme.colorScheme.onBackground,
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
if (Build.VERSION.SDK_INT >= 29) {
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoading.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoading.kt
index aea8aa8ac..352bca9c5 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoading.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoading.kt
@@ -19,7 +19,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedLoading(
+fun FeedLoading(
modifier: Modifier = Modifier,
paddingValues: PaddingValues = PaddingValues(),
) {
@@ -29,6 +29,7 @@ internal fun FeedLoading(
) {
items(MovieResponse.DEFAULT_PAGE_SIZE) {
FeedMovieBox(
+ movie = MovieDb.Empty,
modifier = Modifier
.fillMaxWidth()
.padding(
@@ -40,8 +41,7 @@ internal fun FeedLoading(
color = MaterialTheme.colorScheme.inversePrimary,
shape = MaterialTheme.shapes.small,
highlight = PlaceholderHighlight.fade()
- ),
- movie = MovieDb.Empty
+ )
)
}
}
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoadingBox.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoadingBox.kt
index ec7d02d85..fa5121ef5 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoadingBox.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedLoadingBox.kt
@@ -15,7 +15,7 @@ import org.michaelbel.movies.ui.preview.DevicePreviews
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedLoadingBox(
+fun FeedLoadingBox(
modifier: Modifier = Modifier
) {
Column(
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedMovieBox.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedMovieBox.kt
index 1b96a36be..07f382752 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedMovieBox.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedMovieBox.kt
@@ -32,9 +32,9 @@ import org.michaelbel.movies.ui.preview.provider.MoviePreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedMovieBox(
- modifier: Modifier = Modifier,
- movie: MovieDb
+fun FeedMovieBox(
+ movie: MovieDb,
+ modifier: Modifier = Modifier
) {
var isNoImageVisible: Boolean by remember { mutableStateOf(false) }
@@ -78,8 +78,9 @@ internal fun FeedMovieBox(
) {
Text(
text = stringResource(R.string.feed_no_image),
- color = MaterialTheme.colorScheme.secondary,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.secondary
+ )
)
}
@@ -94,10 +95,11 @@ internal fun FeedMovieBox(
end.linkTo(parent.end, 16.dp)
bottom.linkTo(parent.bottom, 16.dp)
},
- color = MaterialTheme.colorScheme.onPrimaryContainer,
maxLines = 10,
overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
}
}
@@ -109,6 +111,7 @@ private fun MovieBoxPreview(
) {
MoviesTheme {
FeedMovieBox(
+ movie = movie,
modifier = Modifier
.fillMaxWidth()
.padding(
@@ -116,8 +119,7 @@ private fun MovieBoxPreview(
vertical = 4.dp
)
.clip(MaterialTheme.shapes.small)
- .background(MaterialTheme.colorScheme.inversePrimary),
- movie = movie
+ .background(MaterialTheme.colorScheme.inversePrimary)
)
}
}
\ No newline at end of file
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedScreenContent.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedScreenContent.kt
index e25ebf93f..09df85ecb 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedScreenContent.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedScreenContent.kt
@@ -59,7 +59,6 @@ fun FeedRoute(
val networkStatus: NetworkStatus by viewModel.networkStatus.collectAsStateWithLifecycle()
FeedScreenContent(
- modifier = modifier,
pagingItems = pagingItems,
account = account.orEmpty,
networkStatus = networkStatus,
@@ -67,13 +66,13 @@ fun FeedRoute(
onNavigateToAuth = onNavigateToAuth,
onNavigateToAccount = onNavigateToAccount,
onNavigateToSettings = onNavigateToSettings,
- onNavigateToDetails = onNavigateToDetails
+ onNavigateToDetails = onNavigateToDetails,
+ modifier = modifier
)
}
@Composable
private fun FeedScreenContent(
- modifier: Modifier = Modifier,
pagingItems: LazyPagingItems,
account: AccountDb,
networkStatus: NetworkStatus,
@@ -81,7 +80,8 @@ private fun FeedScreenContent(
onNavigateToAuth: () -> Unit,
onNavigateToAccount: () -> Unit,
onNavigateToSettings: () -> Unit,
- onNavigateToDetails: (Int) -> Unit
+ onNavigateToDetails: (Int) -> Unit,
+ modifier: Modifier = Modifier
) {
val context: Context = LocalContext.current
val scope: CoroutineScope = rememberCoroutineScope()
@@ -167,12 +167,11 @@ private fun FeedScreenContent(
}
else -> {
FeedContent(
- modifier = Modifier
- .fillMaxSize(),
- paddingValues = paddingValues,
listState = listState,
pagingItems = pagingItems,
- onMovieClick = onNavigateToDetails
+ onMovieClick = onNavigateToDetails,
+ contentPadding = paddingValues,
+ modifier = Modifier.fillMaxSize()
)
}
}
diff --git a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedToolbar.kt b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedToolbar.kt
index dbb2e014d..99e344503 100644
--- a/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedToolbar.kt
+++ b/feature/feed-impl/src/main/kotlin/org/michaelbel/movies/feed/ui/FeedToolbar.kt
@@ -26,22 +26,23 @@ import org.michaelbel.movies.ui.preview.provider.BooleanPreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun FeedToolbar(
- modifier: Modifier = Modifier,
+fun FeedToolbar(
account: AccountDb,
isSettingsIconVisible: Boolean,
onAuthIconClick: () -> Unit,
onAccountIconClick: () -> Unit,
- onSettingsIconClick: () -> Unit
+ onSettingsIconClick: () -> Unit,
+ modifier: Modifier = Modifier
) {
TopAppBar(
title = {
Text(
text = stringResource(R.string.feed_title),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
},
modifier = modifier,
@@ -69,10 +70,9 @@ internal fun FeedToolbar(
)
} else {
AccountAvatar(
- modifier = Modifier
- .size(32.dp),
account = account,
- fontSize = account.lettersTextFontSizeSmall
+ fontSize = account.lettersTextFontSizeSmall,
+ modifier = Modifier.size(32.dp)
)
}
}
@@ -90,13 +90,12 @@ private fun FeedToolbarPreview(
) {
MoviesTheme {
FeedToolbar(
- modifier = Modifier
- .statusBarsPadding(),
account = AccountDb.Empty,
isSettingsIconVisible = visible,
onAccountIconClick = {},
onAuthIconClick = {},
- onSettingsIconClick = {}
+ onSettingsIconClick = {},
+ modifier = Modifier.statusBarsPadding()
)
}
}
\ No newline at end of file
diff --git a/feature/feed/build.gradle.kts b/feature/feed/build.gradle.kts
index a641d7fdc..2df65842f 100644
--- a/feature/feed/build.gradle.kts
+++ b/feature/feed/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -27,6 +28,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -38,4 +44,6 @@ android {
dependencies {
implementation(project(":feature:feed-impl"))
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/feature/settings-impl/build.gradle.kts b/feature/settings-impl/build.gradle.kts
index e38b593e0..8843c8187 100644
--- a/feature/settings-impl/build.gradle.kts
+++ b/feature/settings-impl/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
id("movies-android-hilt")
}
@@ -10,6 +11,7 @@ android {
defaultConfig {
minSdk = libs.versions.min.sdk.get().toInt()
compileSdk = libs.versions.compile.sdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -30,6 +32,7 @@ android {
}
buildFeatures {
+ buildConfig = true
compose = true
}
@@ -37,6 +40,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -54,7 +62,11 @@ dependencies {
implementation(project(":core:domain"))
testImplementation(libs.junit)
- debugImplementation(libs.androidx.compose.ui.test.manifest)
- androidTestImplementation(libs.androidx.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.test.ext.junit.ktx)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.benchmark.junit)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsDynamicColorsBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBoxTest.kt
similarity index 77%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsDynamicColorsBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBoxTest.kt
index 2bba12e17..f935b9e27 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsDynamicColorsBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBoxTest.kt
@@ -1,23 +1,21 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsDynamicColorsBox
import org.michaelbel.movies.ui.theme.MoviesTheme
-class SettingsDynamicColorsBoxTest {
+internal class SettingsDynamicColorsBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -27,28 +25,29 @@ class SettingsDynamicColorsBoxTest {
composeTestRule.setContent {
MoviesTheme {
SettingsDynamicColorsBox(
+ isDynamicColorsEnabled = IS_DYNAMIC_COLORS_ENABLED,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .clickable {},
- isDynamicColorsEnabled = IS_DYNAMIC_COLORS_ENABLED
+ .clickable {}
)
}
}
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "Text", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
composeTestRule
.onNodeWithTag(testTag = "Switch", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
}
private companion object {
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsLanguageBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBoxTest.kt
similarity index 63%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsLanguageBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBoxTest.kt
index 072763351..b9cdb69cb 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsLanguageBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBoxTest.kt
@@ -1,24 +1,21 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsLanguageBox
-import org.michaelbel.movies.ui.language.model.AppLanguage
+import org.michaelbel.movies.common.localization.model.AppLanguage
import org.michaelbel.movies.ui.theme.MoviesTheme
-class SettingsLanguageBoxTest {
+internal class SettingsLanguageBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -28,31 +25,37 @@ class SettingsLanguageBoxTest {
composeTestRule.setContent {
MoviesTheme {
SettingsLanguageBox(
+ languages = LANGUAGES,
+ currentLanguage = CURRENT_LANGUAGE,
+ onLanguageSelect = {},
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .clickable {},
- currentLanguage = CURRENT_LANGUAGE
)
}
}
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "TitleText", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
composeTestRule
.onNodeWithTag(testTag = "ValueText", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
}
private companion object {
- private val CURRENT_LANGUAGE = AppLanguage.English
+ private val LANGUAGES: List = listOf(
+ AppLanguage.English,
+ AppLanguage.Russian
+ )
+ private val CURRENT_LANGUAGE: AppLanguage = AppLanguage.English
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialogTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialogTest.kt
new file mode 100644
index 000000000..9a53d0161
--- /dev/null
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialogTest.kt
@@ -0,0 +1,64 @@
+package org.michaelbel.movies.settings.ui
+
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import org.junit.Rule
+import org.junit.Test
+import org.michaelbel.movies.common.localization.model.AppLanguage
+import org.michaelbel.movies.ui.theme.MoviesTheme
+
+internal class SettingsLanguageDialogTest {
+
+ @get:Rule
+ val composeTestRule: ComposeContentTestRule = createComposeRule()
+
+ @Test
+ fun testSettingsLanguageDialog() {
+ composeTestRule.setContent {
+ MoviesTheme {
+ SettingLanguageDialog(
+ languages = LANGUAGES,
+ currentLanguage = CURRENT_LANGUAGE,
+ onLanguageSelect = {},
+ onDismissRequest = {}
+ )
+ }
+ }
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConfirmTextButton", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConfirmText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Icon", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Title", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Content", useUnmergedTree = true)
+ .assertIsDisplayed()
+ }
+
+ private companion object {
+ private val LANGUAGES: List = listOf(
+ AppLanguage.English,
+ AppLanguage.Russian
+ )
+ private val CURRENT_LANGUAGE: AppLanguage = AppLanguage.English
+ }
+}
\ No newline at end of file
diff --git a/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBoxTest.kt
new file mode 100644
index 000000000..30cd02a13
--- /dev/null
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBoxTest.kt
@@ -0,0 +1,56 @@
+package org.michaelbel.movies.settings.ui
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import org.junit.Rule
+import org.junit.Test
+import org.michaelbel.movies.ui.theme.MoviesTheme
+
+internal class SettingsNetworkRequestDelayBoxTest {
+
+ @get:Rule
+ val composeTestRule: ComposeContentTestRule = createComposeRule()
+
+ @Test
+ fun testSettingsNetworkRequestDelayBox() {
+ composeTestRule.setContent {
+ MoviesTheme {
+ SettingsNetworkRequestDelayBox(
+ modifier = Modifier
+ .fillMaxWidth(),
+ delay = NETWORK_REQUEST_DELAY,
+ onDelayChangeFinished = {}
+ )
+ }
+ }
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "TitleText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ValueText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Slider", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+ }
+
+ private companion object {
+ private const val NETWORK_REQUEST_DELAY: Int = 0
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsPostNotificationsBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBoxTest.kt
similarity index 64%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsPostNotificationsBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBoxTest.kt
index fabe6122e..a3f9648bc 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsPostNotificationsBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBoxTest.kt
@@ -1,23 +1,20 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsPostNotificationsBox
import org.michaelbel.movies.ui.theme.MoviesTheme
-class SettingsPostNotificationsBoxTest {
+internal class SettingsPostNotificationsBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -29,29 +26,25 @@ class SettingsPostNotificationsBoxTest {
SettingsPostNotificationsBox(
modifier = Modifier
.fillMaxWidth()
- .height(48.dp)
- .clickable {},
- areNotificationsEnabled = ARE_NOTIFICATIONS_ENABLED
+ .height(48.dp),
+ onShowPermissionSnackbar = {}
)
}
}
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "Text", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
composeTestRule
.onNodeWithTag(testTag = "Switch", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
- }
-
- private companion object {
- private const val ARE_NOTIFICATIONS_ENABLED = true
+ .assertHasNoClickAction()
}
}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsReviewBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBoxTest.kt
similarity index 78%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsReviewBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBoxTest.kt
index 8ef40df1b..2e8f30ca3 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsReviewBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBoxTest.kt
@@ -1,23 +1,21 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsReviewBox
import org.michaelbel.movies.ui.theme.MoviesTheme
-class SettingsReviewBoxTest {
+internal class SettingsReviewBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -37,11 +35,12 @@ class SettingsReviewBoxTest {
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "Text", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
}
}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsRtlBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBoxTest.kt
similarity index 75%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsRtlBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBoxTest.kt
index 62d69af78..f74c215ab 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsRtlBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBoxTest.kt
@@ -1,23 +1,21 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsRtlBox
import org.michaelbel.movies.ui.theme.MoviesTheme
-class SettingsRtlBoxTest {
+internal class SettingsRtlBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -27,28 +25,29 @@ class SettingsRtlBoxTest {
composeTestRule.setContent {
MoviesTheme {
SettingsRtlBox(
+ isRtlEnabled = IS_RTL_ENABLED,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .clickable {},
- isRtlEnabled = IS_RTL_ENABLED
+ .clickable {}
)
}
}
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "Text", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
composeTestRule
.onNodeWithTag(testTag = "Switch", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
}
private companion object {
diff --git a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsThemeBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBoxTest.kt
similarity index 66%
rename from app/src/androidTest/kotlin/org/michaelbel/movies/SettingsThemeBoxTest.kt
rename to feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBoxTest.kt
index 1b98968dc..e416a69d3 100644
--- a/app/src/androidTest/kotlin/org/michaelbel/movies/SettingsThemeBoxTest.kt
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBoxTest.kt
@@ -1,24 +1,21 @@
-package org.michaelbel.movies
+package org.michaelbel.movies.settings.ui
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasClickAction
-import androidx.compose.ui.test.hasNoClickAction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
import org.junit.Rule
import org.junit.Test
-import org.michaelbel.movies.settings.ui.SettingsThemeBox
+import org.michaelbel.movies.common.theme.AppTheme
import org.michaelbel.movies.ui.theme.MoviesTheme
-import org.michaelbel.movies.ui.theme.model.AppTheme
-class SettingsThemeBoxTest {
+internal class SettingsThemeBoxTest {
@get:Rule
val composeTestRule: ComposeContentTestRule = createComposeRule()
@@ -28,31 +25,38 @@ class SettingsThemeBoxTest {
composeTestRule.setContent {
MoviesTheme {
SettingsThemeBox(
+ themes = THEMES,
+ currentTheme = CURRENT_THEME,
+ onThemeSelect = {},
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .clickable {},
- currentTheme = CURRENT_THEME
)
}
}
composeTestRule
.onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
- .assert(hasClickAction())
+ .assertIsDisplayed()
+ .assertHasClickAction()
composeTestRule
.onNodeWithTag(testTag = "TitleText", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
composeTestRule
.onNodeWithTag(testTag = "ValueText", useUnmergedTree = true)
.assertIsDisplayed()
- .assert(hasNoClickAction())
+ .assertHasNoClickAction()
}
private companion object {
+ private val THEMES: List = listOf(
+ AppTheme.NightNo,
+ AppTheme.NightYes,
+ AppTheme.FollowSystem
+ )
private val CURRENT_THEME = AppTheme.FollowSystem
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialogTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialogTest.kt
new file mode 100644
index 000000000..0341bc666
--- /dev/null
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialogTest.kt
@@ -0,0 +1,65 @@
+package org.michaelbel.movies.settings.ui
+
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import org.junit.Rule
+import org.junit.Test
+import org.michaelbel.movies.common.theme.AppTheme
+import org.michaelbel.movies.ui.theme.MoviesTheme
+
+internal class SettingsThemeDialogTest {
+
+ @get:Rule
+ val composeTestRule: ComposeContentTestRule = createComposeRule()
+
+ @Test
+ fun testSettingsThemeDialog() {
+ composeTestRule.setContent {
+ MoviesTheme {
+ SettingThemeDialog(
+ themes = THEMES,
+ currentTheme = CURRENT_THEME,
+ onThemeSelect = {},
+ onDismissRequest = {}
+ )
+ }
+ }
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConfirmTextButton", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConfirmText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Icon", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Title", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Content", useUnmergedTree = true)
+ .assertIsDisplayed()
+ }
+
+ private companion object {
+ private val THEMES: List = listOf(
+ AppTheme.NightNo,
+ AppTheme.NightYes,
+ AppTheme.FollowSystem
+ )
+ private val CURRENT_THEME = AppTheme.FollowSystem
+ }
+}
\ No newline at end of file
diff --git a/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbarTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbarTest.kt
new file mode 100644
index 000000000..8a1e5e9b3
--- /dev/null
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbarTest.kt
@@ -0,0 +1,52 @@
+package org.michaelbel.movies.settings.ui
+
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import org.junit.Rule
+import org.junit.Test
+import org.michaelbel.movies.ui.theme.MoviesTheme
+
+internal class SettingsToolbarTest {
+
+ @get:Rule
+ val composeTestRule: ComposeContentTestRule = createComposeRule()
+
+ @Test
+ fun testSettingsToolbar() {
+ composeTestRule.setContent {
+ MoviesTheme {
+ SettingsToolbar(
+ modifier = Modifier
+ .statusBarsPadding(),
+ onNavigationIconClick = {}
+ )
+ }
+ }
+
+ composeTestRule
+ .onNodeWithTag(testTag = "TopAppBar", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "TitleText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "BackIconButton", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "BackImage", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+ }
+}
\ No newline at end of file
diff --git a/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBoxTest.kt b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBoxTest.kt
new file mode 100644
index 000000000..5ca7e95aa
--- /dev/null
+++ b/feature/settings-impl/src/androidTest/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBoxTest.kt
@@ -0,0 +1,58 @@
+package org.michaelbel.movies.settings.ui
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import org.junit.Rule
+import org.junit.Test
+import org.michaelbel.movies.common.version.AppVersionData
+import org.michaelbel.movies.ui.theme.MoviesTheme
+
+internal class SettingsVersionBoxTest {
+
+ @get:Rule
+ val composeTestRule: ComposeContentTestRule = createComposeRule()
+
+ @Test
+ fun testSettingsVersionBox() {
+ composeTestRule.setContent {
+ MoviesTheme {
+ SettingsVersionBox(
+ appVersionData = APP_VERSION_DATA,
+ modifier = Modifier
+ .navigationBarsPadding()
+ .fillMaxWidth()
+ )
+ }
+ }
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ConstraintLayout", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "Icon", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "TitleText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+
+ composeTestRule
+ .onNodeWithTag(testTag = "ValueText", useUnmergedTree = true)
+ .assertIsDisplayed()
+ .assertHasNoClickAction()
+ }
+
+ private companion object {
+ private val APP_VERSION_DATA: AppVersionData = AppVersionData("1.0.0", 1L)
+ }
+}
\ No newline at end of file
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBox.kt
index a6652e9f8..32f052d7b 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsDynamicColorsBox.kt
@@ -21,12 +21,11 @@ import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
fun SettingsDynamicColorsBox(
- modifier: Modifier = Modifier,
- isDynamicColorsEnabled: Boolean
+ isDynamicColorsEnabled: Boolean,
+ modifier: Modifier = Modifier
) {
ConstraintLayout(
- modifier = modifier
- .testTag("ConstraintLayout")
+ modifier = modifier.testTag("ConstraintLayout")
) {
val (title, value) = createRefs()
@@ -41,8 +40,9 @@ fun SettingsDynamicColorsBox(
bottom.linkTo(parent.bottom)
}
.testTag("Text"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Switch(
@@ -68,11 +68,11 @@ private fun SettingsDynamicColorsBoxPreview(
) {
MoviesTheme {
SettingsDynamicColorsBox(
+ isDynamicColorsEnabled = isEnabled,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .background(MaterialTheme.colorScheme.primaryContainer),
- isDynamicColorsEnabled = isEnabled
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBox.kt
index 33fb81f89..321b647df 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageBox.kt
@@ -27,10 +27,10 @@ import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
fun SettingsLanguageBox(
- modifier: Modifier = Modifier,
languages: List,
currentLanguage: AppLanguage,
- onLanguageSelect: (AppLanguage) -> Unit
+ onLanguageSelect: (AppLanguage) -> Unit,
+ modifier: Modifier = Modifier,
) {
var languageDialog: Boolean by remember { mutableStateOf(false) }
@@ -65,8 +65,9 @@ fun SettingsLanguageBox(
bottom.linkTo(parent.bottom)
}
.testTag("TitleText"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Text(
@@ -80,8 +81,9 @@ fun SettingsLanguageBox(
bottom.linkTo(parent.bottom)
}
.testTag("ValueText"),
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
}
@@ -93,13 +95,13 @@ private fun SettingsLanguageBoxPreview(
) {
MoviesTheme {
SettingsLanguageBox(
+ languages = listOf(AppLanguage.English, AppLanguage.Russian),
+ currentLanguage = language,
+ onLanguageSelect = {},
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .background(MaterialTheme.colorScheme.primaryContainer),
- languages = listOf(AppLanguage.English, AppLanguage.Russian),
- currentLanguage = language,
- onLanguageSelect = {}
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialog.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialog.kt
index cbac8a4ee..900e1bd7e 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialog.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsLanguageDialog.kt
@@ -17,6 +17,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -30,7 +31,7 @@ import org.michaelbel.movies.ui.preview.provider.LanguagePreviewParameterProvide
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-internal fun SettingLanguageDialog(
+fun SettingLanguageDialog(
languages: List,
currentLanguage: AppLanguage,
onLanguageSelect: (AppLanguage) -> Unit,
@@ -41,25 +42,35 @@ internal fun SettingLanguageDialog(
confirmButton = {
TextButton(
onClick = onDismissRequest,
+ modifier = Modifier
+ .testTag("ConfirmTextButton")
) {
Text(
text = stringResource(R.string.settings_action_cancel),
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.labelLarge
+ modifier = Modifier
+ .testTag("ConfirmText"),
+ style = MaterialTheme.typography.labelLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
},
icon = {
Icon(
imageVector = MoviesIcons.Language,
- contentDescription = null
+ contentDescription = null,
+ modifier = Modifier
+ .testTag("Icon")
)
},
title = {
Text(
text = stringResource(R.string.settings_language),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.headlineSmall
+ modifier = Modifier
+ .testTag("Title"),
+ style = MaterialTheme.typography.headlineSmall.copy(
+ color = MaterialTheme.colorScheme.onSurface
+ )
)
},
text = {
@@ -69,7 +80,8 @@ internal fun SettingLanguageDialog(
onLanguageSelect = { language ->
onLanguageSelect(language)
onDismissRequest()
- }
+ },
+ modifier = Modifier.testTag("Content")
)
},
shape = RoundedCornerShape(28.dp),
@@ -87,9 +99,12 @@ internal fun SettingLanguageDialog(
private fun SettingLanguageDialogContent(
languages: List,
currentLanguage: AppLanguage,
- onLanguageSelect: (AppLanguage) -> Unit
+ onLanguageSelect: (AppLanguage) -> Unit,
+ modifier: Modifier = Modifier
) {
- Column {
+ Column(
+ modifier = modifier
+ ) {
languages.forEach { language: AppLanguage ->
Row(
modifier = Modifier
@@ -108,19 +123,16 @@ private fun SettingLanguageDialogContent(
unselectedColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6F)
),
modifier = Modifier
- .padding(
- start = 16.dp
- )
+ .padding(start = 16.dp)
)
Text(
text = language.languageText,
modifier = Modifier
- .padding(
- start = 8.dp
- ),
- color = MaterialTheme.colorScheme.onBackground,
- style = MaterialTheme.typography.bodyLarge
+ .padding(start = 8.dp),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
}
}
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBox.kt
index 4ecddd285..2c3f7d975 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsNetworkRequestDelayBox.kt
@@ -12,6 +12,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -25,15 +26,16 @@ import kotlin.math.roundToInt
@Composable
fun SettingsNetworkRequestDelayBox(
- modifier: Modifier = Modifier,
delay: Int,
- onDelayChangeFinished: (Int) -> Unit
+ onDelayChangeFinished: (Int) -> Unit,
+ modifier: Modifier = Modifier
) {
var sliderPosition: Float by remember { mutableStateOf(delay.toFloat()) }
sliderPosition = delay.toFloat()
ConstraintLayout(
modifier = modifier
+ .testTag("ConstraintLayout")
) {
val (title, value, slider) = createRefs()
@@ -45,9 +47,11 @@ fun SettingsNetworkRequestDelayBox(
height = Dimension.wrapContent
start.linkTo(parent.start, 16.dp)
top.linkTo(parent.top, 8.dp)
- },
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ }
+ .testTag("TitleText"),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Text(
@@ -58,9 +62,11 @@ fun SettingsNetworkRequestDelayBox(
height = Dimension.wrapContent
top.linkTo(parent.top, 8.dp)
end.linkTo(parent.end, 16.dp)
- },
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.bodyLarge
+ }
+ .testTag("ValueText"),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
Slider(
@@ -76,7 +82,8 @@ fun SettingsNetworkRequestDelayBox(
top.linkTo(title.bottom, 4.dp)
end.linkTo(parent.end, 16.dp)
bottom.linkTo(parent.bottom, 8.dp)
- },
+ }
+ .testTag("Slider"),
valueRange = 0F..10000F,
steps = 9,
onValueChangeFinished = {
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBox.kt
index c081bf79b..3b20fc672 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsPostNotificationsBox.kt
@@ -102,8 +102,9 @@ fun SettingsPostNotificationsBox(
bottom.linkTo(parent.bottom)
}
.testTag("Text"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Switch(
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBox.kt
index 8442fdfbb..18b052f02 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsReviewBox.kt
@@ -21,8 +21,7 @@ fun SettingsReviewBox(
modifier: Modifier = Modifier
) {
ConstraintLayout(
- modifier = modifier
- .testTag("ConstraintLayout")
+ modifier = modifier.testTag("ConstraintLayout")
) {
val (title) = createRefs()
@@ -37,8 +36,9 @@ fun SettingsReviewBox(
bottom.linkTo(parent.bottom)
}
.testTag("Text"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
}
}
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBox.kt
index 427c79a33..0e15bd298 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsRtlBox.kt
@@ -21,12 +21,11 @@ import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
fun SettingsRtlBox(
- modifier: Modifier = Modifier,
- isRtlEnabled: Boolean
+ isRtlEnabled: Boolean,
+ modifier: Modifier = Modifier
) {
ConstraintLayout(
- modifier = modifier
- .testTag("ConstraintLayout")
+ modifier = modifier.testTag("ConstraintLayout")
) {
val (title, value) = createRefs()
@@ -41,8 +40,9 @@ fun SettingsRtlBox(
bottom.linkTo(parent.bottom)
}
.testTag("Text"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Switch(
@@ -68,11 +68,11 @@ private fun SettingsRtlBoxPreview(
) {
MoviesTheme {
SettingsRtlBox(
+ isRtlEnabled = isEnabled,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .background(MaterialTheme.colorScheme.primaryContainer),
- isRtlEnabled = isEnabled
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsScreenContent.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsScreenContent.kt
index 4f2314044..0fd3e41d9 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsScreenContent.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsScreenContent.kt
@@ -63,7 +63,6 @@ fun SettingsRoute(
SettingsScreenContent(
onBackClick = onBackClick,
- modifier = modifier,
languages = viewModel.languages,
currentLanguage = currentLanguage,
onLanguageSelect = viewModel::selectLanguage,
@@ -81,14 +80,14 @@ fun SettingsRoute(
isAppFromGooglePlay = isAppFromGooglePlay,
networkRequestDelay = networkRequestDelay,
onDelayChangeFinished = viewModel::setNetworkRequestDelay,
- appVersionData = appVersionData
+ appVersionData = appVersionData,
+ modifier = modifier
)
}
@Composable
private fun SettingsScreenContent(
onBackClick: () -> Unit,
- modifier: Modifier = Modifier,
languages: List,
currentLanguage: AppLanguage,
onLanguageSelect: (AppLanguage) -> Unit,
@@ -106,7 +105,8 @@ private fun SettingsScreenContent(
isAppFromGooglePlay: Boolean,
networkRequestDelay: Int,
onDelayChangeFinished: (Int) -> Unit,
- appVersionData: AppVersionData
+ appVersionData: AppVersionData,
+ modifier: Modifier = Modifier
) {
val context: Context = LocalContext.current
val scope: CoroutineScope = rememberCoroutineScope()
@@ -178,11 +178,11 @@ private fun SettingsScreenContent(
)
},
bottomBar = {
- SettingsLanguageBox(
+ SettingsVersionBox(
+ appVersionData = appVersionData,
modifier = Modifier
.navigationBarsPadding()
- .fillMaxWidth(),
- appVersionData = appVersionData
+ .fillMaxWidth()
)
},
snackbarHost = {
@@ -193,48 +193,47 @@ private fun SettingsScreenContent(
containerColor = MaterialTheme.colorScheme.primaryContainer
) { paddingValues: PaddingValues ->
Column(
- modifier = Modifier
- .padding(paddingValues)
+ modifier = Modifier.padding(paddingValues)
) {
SettingsLanguageBox(
- modifier = Modifier
- .fillMaxWidth()
- .height(48.dp),
languages = languages,
currentLanguage = currentLanguage,
- onLanguageSelect = onLanguageSelect
+ onLanguageSelect = onLanguageSelect,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
)
SettingsThemeBox(
- modifier = Modifier
- .fillMaxWidth()
- .height(48.dp),
themes = themes,
currentTheme = currentTheme,
- onThemeSelect = onThemeSelect
+ onThemeSelect = onThemeSelect,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
)
if (isDynamicColorsFeatureEnabled) {
SettingsDynamicColorsBox(
+ isDynamicColorsEnabled = dynamicColors,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.clickable {
onSetDynamicColors(!dynamicColors)
- },
- isDynamicColorsEnabled = dynamicColors
+ }
)
}
if (isRtlFeatureEnabled) {
SettingsRtlBox(
+ isRtlEnabled = isRtlEnabled,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.clickable {
onEnableRtlChanged(!isRtlEnabled)
- },
- isRtlEnabled = isRtlEnabled
+ }
)
}
@@ -258,10 +257,9 @@ private fun SettingsScreenContent(
if (BuildConfig.DEBUG) {
SettingsNetworkRequestDelayBox(
- modifier = Modifier
- .fillMaxWidth(),
delay = networkRequestDelay,
- onDelayChangeFinished = onDelayChangeFinished
+ onDelayChangeFinished = onDelayChangeFinished,
+ modifier = Modifier.fillMaxWidth()
)
}
}
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBox.kt
index 066339022..419baa594 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeBox.kt
@@ -27,10 +27,10 @@ import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
fun SettingsThemeBox(
- modifier: Modifier = Modifier,
themes: List,
currentTheme: AppTheme,
- onThemeSelect: (AppTheme) -> Unit
+ onThemeSelect: (AppTheme) -> Unit,
+ modifier: Modifier = Modifier
) {
var themeDialog: Boolean by remember { mutableStateOf(false) }
@@ -65,8 +65,9 @@ fun SettingsThemeBox(
bottom.linkTo(parent.bottom)
}
.testTag("TitleText"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Text(
@@ -80,8 +81,9 @@ fun SettingsThemeBox(
bottom.linkTo(parent.bottom)
}
.testTag("ValueText"),
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
}
@@ -93,13 +95,13 @@ private fun SettingsThemeBoxPreview(
) {
MoviesTheme {
SettingsThemeBox(
+ themes = listOf(AppTheme.FollowSystem, AppTheme.NightNo, AppTheme.NightYes),
+ currentTheme = theme,
+ onThemeSelect = {},
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
- .background(MaterialTheme.colorScheme.primaryContainer),
- themes = listOf(AppTheme.FollowSystem, AppTheme.NightNo, AppTheme.NightYes),
- currentTheme = theme,
- onThemeSelect = {}
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialog.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialog.kt
index 9e3a66867..7108e505f 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialog.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsThemeDialog.kt
@@ -17,6 +17,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -42,25 +43,35 @@ internal fun SettingThemeDialog(
confirmButton = {
TextButton(
onClick = onDismissRequest,
+ modifier = Modifier
+ .testTag("ConfirmTextButton")
) {
Text(
text = stringResource(R.string.settings_action_cancel),
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.labelLarge
+ modifier = Modifier
+ .testTag("ConfirmText"),
+ style = MaterialTheme.typography.labelLarge.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
},
icon = {
Icon(
painter = painterResource(MoviesIcons.ThemeLightDark),
- contentDescription = null
+ contentDescription = null,
+ modifier = Modifier
+ .testTag("Icon")
)
},
title = {
Text(
text = stringResource(R.string.settings_theme),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.headlineSmall
+ modifier = Modifier
+ .testTag("Title"),
+ style = MaterialTheme.typography.headlineSmall.copy(
+ color = MaterialTheme.colorScheme.onSurface
+ )
)
},
text = {
@@ -70,7 +81,8 @@ internal fun SettingThemeDialog(
onThemeSelect = { theme ->
onThemeSelect(theme)
onDismissRequest()
- }
+ },
+ modifier = Modifier.testTag("Content")
)
},
shape = RoundedCornerShape(28.dp),
@@ -88,9 +100,12 @@ internal fun SettingThemeDialog(
private fun SettingThemeDialogContent(
themes: List,
currentTheme: AppTheme,
- onThemeSelect: (AppTheme) -> Unit
+ onThemeSelect: (AppTheme) -> Unit,
+ modifier: Modifier = Modifier
) {
- Column {
+ Column(
+ modifier = modifier
+ ) {
themes.forEach { theme: AppTheme ->
Row(
modifier = Modifier
@@ -109,19 +124,16 @@ private fun SettingThemeDialogContent(
unselectedColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6F)
),
modifier = Modifier
- .padding(
- start = 16.dp
- )
+ .padding(start = 16.dp)
)
Text(
text = theme.themeText,
modifier = Modifier
- .padding(
- start = 8.dp
- ),
- color = MaterialTheme.colorScheme.onBackground,
- style = MaterialTheme.typography.bodyLarge
+ .padding(start = 8.dp),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onBackground
+ )
)
}
}
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbar.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbar.kt
index 5426b1fd3..2f58fad3c 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbar.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsToolbar.kt
@@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import org.michaelbel.movies.settings_impl.R
@@ -27,20 +28,24 @@ internal fun SettingsToolbar(
title = {
Text(
text = stringResource(R.string.settings_title),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
+ modifier = Modifier.testTag("TitleText"),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
},
- modifier = modifier,
+ modifier = modifier.testTag("TopAppBar"),
navigationIcon = {
IconButton(
- onClick = onNavigationIconClick
+ onClick = onNavigationIconClick,
+ modifier = Modifier.testTag("BackIconButton")
) {
Image(
imageVector = MoviesIcons.ArrowBack,
contentDescription = null,
+ modifier = Modifier.testTag("BackImage"),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimaryContainer)
)
}
@@ -56,8 +61,7 @@ internal fun SettingsToolbar(
private fun SettingsToolbarPreview() {
MoviesTheme {
SettingsToolbar(
- modifier = Modifier
- .statusBarsPadding(),
+ modifier = Modifier.statusBarsPadding(),
onNavigationIconClick = {}
)
}
diff --git a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBox.kt b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBox.kt
index 125a46bea..5fcbbde43 100644
--- a/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBox.kt
+++ b/feature/settings-impl/src/main/kotlin/org/michaelbel/movies/settings/ui/SettingsVersionBox.kt
@@ -24,13 +24,12 @@ import org.michaelbel.movies.ui.preview.provider.VersionPreviewParameterProvider
import org.michaelbel.movies.ui.theme.MoviesTheme
@Composable
-fun SettingsLanguageBox(
- modifier: Modifier = Modifier,
- appVersionData: AppVersionData
+fun SettingsVersionBox(
+ appVersionData: AppVersionData,
+ modifier: Modifier = Modifier
) {
ConstraintLayout(
- modifier = modifier
- .testTag("ConstraintLayout")
+ modifier = modifier.testTag("ConstraintLayout")
) {
val (icon, version, code) = createRefs()
createHorizontalChain(icon, version, code, chainStyle = ChainStyle.Packed)
@@ -46,7 +45,8 @@ fun SettingsLanguageBox(
top.linkTo(parent.top)
end.linkTo(version.start)
bottom.linkTo(parent.bottom)
- },
+ }
+ .testTag("Icon"),
tint = MaterialTheme.colorScheme.primary
)
@@ -63,8 +63,9 @@ fun SettingsLanguageBox(
}
.padding(start = 4.dp)
.testTag("TitleText"),
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium.copy(
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
)
Text(
@@ -80,24 +81,25 @@ fun SettingsLanguageBox(
}
.padding(start = 2.dp)
.testTag("ValueText"),
- color = MaterialTheme.colorScheme.primary,
- style = MaterialTheme.typography.bodySmall
+ style = MaterialTheme.typography.bodySmall.copy(
+ color = MaterialTheme.colorScheme.primary
+ )
)
}
}
@Composable
@DevicePreviews
-private fun SettingsLanguageBoxPreview(
+private fun SettingsVersionBoxPreview(
@PreviewParameter(VersionPreviewParameterProvider::class) appVersionData: AppVersionData
) {
MoviesTheme {
- SettingsLanguageBox(
+ SettingsVersionBox(
+ appVersionData = appVersionData,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
- .background(MaterialTheme.colorScheme.primaryContainer),
- appVersionData = appVersionData
+ .background(MaterialTheme.colorScheme.primaryContainer)
)
}
}
\ No newline at end of file
diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts
index bcda9937d..827b0c8a1 100644
--- a/feature/settings/build.gradle.kts
+++ b/feature/settings/build.gradle.kts
@@ -1,6 +1,7 @@
+@Suppress("dsl_scope_violation")
plugins {
- id("movies-android-library")
- id("movies-android-library-compose")
+ alias(libs.plugins.library)
+ alias(libs.plugins.kotlin)
}
android {
@@ -27,6 +28,11 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
lint {
quiet = true
abortOnError = false
@@ -38,4 +44,6 @@ android {
dependencies {
implementation(project(":feature:settings-impl"))
+
+ lintChecks(libs.lint.checks)
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 0e3fb97ea..8d18c27d9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,23 @@
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
-org.gradle.parallel=false
+org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
-kotlin.code.style=official
+# https://docs.gradle.org/current/userguide/performance.html#parallel_execution
+org.gradle.parallel=true
+# https://d.android.com/jetpack/androidx/migrate
android.useAndroidX=true
-android.enableJetifier=true
-android.nonTransitiveRClass=true
-android.defaults.buildFeatures.aidl=false
-android.defaults.buildFeatures.buildConfig=false
-android.defaults.buildFeatures.compose=false
-android.defaults.buildFeatures.dataBinding=false
-android.defaults.buildFeatures.prefab=false
-android.defaults.buildFeatures.renderScript=false
+# If your project already has the enableJetifier flag and it's turned on, you can run
+# Build Analyzer's Jetifier check to confirm if it's actually needed.
+# https://d.android.com/jetpack/androidx/migrate#migrate_an_existing_project_using_android_studio
+# https://d.android.com/build/optimize-your-build#disable-the-jetifier-flag
+android.enableJetifier=false
+
+# https://d.android.com/reference/tools/gradle-api/8.0/com/android/build/api/dsl/BuildFeatures#resValues()
+# Flag to enable Resource Values generation.
+# Default value is true.
android.defaults.buildFeatures.resValues=false
-android.defaults.buildFeatures.shaders=false
-android.defaults.buildFeatures.viewBinding=false
\ No newline at end of file
+
+# https://d.android.com/reference/tools/gradle-api/8.0/com/android/build/api/dsl/BuildFeatures#shaders()
+# Flag to enable Shader compilation.
+# Default value is true.
+android.defaults.buildFeatures.shaders=false
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 15d604c2b..e5b68e605 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -8,64 +8,68 @@ compile-sdk = "33"
# @keep
target-sdk = "33"
# @pin
-gradle = "7.4.2"
+gradle = "8.0.0"
# @pin update when updating compose compiler
-kotlin = "1.8.10"
-kotlin-ksp = "1.8.10-1.0.9"
+kotlin = "1.8.20"
+kotlin-ksp = "1.8.20-1.0.11"
kotlin-coroutines = "1.6.4"
kotlin-serialization = "1.5.0"
# @pin update when updating kotlin
-compose-compiler = "1.4.3"
+compose-compiler = "1.4.6"
detekt = "1.22.0"
-spotless = "6.17.0"
+spotless = "6.18.0"
google-services = "4.3.15"
-play-services-ads = "21.5.0"
+play-services-ads = "22.0.0"
play-services-base = "18.2.0"
-firebase-analytics = "21.2.0"
+firebase-analytics = "21.2.2"
firebase-appdistribution = "3.2.0"
-firebase-config = "21.2.1"
+firebase-config = "21.3.0"
firebase-crashlytics-plugin = "2.9.4"
-firebase-crashlytics = "18.3.5"
+firebase-crashlytics = "18.3.6"
play-core = "1.8.1"
-accompanist = "0.28.0"
+accompanist = "0.30.1"
material = "1.8.0"
material-compose-theme-adapter = "1.2.1"
hilt = "2.45"
-androidx-compose-foundation = "1.3.1"
-androidx-compose-runtime = "1.3.3"
-androidx-compose-ui = "1.3.3"
-androidx-compose-compiler = "1.4.3"
-androidx-compose-material = "1.3.1"
+androidx-compose-foundation = "1.4.2"
+androidx-compose-runtime = "1.4.2"
+androidx-compose-ui = "1.4.2"
+androidx-compose-compiler = "1.4.6"
+androidx-compose-material = "1.4.2"
androidx-compose-material3 = "1.0.1"
-androidx-appcompat = "1.6.0-rc01"
-androidx-activity = "1.6.1"
+androidx-appcompat = "1.7.0-alpha02"
+androidx-activity = "1.7.1"
androidx-browser = "1.5.0"
-androidx-core = "1.9.0"
-androidx-core-splashscreen = "1.0.0"
+androidx-core = "1.10.0"
+androidx-core-splashscreen = "1.0.1"
androidx-constraintlayout = "1.0.1"
-androidx-lifecycle = "2.6.0-rc01"
+androidx-lifecycle = "2.6.1"
androidx-hilt-navigation-compose = "1.0.0"
androidx-hilt-work = "1.0.0"
androidx-navigation = "2.5.3"
androidx-paging = "1.0.0-alpha18"
androidx-datastore = "1.0.0"
androidx-startup = "1.1.1"
-androidx-room = "2.5.0"
+androidx-room = "2.5.1"
androidx-test = "1.5.2"
androidx-test-ext = "1.1.5"
androidx-test-uiautomator = "2.2.0"
androidx-espresso-core = "3.5.1"
androidx-benchmark = "1.1.1"
-androidx-profile-installer = "1.3.0-beta01"
-androidx-work = "2.8.0"
-coil = "2.2.2"
-okhttp = "4.10.0"
+androidx-profile-installer = "1.3.0"
+androidx-work = "2.8.1"
+coil = "2.3.0"
+okhttp = "4.11.0"
retrofit = "2.9.0"
-retrofit-converter-serialization = "0.8.0"
+retrofit-converter-serialization = "1.0.0"
chucker = "3.5.2"
timber = "5.0.1"
javapoet = "1.13.0"
junit = "4.13.2"
+lint-checks = "1.2.0"
+palantir-git = "3.0.0"
+ben-manes-versions = "0.46.0"
+littlerobots-version-catalog-update = "0.8.0"
[libraries]
kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
@@ -142,6 +146,7 @@ chucker-library-no-op = { module = "com.github.chuckerteam.chucker:library-no-op
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" }
junit = { module = "junit:junit", version.ref = "junit" }
+lint-checks = { module = "com.slack.lint.compose:compose-lint-checks", version.ref = "lint-checks" }
gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
@@ -208,4 +213,7 @@ hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
androidx-navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "androidx-navigation" }
application = { id = "com.android.application", version.ref = "gradle" }
library = { id = "com.android.library", version.ref = "gradle" }
-test = { id = "com.android.test", version.ref = "gradle" }
\ No newline at end of file
+test = { id = "com.android.test", version.ref = "gradle" }
+palantir-git = { id = "com.palantir.git-version", version.ref = "palantir-git" }
+ben-manes-versions = { id = "com.github.ben-manes.versions", version.ref = "ben-manes-versions" }
+littlerobots-version-catalog-update = { id = "nl.littlerobots.version-catalog-update", version.ref = "littlerobots-version-catalog-update" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0643dcb7d..afcd5d3a4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Sun Apr 04 22:00:34 MSK 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 13b3c60f8..79d29ca27 100644
--- a/readme.md
+++ b/readme.md
@@ -23,7 +23,7 @@ TMDB_API_KEY=your_own_tmdb_api_key
## Download
[](https://play.google.com/store/apps/details?id=org.michaelbel.moviemade)
-[](https://github.com/michaelbel/movies/releases/download/1.4.4/movies-v1.4.4.apk)
+[](https://github.com/michaelbel/movies/releases/download/1.4.5/Movies-v1.4.5.1178.-release.apk)
## Technologies
@@ -32,8 +32,8 @@ TMDB_API_KEY=your_own_tmdb_api_key
- [x] [Clean Architecture](https://d.android.com/topic/architecture)
- [x] [TMDB API](https://developers.themoviedb.org/3/getting-started)
- [x] [KTS Gradle Files](https://d.android.com/studio/build/migrate-to-kts)
-- [x] [Kotlin Symbol Processing API](https://d.android.com/studio/build/migrate-to-ksp) 1.8.10-1.0.9
-- [x] [Gradle Plugin](https://d.android.com/studio/releases/gradle-plugin) 7.4.2
+- [x] [Kotlin Symbol Processing API](https://d.android.com/studio/build/migrate-to-ksp) 1.8.20-1.0.11
+- [x] [Gradle Plugin](https://d.android.com/studio/releases/gradle-plugin) 8.0.0
- [x] Gradle Version Catalog
- [x] MinSDK 21
- [x] TargetSDK 33
@@ -42,35 +42,35 @@ TMDB_API_KEY=your_own_tmdb_api_key
- [x] [Dark Theme](https://d.android.com/develop/ui/views/theming/darktheme)
- [x] [Dynamic Colors](https://d.android.com/develop/ui/views/theming/dynamic-colors)
- [x] [Themed App Icon](https://d.android.com/develop/ui/views/launch/icon_design_adaptive)
-- [x] 100% [Kotlin](https://d.android.com/kotlin) 1.8.10
-- [x] 100% [Jetpack Compose](https://d.android.com/jetpack/compose) 1.4.3
-- [x] [Accompanist](https://github.com/google/accompanist) 0.28.0
+- [x] 100% [Kotlin](https://d.android.com/kotlin) 1.8.20
+- [x] 100% [Jetpack Compose](https://d.android.com/jetpack/compose) 1.4.6
+- [x] [Accompanist](https://github.com/google/accompanist) 0.30.1
- [x] [Compose PreviewParameterProvider](https://d.android.com/jetpack/compose/tooling#previewparameter)
- [x] [Downloadable Fonts](https://d.android.com/develop/ui/views/text-and-emoji/downloadable-fonts)
- [x] [KotlinX Coroutines](https://github.com/Kotlin/kotlinx.coroutines) 1.6.4
-- [x] [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization) 1.4.1
+- [x] [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization) 1.5.0
- [x] [Appcompat](https://d.android.com/jetpack/androidx/releases/appcompat) 1.6.0-rc01
- [x] [Dagger Hilt](https://github.com/google/dagger) 2.45
- [x] [ViewModel](https://d.android.com/topic/libraries/architecture/viewmodel)
- [x] [Lifecycle](https://d.android.com/topic/libraries/architecture/lifecycle) 2.6.0-alpha03
-- [x] [Room](https://d.android.com/training/data-storage/room) 2.5.0
-- [x] [WorkManager](https://d.android.com/topic/libraries/architecture/workmanager) 2.8.0
+- [x] [Room](https://d.android.com/training/data-storage/room) 2.5.1
+- [x] [WorkManager](https://d.android.com/topic/libraries/architecture/workmanager) 2.8.1
- [x] [DataStore](https://d.android.com/datastore) 1.0.0
- [x] [Startup](https://d.android.com/jetpack/androidx/releases/startup) 1.1.1
- [x] [Navigation](https://d.android.com/guide/navigation) 2.5.3
- [x] [Paging](https://d.android.com/topic/libraries/architecture/paging/v3-overview) (RemoteMediator & PagingSource) 1.0.0-alpha18
- [x] [ConstraintLayout](https://d.android.com/develop/ui/views/layout/constraint-layout)
- [x] [Browser](https://d.android.com/jetpack/androidx/releases/browser) 1.5.0
-- [x] [OkHttp](https://github.com/square/okhttp) 4.10.0
+- [x] [OkHttp](https://github.com/square/okhttp) 4.11.0
- [x] [Retrofit](https://github.com/square/retrofit) 2.9.0
- [x] [Retrofit Kotlinx Converter Serialization](https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter) 0.8.0
- [x] [Chucker](https://github.com/ChuckerTeam/chucker) 3.5.2
-- [x] [Coil](https://github.com/coil-kt/coil) 2.2.2
+- [x] [Coil](https://github.com/coil-kt/coil) 2.3.0
- [x] [Timber](https://github.com/JakeWharton/timber) 5.0.1
-- [x] [Firebase Crashlytics](https://firebase.google.com/products/crashlytics) 18.3.5
+- [x] [Firebase Crashlytics](https://firebase.google.com/products/crashlytics) 18.3.6
- [x] [Firebase App Distribution](https://firebase.google.com/products/app-distribution) 3.2.0
-- [x] [Firebase Remote Config](https://firebase.google.com/products/remote-config) 21.2.1
-- [x] [Google Analytics for Firebase](https://firebase.google.com/products/analytics) 21.2.0
+- [x] [Firebase Remote Config](https://firebase.google.com/products/remote-config) 21.3.0
+- [x] [Google Analytics for Firebase](https://firebase.google.com/products/analytics) 21.2.2
- [x] [In-App Reviews](https://d.android.com/guide/playcore/in-app-review)
- [x] [App Shortcuts](https://d.android.com/develop/ui/views/launch/shortcuts)
- [x] [Dependabot](https://github.com/dependabot)
@@ -78,10 +78,10 @@ TMDB_API_KEY=your_own_tmdb_api_key
- [x] [Github Releases](https://github.com/michaelbel/movies/releases)
- [x] [Lint](https://d.android.com/studio/write/lint)
- [x] [Detekt](https://github.com/detekt/detekt) 1.22.0
-- [x] [Spotless](https://github.com/diffplug/spotless) 6.15.0
+- [x] [Spotless](https://github.com/diffplug/spotless) 6.18.0
- [x] [Distribute App via Telegram Bot](https://github.com/appleboy/telegram-action)
- [x] [Non-Transitive R classes](https://d.android.com/studio/build/optimize-your-build#use-non-transitive-r-classes)
-- [x] [SplashScreen API](https://d.android.com/develop/ui/views/launch/splash-screen)
+- [x] [SplashScreen API](https://d.android.com/develop/ui/views/launch/splash-screen) 1.0.1
- [x] [Per-App Language Preferences](https://d.android.com/guide/topics/resources/app-languages)
- [x] [Settings Panel](https://d.android.com/reference/android/provider/Settings.Panel)
- [x] [Benchmark](https://d.android.com/topic/performance/benchmarking/benchmarking-overview)