From 0c55196041c2e61e066a53d4eb957842a7a4a63d Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 16:20:05 +0300 Subject: [PATCH 1/7] rename ThemePreferenceManager to PreferencesManager - Add shared preference for completing onboarding - Refactor usages of ThemePreferenceManager following the rename --- .../data/preferences/PreferenceManager.kt | 30 +++++++++++++++++++ .../preferences/ThemePreferenceManager.kt | 20 ------------- .../java/com/example/tudee/di/AppModule.kt | 10 ++----- .../category/viewmodel/CategoriesViewModel.kt | 1 - .../screen/home/viewmodel/HomeViewModel.kt | 1 - .../viewmodel/TasksScreenViewModel.kt | 5 ++-- .../themeViewModel/ThemeViewModel.kt | 4 +-- 7 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/example/tudee/data/preferences/PreferenceManager.kt delete mode 100644 app/src/main/java/com/example/tudee/data/preferences/ThemePreferenceManager.kt diff --git a/app/src/main/java/com/example/tudee/data/preferences/PreferenceManager.kt b/app/src/main/java/com/example/tudee/data/preferences/PreferenceManager.kt new file mode 100644 index 00000000..1a63bf67 --- /dev/null +++ b/app/src/main/java/com/example/tudee/data/preferences/PreferenceManager.kt @@ -0,0 +1,30 @@ +package com.example.tudee.data.preferences + + +import android.content.Context +import androidx.core.content.edit + +class PreferencesManager(context: Context) { + private val prefs = context.getSharedPreferences("tudee_preferences", Context.MODE_PRIVATE) + + companion object { + private const val KEY_DARK_MODE = "dark_mode" + private const val KEY_ONBOARDING_COMPLETED = "onboarding_completed" + } + + fun isDarkMode(): Boolean { + return prefs.getBoolean(KEY_DARK_MODE, false) + } + + fun setDarkMode(isDark: Boolean) { + prefs.edit { putBoolean(KEY_DARK_MODE, isDark) } + } + + fun isOnboardingCompleted(): Boolean { + return prefs.getBoolean(KEY_ONBOARDING_COMPLETED, false) + } + + fun setOnboardingCompleted() { + prefs.edit { putBoolean(KEY_ONBOARDING_COMPLETED, true) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/data/preferences/ThemePreferenceManager.kt b/app/src/main/java/com/example/tudee/data/preferences/ThemePreferenceManager.kt deleted file mode 100644 index 88cac068..00000000 --- a/app/src/main/java/com/example/tudee/data/preferences/ThemePreferenceManager.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.example.tudee.data.preferences - - -import android.content.Context - -class ThemePreferenceManager(context: Context) { - private val prefs = context.getSharedPreferences("tudee_preferences", Context.MODE_PRIVATE) - - companion object { - private const val KEY_DARK_MODE = "dark_mode" - } - - fun isDarkMode(): Boolean { - return prefs.getBoolean(KEY_DARK_MODE, false) - } - - fun setDarkMode(isDark: Boolean) { - prefs.edit().putBoolean(KEY_DARK_MODE, isDark).apply() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/di/AppModule.kt b/app/src/main/java/com/example/tudee/di/AppModule.kt index a39c4249..6fa176b2 100644 --- a/app/src/main/java/com/example/tudee/di/AppModule.kt +++ b/app/src/main/java/com/example/tudee/di/AppModule.kt @@ -2,15 +2,12 @@ package com.example.tudee.di import androidx.lifecycle.SavedStateHandle import androidx.room.Room -import com.example.tudee.data.dao.AppEntryDao import com.example.tudee.data.dao.TaskCategoryDao import com.example.tudee.data.dao.TaskDao import com.example.tudee.data.database.AppDatabase -import com.example.tudee.data.preferences.ThemePreferenceManager +import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.data.service.TaskCategoryServiceImpl import com.example.tudee.data.service.TaskServiceImpl -import com.example.tudee.domain.AppEntry -import com.example.tudee.domain.AppEntryImpl import com.example.tudee.domain.TaskCategoryService import com.example.tudee.domain.TaskService import com.example.tudee.presentation.screen.category.tasks.CategoryTasksViewModel @@ -52,10 +49,7 @@ val appModule = module { } single { OnBoardingViewModel(get(), get()) } - single { get().appEntryDao() } - single { AppEntryImpl(get()) } - - single { ThemePreferenceManager(androidContext()) } + single { PreferencesManager(androidContext()) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/presentation/screen/category/viewmodel/CategoriesViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/category/viewmodel/CategoriesViewModel.kt index 5ded5712..8fd0f503 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/category/viewmodel/CategoriesViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/category/viewmodel/CategoriesViewModel.kt @@ -2,7 +2,6 @@ package com.example.tudee.presentation.screen.category.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.tudee.data.preferences.ThemePreferenceManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.domain.TaskService import com.example.tudee.domain.request.CategoryCreationRequest diff --git a/app/src/main/java/com/example/tudee/presentation/screen/home/viewmodel/HomeViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/home/viewmodel/HomeViewModel.kt index e6846bf8..57a7ccb2 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/home/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/home/viewmodel/HomeViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.tudee.R import com.example.tudee.data.mapper.toCategoryUiState -import com.example.tudee.data.preferences.ThemePreferenceManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.domain.TaskService import com.example.tudee.domain.entity.Task diff --git a/app/src/main/java/com/example/tudee/presentation/screen/task_screen/viewmodel/TasksScreenViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/task_screen/viewmodel/TasksScreenViewModel.kt index 32857a49..5a9539a4 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/task_screen/viewmodel/TasksScreenViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/task_screen/viewmodel/TasksScreenViewModel.kt @@ -3,10 +3,9 @@ package com.example.tudee.presentation.screen.task_screen.viewmodel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.tudee.data.preferences.ThemePreferenceManager +import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.domain.TaskService -import com.example.tudee.domain.entity.TaskPriority import com.example.tudee.domain.entity.TaskStatus import com.example.tudee.presentation.components.buttons.ButtonState import com.example.tudee.presentation.screen.task_screen.interactors.TaskScreenInteractor @@ -33,7 +32,7 @@ class TasksScreenViewModel( private val taskService: TaskService, private val categoryService: TaskCategoryService, private val savedStateHandle: SavedStateHandle, - private val themePrefs: ThemePreferenceManager + private val themePrefs: PreferencesManager ) : ViewModel(), TaskScreenInteractor { private val _taskScreenUiState = MutableStateFlow(TasksScreenUiState( isDarkMode = themePrefs.isDarkMode())) val taskScreenUiState = _taskScreenUiState diff --git a/app/src/main/java/com/example/tudee/presentation/themeViewModel/ThemeViewModel.kt b/app/src/main/java/com/example/tudee/presentation/themeViewModel/ThemeViewModel.kt index 352590ee..eaf694b1 100644 --- a/app/src/main/java/com/example/tudee/presentation/themeViewModel/ThemeViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/themeViewModel/ThemeViewModel.kt @@ -1,11 +1,11 @@ package com.example.tudee.presentation.themeViewModel import androidx.lifecycle.ViewModel -import com.example.tudee.data.preferences.ThemePreferenceManager +import com.example.tudee.data.preferences.PreferencesManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class ThemeViewModel(private val prefs: ThemePreferenceManager) : ViewModel() { +class ThemeViewModel(private val prefs: PreferencesManager) : ViewModel() { private val _isDarkMode = MutableStateFlow(prefs.isDarkMode()) val isDarkMode: StateFlow = _isDarkMode From 0a67f2ecd5ebd69151c265d7b92fc18c425ee844 Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 16:28:37 +0300 Subject: [PATCH 2/7] update OnBoardingViewModel and its tests --- .../screen/onboarding/OnBoardingViewModel.kt | 18 +++------- .../example/tudee/OnBoardingViewModelTest.kt | 36 +++++++++---------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt index 59c09718..d26b01bd 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt @@ -2,7 +2,7 @@ package com.example.tudee.presentation.screen.onboarding import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.tudee.domain.AppEntry +import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.presentation.utils.predefinedCategories import kotlinx.coroutines.flow.MutableStateFlow @@ -11,23 +11,13 @@ import kotlinx.coroutines.launch class OnBoardingViewModel( private val taskCategoryService: TaskCategoryService, - private val appEntry: AppEntry + private val prefs: PreferencesManager ) : ViewModel() { - private val _isFirstEntry = MutableStateFlow(true) + private val _isFirstEntry = MutableStateFlow(prefs.isOnboardingCompleted()) val isFirstEntry = _isFirstEntry.asStateFlow() - init { - viewModelScope.launch { - _isFirstEntry.value = appEntry.isFirstEntry() - } - } - - fun saveFirstEntry() { - viewModelScope.launch { - appEntry.saveFirstEntry() - } - } + fun saveFirstEntry() = prefs.setOnboardingCompleted() fun loadInitialData() { viewModelScope.launch { diff --git a/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt b/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt index 6f5dfcb1..79cc0d2c 100644 --- a/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt +++ b/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt @@ -1,12 +1,13 @@ package com.example.tudee -import com.example.tudee.domain.AppEntry +import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.presentation.screen.onboarding.OnBoardingViewModel import com.example.tudee.presentation.utils.predefinedCategories import com.google.common.truth.Truth.assertThat import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.runs @@ -27,14 +28,14 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class OnBoardingViewModelTest { private lateinit var viewModel: OnBoardingViewModel - private lateinit var appEntry: AppEntry + private lateinit var preferencesManager: PreferencesManager private lateinit var taskCategoryService: TaskCategoryService private val testDispatcher = StandardTestDispatcher() @Before fun setup() { Dispatchers.setMain(testDispatcher) - appEntry = mockk() + preferencesManager = mockk() taskCategoryService = mockk() } @@ -44,52 +45,49 @@ class OnBoardingViewModelTest { } @Test - fun `isFirstEntry should emit true when appEntry returns true`() = runTest { + fun `isFirstEntry should emit true when preferences returns true`() = runTest { // Given - coEvery { appEntry.isFirstEntry() } returns true + every { preferencesManager.isOnboardingCompleted() } returns true // When - viewModel = OnBoardingViewModel(taskCategoryService, appEntry) - advanceUntilIdle() + viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) // Then assertThat(viewModel.isFirstEntry.value).isTrue() } @Test - fun `isFirstEntry should emit false when appEntry returns false`() = runTest { + fun `isFirstEntry should emit false when preferences returns false`() = runTest { // Given - coEvery { appEntry.isFirstEntry() } returns false + every { preferencesManager.isOnboardingCompleted() } returns false // When - viewModel = OnBoardingViewModel(taskCategoryService, appEntry) - advanceUntilIdle() + viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) // Then assertThat(viewModel.isFirstEntry.value).isFalse() } @Test - fun `saveFirstEntry should call appEntry saveFirstEntry`() = runTest { + fun `saveFirstEntry should call preferences setOnboardingCompleted`() = runTest { // Given - coEvery { appEntry.isFirstEntry() } returns true - coEvery { appEntry.saveFirstEntry() } just runs - viewModel = OnBoardingViewModel(taskCategoryService, appEntry) + every { preferencesManager.isOnboardingCompleted() } returns true + every { preferencesManager.setOnboardingCompleted() } just runs + viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) // When viewModel.saveFirstEntry() - advanceUntilIdle() // Then - coVerify(exactly = 1) { appEntry.saveFirstEntry() } + coVerify(exactly = 1) { preferencesManager.setOnboardingCompleted() } } @Test fun `loadInitialData should create all predefined categories`() = runTest { // Given - coEvery { appEntry.isFirstEntry() } returns true + every { preferencesManager.isOnboardingCompleted() } returns true coEvery { taskCategoryService.createCategory(any()) } just runs - viewModel = OnBoardingViewModel(taskCategoryService, appEntry) + viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) // When viewModel.loadInitialData() From 12bf70a81dd9727c86be053316e0b0740b113e68 Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 16:29:34 +0300 Subject: [PATCH 3/7] delete all AppEntry files, and remove it from the AppDB --- .../com/example/tudee/data/dao/AppEntryDao.kt | 15 --------------- .../example/tudee/data/database/AppDataBase.kt | 5 +---- .../example/tudee/data/model/AppEntryEntity.kt | 10 ---------- .../java/com/example/tudee/domain/AppEntry.kt | 6 ------ .../com/example/tudee/domain/AppEntryImpl.kt | 16 ---------------- 5 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 app/src/main/java/com/example/tudee/data/dao/AppEntryDao.kt delete mode 100644 app/src/main/java/com/example/tudee/data/model/AppEntryEntity.kt delete mode 100644 app/src/main/java/com/example/tudee/domain/AppEntry.kt delete mode 100644 app/src/main/java/com/example/tudee/domain/AppEntryImpl.kt diff --git a/app/src/main/java/com/example/tudee/data/dao/AppEntryDao.kt b/app/src/main/java/com/example/tudee/data/dao/AppEntryDao.kt deleted file mode 100644 index 8f437500..00000000 --- a/app/src/main/java/com/example/tudee/data/dao/AppEntryDao.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.tudee.data.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.Query -import com.example.tudee.data.model.AppEntryEntity - -@Dao -interface AppEntryDao { - @Query("SELECT * FROM app_entry LIMIT 1") - suspend fun getAppEntry(): AppEntryEntity? - - @Insert - suspend fun insertAppEntry(appEntry: AppEntryEntity) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/data/database/AppDataBase.kt b/app/src/main/java/com/example/tudee/data/database/AppDataBase.kt index bd775953..1ed48bc9 100644 --- a/app/src/main/java/com/example/tudee/data/database/AppDataBase.kt +++ b/app/src/main/java/com/example/tudee/data/database/AppDataBase.kt @@ -2,20 +2,17 @@ package com.example.tudee.data.database import androidx.room.Database import androidx.room.RoomDatabase -import com.example.tudee.data.dao.AppEntryDao import com.example.tudee.data.dao.TaskCategoryDao import com.example.tudee.data.dao.TaskDao -import com.example.tudee.data.model.AppEntryEntity import com.example.tudee.data.model.TaskCategoryEntity import com.example.tudee.data.model.TaskEntity @Database( - entities = [TaskEntity::class, TaskCategoryEntity::class, AppEntryEntity::class], + entities = [TaskEntity::class, TaskCategoryEntity::class], version = 1, exportSchema = false ) abstract class AppDatabase : RoomDatabase() { abstract fun taskDao(): TaskDao abstract fun taskCategoryDao(): TaskCategoryDao - abstract fun appEntryDao(): AppEntryDao } \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/data/model/AppEntryEntity.kt b/app/src/main/java/com/example/tudee/data/model/AppEntryEntity.kt deleted file mode 100644 index 3a7fd028..00000000 --- a/app/src/main/java/com/example/tudee/data/model/AppEntryEntity.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.example.tudee.data.model - -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "app_entry") -data class AppEntryEntity( - @PrimaryKey val id: Int = 1, - val hasSeenOnboarding: Boolean = true -) \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/domain/AppEntry.kt b/app/src/main/java/com/example/tudee/domain/AppEntry.kt deleted file mode 100644 index 7486e7e3..00000000 --- a/app/src/main/java/com/example/tudee/domain/AppEntry.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.tudee.domain - -interface AppEntry { - suspend fun saveFirstEntry() - suspend fun isFirstEntry(): Boolean -} \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/domain/AppEntryImpl.kt b/app/src/main/java/com/example/tudee/domain/AppEntryImpl.kt deleted file mode 100644 index b46c7eae..00000000 --- a/app/src/main/java/com/example/tudee/domain/AppEntryImpl.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.tudee.domain - -import com.example.tudee.data.dao.AppEntryDao -import com.example.tudee.data.model.AppEntryEntity - -class AppEntryImpl( - private val appEntryDao: AppEntryDao -) : AppEntry { - override suspend fun saveFirstEntry() { - appEntryDao.insertAppEntry(AppEntryEntity()) - } - - override suspend fun isFirstEntry(): Boolean { - return appEntryDao.getAppEntry() == null - } -} \ No newline at end of file From 8972f8d8c2e62ddc7d7dfbe8eac5e1e6e6ca4b17 Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 16:29:54 +0300 Subject: [PATCH 4/7] update SplashScreen to use SharedPreferences instead of AppEntry --- .../tudee/presentation/screen/splash/SplashScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt b/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt index f5dfe17a..eeb06661 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt @@ -20,8 +20,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import com.example.tudee.R +import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.designsystem.theme.TudeeTheme -import com.example.tudee.domain.AppEntry import com.example.tudee.naviagtion.Destination import com.example.tudee.presentation.themeViewModel.ThemeViewModel import kotlinx.coroutines.delay @@ -37,12 +37,12 @@ fun SplashScreen( overlayColor: Color = TudeeTheme.color.statusColors.overlay, backgroundPainter: Painter = painterResource(R.drawable.background_ellipse), iconPainter: Painter = painterResource(R.drawable.tudee_logo), - appEntry: AppEntry = getKoin().get() + prefs: PreferencesManager = getKoin().get() ) { val isDark by themeViewModel.isDarkMode.collectAsState() LaunchedEffect(Unit) { delay(3000) - if (appEntry.isFirstEntry()) { + if (prefs.isOnboardingCompleted()) { navController.navigate(Destination.OnBoardingScreen.route) { popUpTo(Destination.SplashScreen.route) { inclusive = true } } From f79d6ac4f9adf03c7048f6c6297921af4d30f1ef Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 17:39:12 +0300 Subject: [PATCH 5/7] update SplashScreen to properly navigate to OnBoardingScreen if not completed --- .../example/tudee/presentation/screen/splash/SplashScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt b/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt index eeb06661..762204ef 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/splash/SplashScreen.kt @@ -42,7 +42,7 @@ fun SplashScreen( val isDark by themeViewModel.isDarkMode.collectAsState() LaunchedEffect(Unit) { delay(3000) - if (prefs.isOnboardingCompleted()) { + if (prefs.isOnboardingCompleted().not()) { navController.navigate(Destination.OnBoardingScreen.route) { popUpTo(Destination.SplashScreen.route) { inclusive = true } } From 5df849527b3f277848d1d0ea6cd016e7c7f03720 Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 17:39:51 +0300 Subject: [PATCH 6/7] remove redundant code checking first entry in OnBoardingScreen Splash screen already does this step. --- .../screen/onboarding/OnBoardingScreen.kt | 21 ++++++++----------- .../screen/onboarding/OnBoardingViewModel.kt | 7 ------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingScreen.kt b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingScreen.kt index 15a8bc51..1de073af 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingScreen.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingScreen.kt @@ -1,6 +1,8 @@ package com.example.tudee.presentation.screen.onboarding import android.content.res.Configuration +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -12,8 +14,6 @@ import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment @@ -44,15 +44,6 @@ fun OnBoardingScreen( navController: NavController = rememberNavController(), viewModel: OnBoardingViewModel = getKoin().get() ) { - - val isFirstEntry by viewModel.isFirstEntry.collectAsState() - - if (!isFirstEntry) { - navController.navigate(Destination.HomeScreen.route) { - popUpTo(Destination.OnBoardingScreen.route) { inclusive = true } - } - } - val onboardingOnBoardingPageUiModels = listOf( OnBoardingPageUiModel( title = stringResource(R.string.on_boarding_title1), @@ -147,7 +138,13 @@ private fun OnBoardingContent( onClick = { scope.launch { if (pageState.currentPage != onBoardingPageUiModels.lastIndex) { - pageState.animateScrollToPage(pageState.currentPage + Pages.SecondPage.page) + pageState.animateScrollToPage( + page = pageState.currentPage + 1, + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) } else { navigateToHome() } diff --git a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt index d26b01bd..ca7f05e4 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/onboarding/OnBoardingViewModel.kt @@ -5,20 +5,13 @@ import androidx.lifecycle.viewModelScope import com.example.tudee.data.preferences.PreferencesManager import com.example.tudee.domain.TaskCategoryService import com.example.tudee.presentation.utils.predefinedCategories -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class OnBoardingViewModel( private val taskCategoryService: TaskCategoryService, private val prefs: PreferencesManager ) : ViewModel() { - - private val _isFirstEntry = MutableStateFlow(prefs.isOnboardingCompleted()) - val isFirstEntry = _isFirstEntry.asStateFlow() - fun saveFirstEntry() = prefs.setOnboardingCompleted() - fun loadInitialData() { viewModelScope.launch { predefinedCategories.forEach { taskCategoryService.createCategory(it) } From 011db15948cb61687d3ec832d7ff982f071d51be Mon Sep 17 00:00:00 2001 From: Yusuf Nasser Date: Fri, 27 Jun 2025 17:50:44 +0300 Subject: [PATCH 7/7] update OnBoardingViewModelTest, remove AppFirstEntry tests --- .../example/tudee/OnBoardingViewModelTest.kt | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt b/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt index 79cc0d2c..52ecf0d5 100644 --- a/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt +++ b/app/src/test/java/com/example/tudee/OnBoardingViewModelTest.kt @@ -44,30 +44,6 @@ class OnBoardingViewModelTest { Dispatchers.resetMain() } - @Test - fun `isFirstEntry should emit true when preferences returns true`() = runTest { - // Given - every { preferencesManager.isOnboardingCompleted() } returns true - - // When - viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) - - // Then - assertThat(viewModel.isFirstEntry.value).isTrue() - } - - @Test - fun `isFirstEntry should emit false when preferences returns false`() = runTest { - // Given - every { preferencesManager.isOnboardingCompleted() } returns false - - // When - viewModel = OnBoardingViewModel(taskCategoryService, preferencesManager) - - // Then - assertThat(viewModel.isFirstEntry.value).isFalse() - } - @Test fun `saveFirstEntry should call preferences setOnboardingCompleted`() = runTest { // Given