Skip to content

Commit

Permalink
Merge pull request #34 from omer358/dev
Browse files Browse the repository at this point in the history
Resolves #34
  • Loading branch information
omer358 authored Jul 31, 2024
2 parents 402551b + 026cb2a commit ea27510
Show file tree
Hide file tree
Showing 23 changed files with 689 additions and 39 deletions.
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,10 @@ dependencies {

// Lottie
implementation(libs.lottie.compose)

// compose animation
implementation(libs.androidx.animation)

// Data Store
implementation(libs.androidx.datastore.preferences)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.rememberme.domain.manager

import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.rememberme.data.manager.SettingsManagerImpl
import com.example.rememberme.domain.model.ThemeMode
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SettingsManagerImplTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()

private lateinit var settingsManager: SettingsManagerImpl
private lateinit var context: Context

@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
settingsManager = SettingsManagerImpl(context)
}

@Test
fun testSetThemeMode() = runBlocking {
settingsManager.setThemeMode(ThemeMode.DARK)

val themeMode = settingsManager.getThemeMode().first()
assertEquals(ThemeMode.DARK, themeMode)
}

@Test
fun testGetThemeMode() = runBlocking {
settingsManager.setThemeMode(ThemeMode.SYSTEM)

val themeMode = settingsManager.getThemeMode().first()
assertEquals(ThemeMode.SYSTEM, themeMode)
}

@Test
fun testIsDarkModeEnabled() = runBlocking {
settingsManager.setThemeMode(ThemeMode.DARK)

val isDarkModeEnabled = settingsManager.isDarkModeEnabled().first()
assertTrue(isDarkModeEnabled)
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/example/rememberme/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.getValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.example.rememberme.presentation.settings.SettingsEvent
import com.example.rememberme.presentation.settings.SettingsViewModel
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -22,6 +27,9 @@ class MainActivity : ComponentActivity() {
// }
// }
setContent {
val viewModel: SettingsViewModel = hiltViewModel()
val themeMode by viewModel.themeMode.collectAsStateWithLifecycle()
viewModel.onEvent(SettingsEvent.GetTheme)
RememberMeApp()
}
}
Expand Down
23 changes: 22 additions & 1 deletion app/src/main/java/com/example/rememberme/RememberMeApp.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
package com.example.rememberme

import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.util.Log
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.example.rememberme.domain.model.ThemeMode
import com.example.rememberme.presentation.navgraph.NavGraph
import com.example.rememberme.presentation.navgraph.Routes
import com.example.rememberme.presentation.settings.SettingsViewModel
import com.example.rememberme.ui.theme.RememberMeTheme


private const val TAG = "RememberMeApp"

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RememberMeApp() {
RememberMeTheme {
val viewModel: SettingsViewModel = hiltViewModel()

val themeMode by viewModel.themeMode.collectAsStateWithLifecycle()
val isDarkTheme = when (themeMode) {
ThemeMode.LIGHT -> false
ThemeMode.DARK -> true
ThemeMode.SYSTEM -> isSystemInDarkTheme()
}
Log.i(TAG, "RememberMeApp: $isDarkTheme")
RememberMeTheme(
darkTheme = isDarkTheme
){
Surface(
modifier = Modifier.fillMaxSize(),
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.example.rememberme.data.manager

import android.content.Context
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.example.rememberme.domain.manager.SettingsManager
import com.example.rememberme.domain.model.ThemeMode
import com.example.rememberme.utils.Constants
import com.example.rememberme.utils.Constants.REMEMBER_ME_SETTING_NAME
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class SettingsManagerImpl @Inject constructor(
private val context: Context
) : SettingsManager {
override suspend fun getThemeMode(): Flow<ThemeMode> {
Log.d(TAG, "getThemeMode: ")
return context.dataStore.data.map { preferences ->
val ordinal = preferences[PreferencesKeys.UI_MODE] ?: ThemeMode.SYSTEM.ordinal
ThemeMode.entries[ordinal]
}
}

override suspend fun setThemeMode(themeMode: ThemeMode) {
Log.d(TAG, "setThemeMode: ")
context.dataStore.edit { preferences ->
preferences[PreferencesKeys.UI_MODE] = when (themeMode) {
ThemeMode.LIGHT -> ThemeMode.LIGHT.ordinal
ThemeMode.DARK -> ThemeMode.DARK.ordinal
ThemeMode.SYSTEM -> ThemeMode.SYSTEM.ordinal
}
}
}

override suspend fun isDarkModeEnabled(): Flow<Boolean> {
Log.d(TAG, "isDarkModeEnabled: ")
return context.dataStore.data.map { preferences ->
(preferences[PreferencesKeys.UI_MODE] == ThemeMode.DARK.ordinal)
}
}

companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = REMEMBER_ME_SETTING_NAME)

private object PreferencesKeys {
val UI_MODE = intPreferencesKey(Constants.UI_MODE)
}

private const val TAG = "SettingsManagerImpl"
}
}


28 changes: 28 additions & 0 deletions app/src/main/java/com/example/rememberme/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.example.rememberme.di

import android.app.Application
import android.content.Context
import androidx.room.Room
import com.example.rememberme.data.PeopleRepositoryImpl
import com.example.rememberme.data.local.PeopleDatabase
import com.example.rememberme.data.manager.SettingsManagerImpl
import com.example.rememberme.domain.manager.SettingsManager
import com.example.rememberme.domain.repository.PeopleRepository
import com.example.rememberme.domain.usecases.add_person.AddPersonUseCases
import com.example.rememberme.domain.usecases.add_person.ValidateGenderSelectionUseCase
Expand All @@ -16,11 +19,16 @@ import com.example.rememberme.domain.usecases.people.GetPersonById
import com.example.rememberme.domain.usecases.people.InsertNewPerson
import com.example.rememberme.domain.usecases.people.PeopleUseCases
import com.example.rememberme.domain.usecases.people.UpdatePerson
import com.example.rememberme.domain.usecases.theme.GetThemeMode
import com.example.rememberme.domain.usecases.theme.IsDarkModeEnabled
import com.example.rememberme.domain.usecases.theme.SetThemeMode
import com.example.rememberme.domain.usecases.theme.ThemeUseCases
import com.example.rememberme.utils.Constants.PEOPLE_DATABASE_NAME
import com.example.remindme.database.PeopleDao
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

Expand Down Expand Up @@ -55,6 +63,14 @@ object AppModule {
return PeopleRepositoryImpl(peopleDao)
}

@Provides
@Singleton
fun provideSettingsManager(
@ApplicationContext context: Context
): SettingsManager {
return SettingsManagerImpl(context)
}

@Provides
@Singleton
fun providePeopleUseCases(
Expand All @@ -79,4 +95,16 @@ object AppModule {
validateGenderSelectionUseCase = ValidateGenderSelectionUseCase()
)
}

@Provides
@Singleton
fun provideThemeModeUseCases(
settingsManager: SettingsManager
): ThemeUseCases{
return ThemeUseCases(
setThemeMode = SetThemeMode(settingsManager),
getThemeMode = GetThemeMode(settingsManager),
isDarkModeEnabled = IsDarkModeEnabled(settingsManager)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.rememberme.domain.manager

import com.example.rememberme.domain.model.ThemeMode
import kotlinx.coroutines.flow.Flow

interface SettingsManager {
suspend fun getThemeMode(): Flow<ThemeMode>
suspend fun setThemeMode(themeMode: ThemeMode)
suspend fun isDarkModeEnabled(): Flow<Boolean>
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/example/rememberme/domain/model/ThemeMode.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.rememberme.domain.model

enum class ThemeMode {
LIGHT {
override fun toString(): String = "Light"
},
DARK {
override fun toString(): String = "Dark"
},
SYSTEM {
override fun toString(): String = "System Default"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.rememberme.domain.usecases.theme

import com.example.rememberme.domain.manager.SettingsManager
import com.example.rememberme.domain.model.ThemeMode
import kotlinx.coroutines.flow.Flow

class GetThemeMode(private val settingsManager: SettingsManager) {
suspend operator fun invoke(): Flow<ThemeMode> {
return settingsManager.getThemeMode()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.rememberme.domain.usecases.theme

import com.example.rememberme.domain.manager.SettingsManager
import kotlinx.coroutines.flow.Flow

class IsDarkModeEnabled(private val settingsManager: SettingsManager) {
suspend fun execute(): Flow<Boolean> {
return settingsManager.isDarkModeEnabled()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.rememberme.domain.usecases.theme

import com.example.rememberme.domain.manager.SettingsManager
import com.example.rememberme.domain.model.ThemeMode

class SetThemeMode(private val settingsManager: SettingsManager) {
suspend operator fun invoke(themeMode: ThemeMode) {
settingsManager.setThemeMode(themeMode)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.rememberme.domain.usecases.theme

data class ThemeUseCases(
val getThemeMode: GetThemeMode,
val setThemeMode: SetThemeMode,
val isDarkModeEnabled: IsDarkModeEnabled
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.example.rememberme.presentation.navgraph

import android.util.Log
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.EaseIn
import androidx.compose.animation.core.EaseOut
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.NavType
Expand All @@ -13,6 +17,7 @@ import com.example.rememberme.presentation.addperson.AddPersonScreen
import com.example.rememberme.presentation.details.PersonDetailsScreen
import com.example.rememberme.presentation.onboarding.OnBoardingScreen
import com.example.rememberme.presentation.peopleList.PeopleScreen
import com.example.rememberme.presentation.settings.SettingsScreen

private const val TAG = "NavGraph"
@Composable
Expand All @@ -22,7 +27,19 @@ fun NavGraph(
) {
NavHost(
navController = navController,
startDestination = startDestination
startDestination = startDestination,
enterTransition = {
slideIntoContainer(
animationSpec = tween(300, easing = EaseIn),
towards = AnimatedContentTransitionScope.SlideDirection.Start
)
},
exitTransition = {
slideOutOfContainer(
animationSpec = tween(300, easing = EaseOut),
towards = AnimatedContentTransitionScope.SlideDirection.End
)
}
) {
navigation(
route = Routes.AppStartNavigation.route,
Expand All @@ -47,6 +64,9 @@ fun NavGraph(
},
navigateToAddNewPersonScreen = {
navController.navigate(Routes.AddPersonScreen.route)
},
navigateToSettingsScreen = {
navController.navigate(Routes.SettingsScreen.route)
}
)
}
Expand Down Expand Up @@ -87,6 +107,13 @@ fun NavGraph(
)
}
}
composable(route = Routes.SettingsScreen.route) {
SettingsScreen(
popUp ={
navController.navigateUp()
}
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ sealed class Routes(
data object AddPersonScreen : Routes("addPersonScreen")
data object AppStartNavigation : Routes("appStartNavigation")
data object PeopleNavigation : Routes("peopleNavigation")
data object SettingsScreen : Routes("settingScreen")
}
Loading

0 comments on commit ea27510

Please sign in to comment.