diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5e5e70656a..ef97f665df 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,6 +35,8 @@ dependencies { implementation(projects.domain.txhistory) implementation(projects.domain.appCurrency) implementation(projects.domain.appCurrency.models) + implementation(projects.domain.appTheme) + implementation(projects.domain.appTheme.models) implementation(project(":common")) implementation(project(":core:analytics")) @@ -55,6 +57,7 @@ dependencies { implementation(projects.data.tokens) implementation(projects.data.txhistory) implementation(projects.data.appCurrency) + implementation(projects.data.appTheme) /** Features */ implementation(project(":features:onboarding")) @@ -86,6 +89,7 @@ dependencies { implementation(deps.lifecycle.runtime.ktx) implementation(deps.lifecycle.common.java8) implementation(deps.lifecycle.viewModel.ktx) + implementation(deps.lifecycle.compose) /** Compose libraries */ implementation(deps.compose.constraintLayout) diff --git a/app/src/main/java/com/tangem/tap/di/domain/AppThemeDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/AppThemeDomainModule.kt new file mode 100644 index 0000000000..b4378101b0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/AppThemeDomainModule.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.di.domain + +import com.tangem.domain.apptheme.ChangeAppThemeModeUseCase +import com.tangem.domain.apptheme.GetAppThemeModeUseCase +import com.tangem.domain.apptheme.repository.AppThemeModeRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent + +@Module +@InstallIn(ViewModelComponent::class) +internal object AppThemeDomainModule { + + @Provides + fun provideGetAppThemeModeUpdatesUseCase(appThemeModeRepository: AppThemeModeRepository): GetAppThemeModeUseCase { + return GetAppThemeModeUseCase(appThemeModeRepository) + } + + @Provides + fun provideChangeAppThemeModeUseCase(appThemeModeRepository: AppThemeModeRepository): ChangeAppThemeModeUseCase { + return ChangeAppThemeModeUseCase(appThemeModeRepository) + } +} \ No newline at end of file diff --git a/data/app-theme/.gitignore b/data/app-theme/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/data/app-theme/.gitignore @@ -0,0 +1 @@ +/build diff --git a/data/app-theme/build.gradle.kts b/data/app-theme/build.gradle.kts new file mode 100644 index 0000000000..b0f1f17a6a --- /dev/null +++ b/data/app-theme/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + id("configuration") +} + +android { + namespace = "com.tangem.data.apptheme" +} + +dependencies { + + /** Project - Domain */ + implementation(projects.domain.core) + implementation(projects.domain.appTheme) + implementation(projects.domain.appTheme.models) + + /** Project - Data */ + implementation(projects.core.datasource) + implementation(projects.data.common) + + /** Project - Utils */ + implementation(projects.core.utils) + + /** DI */ + implementation(deps.hilt.core) + kapt(deps.hilt.kapt) + + /** Other */ + implementation(deps.kotlin.coroutines) + implementation(deps.timber) + implementation(deps.jodatime) +} \ No newline at end of file diff --git a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/MockAppThemeModeRepository.kt b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/MockAppThemeModeRepository.kt new file mode 100644 index 0000000000..a4a885aab2 --- /dev/null +++ b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/MockAppThemeModeRepository.kt @@ -0,0 +1,19 @@ +package com.tangem.data.apptheme + +import com.tangem.domain.apptheme.model.AppThemeMode +import com.tangem.domain.apptheme.repository.AppThemeModeRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +internal class MockAppThemeModeRepository : AppThemeModeRepository { + + private val appThemeModeFlow = MutableStateFlow(AppThemeMode.DEFAULT) + + override fun getAppThemeMode(): Flow { + return appThemeModeFlow + } + + override suspend fun changeAppThemeMode(mode: AppThemeMode) { + appThemeModeFlow.value = mode + } +} \ No newline at end of file diff --git a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt new file mode 100644 index 0000000000..6847fad0b8 --- /dev/null +++ b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt @@ -0,0 +1,20 @@ +package com.tangem.data.apptheme.di + +import com.tangem.data.apptheme.MockAppThemeModeRepository +import com.tangem.domain.apptheme.repository.AppThemeModeRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AppThemeModeDataModule { + + @Provides + @Singleton + fun provideAppThemeModeRepository(): AppThemeModeRepository { + return MockAppThemeModeRepository() + } +} \ No newline at end of file diff --git a/domain/app-theme/.gitignore b/domain/app-theme/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/app-theme/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/app-theme/build.gradle.kts b/domain/app-theme/build.gradle.kts new file mode 100644 index 0000000000..79f941dbbb --- /dev/null +++ b/domain/app-theme/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} + +dependencies { + + /** Project - Domain */ + implementation(projects.core.utils) + implementation(projects.domain.core) + implementation(projects.domain.appTheme.models) +} \ No newline at end of file diff --git a/domain/app-theme/models/.gitignore b/domain/app-theme/models/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/app-theme/models/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/app-theme/models/build.gradle.kts b/domain/app-theme/models/build.gradle.kts new file mode 100644 index 0000000000..7ff7fb7522 --- /dev/null +++ b/domain/app-theme/models/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} \ No newline at end of file diff --git a/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt b/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt new file mode 100644 index 0000000000..f6ff6269f7 --- /dev/null +++ b/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt @@ -0,0 +1,30 @@ +package com.tangem.domain.apptheme.model + +/** + * Enumerates the possible modes for the application's theme. + */ +enum class AppThemeMode { + /** + * Forces the dark theme mode regardless of system settings. + */ + FORCE_DARK, + + /** + * Forces the light theme mode regardless of system settings. + */ + FORCE_LIGHT, + + /** + * Follows the system-wide theme mode. + */ + FOLLOW_SYSTEM, + + ; + + companion object { + /** + * The default [AppThemeMode]. + */ + val DEFAULT: AppThemeMode = FORCE_LIGHT + } +} \ No newline at end of file diff --git a/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/ChangeAppThemeModeUseCase.kt b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/ChangeAppThemeModeUseCase.kt new file mode 100644 index 0000000000..718f75a1a3 --- /dev/null +++ b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/ChangeAppThemeModeUseCase.kt @@ -0,0 +1,31 @@ +package com.tangem.domain.apptheme + +import arrow.core.Either +import arrow.core.raise.catch +import arrow.core.raise.either +import com.tangem.domain.apptheme.error.AppThemeModeError +import com.tangem.domain.apptheme.model.AppThemeMode +import com.tangem.domain.apptheme.repository.AppThemeModeRepository + +/** + * Use case responsible for changing the application theme mode. + * + * @property appThemeModeRepository The repository providing access to theme mode settings. + */ +class ChangeAppThemeModeUseCase( + private val appThemeModeRepository: AppThemeModeRepository, +) { + + /** + * Changes the application theme mode. + * + * @param mode The new [AppThemeMode] to set. + * @return An [Either] instance. The right side contains a [Unit] value indicating success, + * and the left side contains any [AppThemeModeError] that occurred during the process. + */ + suspend operator fun invoke(mode: AppThemeMode): Either = either { + catch({ appThemeModeRepository.changeAppThemeMode(mode) }) { + raise(AppThemeModeError.DataError(it)) + } + } +} \ No newline at end of file diff --git a/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/GetAppThemeModeUseCase.kt b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/GetAppThemeModeUseCase.kt new file mode 100644 index 0000000000..171209c0ba --- /dev/null +++ b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/GetAppThemeModeUseCase.kt @@ -0,0 +1,33 @@ +package com.tangem.domain.apptheme + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.apptheme.error.AppThemeModeError +import com.tangem.domain.apptheme.model.AppThemeMode +import com.tangem.domain.apptheme.repository.AppThemeModeRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map + +/** + * Use case responsible for retrieving the current application theme mode. + * + * @property appThemeModeRepository The repository providing access to theme mode settings. + */ +class GetAppThemeModeUseCase( + private val appThemeModeRepository: AppThemeModeRepository, +) { + + /** + * Invokes the use case to retrieve the current application theme mode. + * + * @return A [Flow] emitting an [Either] instance. The right side contains the retrieved + * [AppThemeMode], and the left side contains any [AppThemeModeError] that occurred during the process. + */ + operator fun invoke(): Flow> { + return appThemeModeRepository.getAppThemeMode() + .map> { it.right() } + .catch { emit(AppThemeModeError.DataError(it).left()) } + } +} \ No newline at end of file diff --git a/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/error/AppThemeModeError.kt b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/error/AppThemeModeError.kt new file mode 100644 index 0000000000..2ef8cfebb3 --- /dev/null +++ b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/error/AppThemeModeError.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.apptheme.error + +sealed class AppThemeModeError { + + data class DataError(val cause: Throwable) : AppThemeModeError() +} \ No newline at end of file diff --git a/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/repository/AppThemeModeRepository.kt b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/repository/AppThemeModeRepository.kt new file mode 100644 index 0000000000..3d2e2d5935 --- /dev/null +++ b/domain/app-theme/src/main/kotlin/com/tangem/domain/apptheme/repository/AppThemeModeRepository.kt @@ -0,0 +1,24 @@ +package com.tangem.domain.apptheme.repository + +import com.tangem.domain.apptheme.model.AppThemeMode +import kotlinx.coroutines.flow.Flow + +/** + * Represents a repository for managing the application's theme mode settings. + */ +interface AppThemeModeRepository { + + /** + * Retrieves the current application theme mode as a flow. + * + * @return A [Flow] emitting the current [AppThemeMode]. + */ + fun getAppThemeMode(): Flow + + /** + * Changes the application's theme mode to the specified [mode]. + * + * @param mode The new [AppThemeMode] to be set. + */ + suspend fun changeAppThemeMode(mode: AppThemeMode) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 33ccfc81d6..d2d077c6e7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -110,6 +110,8 @@ include(":domain:txhistory") include(":domain:txhistory:models") include(":domain:app-currency") include(":domain:app-currency:models") +include(":domain:app-theme") +include(":domain:app-theme:models") // endregion Domain modules // region Data modules @@ -120,4 +122,5 @@ include(":data:source:preferences") include(":data:settings") include(":data:txhistory") include(":data:app-currency") +include(":data:app-theme") // endregion Data modules \ No newline at end of file