From 3e3bd3a612f196b7c054bc4ae6f02408aa4be796 Mon Sep 17 00:00:00 2001 From: razeeman Date: Sun, 23 Jul 2023 15:05:53 +0000 Subject: [PATCH] split settings view model into delegates --- core/src/main/res/values/strings.xml | 2 +- core/src/main/java/com/example/util/simpletimetracker/core/base/BaseViewModel.kt | 10 ++++++++++ core/src/main/java/com/example/util/simpletimetracker/core/base/ScopeHolder.kt | 8 ++++++++ core/src/main/java/com/example/util/simpletimetracker/core/base/ViewModelDelegate.kt | 17 +++++++++++++++++ core/src/main/java/com/example/util/simpletimetracker/core/extension/LiveDataExtensions.kt | 7 +++---- features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------- features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt | 1031 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsMainViewModelDelegate.kt | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsNotificationsViewModelDelegate.kt | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsParent.kt | 12 ++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsRatingViewModelDelegate.kt | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsTranslatorsViewModelDelegate.kt | 20 ++++++++++++++++++++ 14 file(s) changed, 1362 insertion(s)(+), 1049 deletion(s)(-) diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -235,7 +235,7 @@ Enabled Language Use 24-hour format - Use Month/Day date format + Use Month/Day date format Use proportional minute format Show seconds Keep screen on diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/base/BaseViewModel.kt b/core/src/main/java/com/example/util/simpletimetracker/core/base/BaseViewModel.kt new file mode 100644 --- /dev/null +++ b/core/src/main/java/com/example/util/simpletimetracker/core/base/BaseViewModel.kt @@ -0,0 +1,10 @@ +package com.example.util.simpletimetracker.core.base + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.CoroutineScope + +abstract class BaseViewModel : ViewModel(), ScopeHolder { + + override fun getScope(): CoroutineScope { return viewModelScope } +} \ No newline at end of file diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/base/ScopeHolder.kt b/core/src/main/java/com/example/util/simpletimetracker/core/base/ScopeHolder.kt new file mode 100644 --- /dev/null +++ b/core/src/main/java/com/example/util/simpletimetracker/core/base/ScopeHolder.kt @@ -0,0 +1,8 @@ +package com.example.util.simpletimetracker.core.base + +import kotlinx.coroutines.CoroutineScope + +interface ScopeHolder { + + fun getScope(): CoroutineScope +} \ No newline at end of file diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/base/ViewModelDelegate.kt b/core/src/main/java/com/example/util/simpletimetracker/core/base/ViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/core/src/main/java/com/example/util/simpletimetracker/core/base/ViewModelDelegate.kt @@ -0,0 +1,17 @@ +package com.example.util.simpletimetracker.core.base + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel + +abstract class ViewModelDelegate : ScopeHolder { + + val delegateScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + + override fun getScope(): CoroutineScope { return delegateScope } + + fun clear() { + delegateScope.cancel() + } +} \ No newline at end of file diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/extension/LiveDataExtensions.kt b/core/src/main/java/com/example/util/simpletimetracker/core/extension/LiveDataExtensions.kt --- a/core/src/main/java/com/example/util/simpletimetracker/core/extension/LiveDataExtensions.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/extension/LiveDataExtensions.kt @@ -5,8 +5,7 @@ import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import com.example.util.simpletimetracker.core.base.ScopeHolder import kotlinx.coroutines.launch fun LiveData.observeOnce(owner: LifecycleOwner, observer: (T) -> Unit) { @@ -64,11 +63,11 @@ } } -fun ViewModel.lazySuspend( +fun ScopeHolder.lazySuspend( initializer: suspend () -> T, ): Lazy> = lazy { MutableLiveData().let { initial -> - viewModelScope.launch { + getScope().launch { initial.value = initializer() } initial diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt @@ -52,94 +52,99 @@ lateinit var mainTabsViewModelFactory: BaseViewModelFactory private val viewModel: SettingsViewModel by viewModels() - private val backupViewModel: BackupViewModel by activityViewModels( - factoryProducer = { backupViewModelFactory } - ) - private val mainTabsViewModel: MainTabsViewModel by activityViewModels( - factoryProducer = { mainTabsViewModelFactory } - ) + private val backupViewModel: BackupViewModel by activityViewModels { backupViewModelFactory } + private val mainTabsViewModel: MainTabsViewModel by activityViewModels { mainTabsViewModelFactory } private val translatorsAdapter: BaseRecyclerAdapter by lazy { BaseRecyclerAdapter( - createSettingsTranslatorAdapterDelegate() + createSettingsTranslatorAdapterDelegate(), ) } override fun initUi() = with(binding) { layoutSettingsTranslators.rvSettingsTranslators.adapter = translatorsAdapter - with(layoutSettingsMain) { - spinnerSettingsDarkMode.setProcessSameItemSelection(false) - } - with(layoutSettingsDisplay) { - spinnerSettingsDaysInCalendar.setProcessSameItemSelection(false) - spinnerSettingsRecordTypeSort.setProcessSameItemSelection(false) - } - with(layoutSettingsAdditional) { - spinnerSettingsFirstDayOfWeek.setProcessSameItemSelection(false) - } + layoutSettingsMain.spinnerSettingsDarkMode.setProcessSameItemSelection(false) + layoutSettingsDisplay.spinnerSettingsDaysInCalendar.setProcessSameItemSelection(false) + layoutSettingsDisplay.spinnerSettingsRecordTypeSort.setProcessSameItemSelection(false) + layoutSettingsAdditional.spinnerSettingsFirstDayOfWeek.setProcessSameItemSelection(false) } override fun initUx() = with(binding) { with(layoutSettingsMain) { - checkboxSettingsAllowMultitasking.setOnClick(viewModel::onAllowMultitaskingClicked) - spinnerSettingsDarkMode.onPositionSelected = viewModel::onDarkModeSelected - spinnerSettingsLanguage.onPositionSelected = viewModel::onLanguageSelected - layoutSettingsEditCategories.setOnClick(throttle(viewModel::onEditCategoriesClick)) - tvSettingsArchive.setOnClick(throttle(viewModel::onArchiveClick)) - tvSettingsDataEdit.setOnClick(throttle(viewModel::onDataEditClick)) + with(viewModel.mainDelegate) { + checkboxSettingsAllowMultitasking.setOnClick(::onAllowMultitaskingClicked) + spinnerSettingsDarkMode.onPositionSelected = ::onDarkModeSelected + spinnerSettingsLanguage.onPositionSelected = ::onLanguageSelected + layoutSettingsEditCategories.setOnClick(throttle(::onEditCategoriesClick)) + tvSettingsArchive.setOnClick(throttle(::onArchiveClick)) + tvSettingsDataEdit.setOnClick(throttle(::onDataEditClick)) + } } + with(layoutSettingsNotifications) { - layoutSettingsNotificationsTitle.setOnClick(viewModel::onSettingsNotificationsClick) - checkboxSettingsShowNotifications.setOnClick(viewModel::onShowNotificationsClicked) - checkboxSettingsShowNotificationsControls.setOnClick(viewModel::onShowNotificationsControlsClicked) - groupSettingsInactivityReminder.setOnClick(viewModel::onInactivityReminderClicked) - checkboxSettingsInactivityReminderRecurrent.setOnClick(viewModel::onInactivityReminderRecurrentClicked) - tvSettingsInactivityReminderDndStart.setOnClick(viewModel::onInactivityReminderDoNotDisturbStartClicked) - tvSettingsInactivityReminderDndEnd.setOnClick(viewModel::onInactivityReminderDoNotDisturbEndClicked) - groupSettingsActivityReminder.setOnClick(viewModel::onActivityReminderClicked) - checkboxSettingsActivityReminderRecurrent.setOnClick(viewModel::onActivityReminderRecurrentClicked) - tvSettingsActivityReminderDndStart.setOnClick(viewModel::onActivityReminderDoNotDisturbStartClicked) - tvSettingsActivityReminderDndEnd.setOnClick(viewModel::onActivityReminderDoNotDisturbEndClicked) + with(viewModel.notificationsDelegate) { + layoutSettingsNotificationsTitle.setOnClick(viewModel::onSettingsNotificationsClick) + checkboxSettingsShowNotifications.setOnClick(::onShowNotificationsClicked) + checkboxSettingsShowNotificationsControls.setOnClick(::onShowNotificationsControlsClicked) + groupSettingsInactivityReminder.setOnClick(::onInactivityReminderClicked) + checkboxSettingsInactivityReminderRecurrent.setOnClick(::onInactivityReminderRecurrentClicked) + tvSettingsInactivityReminderDndStart.setOnClick(::onInactivityReminderDoNotDisturbStartClicked) + tvSettingsInactivityReminderDndEnd.setOnClick(::onInactivityReminderDoNotDisturbEndClicked) + groupSettingsActivityReminder.setOnClick(::onActivityReminderClicked) + checkboxSettingsActivityReminderRecurrent.setOnClick(::onActivityReminderRecurrentClicked) + tvSettingsActivityReminderDndStart.setOnClick(::onActivityReminderDoNotDisturbStartClicked) + tvSettingsActivityReminderDndEnd.setOnClick(::onActivityReminderDoNotDisturbEndClicked) + } } + with(layoutSettingsDisplay) { - layoutSettingsDisplayTitle.setOnClick(viewModel::onSettingsDisplayClick) - spinnerSettingsDaysInCalendar.onPositionSelected = viewModel::onDaysInCalendarSelected - spinnerSettingsRecordTypeSort.onPositionSelected = viewModel::onRecordTypeOrderSelected - btnCardOrderManual.setOnClick(viewModel::onCardOrderManualClick) - checkboxSettingsShowUntrackedInRecords.setOnClick(viewModel::onShowUntrackedInRecordsClicked) - checkboxSettingsShowUntrackedInStatistics.setOnClick(viewModel::onShowUntrackedInStatisticsClicked) - groupSettingsIgnoreShortUntracked.setOnClick(viewModel::onIgnoreShortUntrackedClicked) - checkboxSettingsUntrackedRange.setOnClick(viewModel::onUntrackedRangeClicked) - tvSettingsUntrackedRangeStart.setOnClick(viewModel::onUntrackedRangeStartClicked) - tvSettingsUntrackedRangeEnd.setOnClick(viewModel::onUntrackedRangeEndClicked) - checkboxSettingsShowRecordsCalendar.setOnClick(viewModel::onShowRecordsCalendarClicked) - checkboxSettingsReverseOrderInCalendar.setOnClick(viewModel::onReverseOrderInCalendarClicked) - checkboxSettingsShowActivityFilters.setOnClick(viewModel::onShowActivityFiltersClicked) - checkboxSettingsShowGoalsSeparately.setOnClick(viewModel::onShowGoalsSeparatelyClicked) - checkboxSettingsUseMilitaryTime.setOnClick(viewModel::onUseMilitaryTimeClicked) - checkboxSettingsUseMonthDayTime.setOnClick(viewModel::onUseMonthDayTimeClicked) - checkboxSettingsUseProportionalMinutes.setOnClick(viewModel::onUseProportionalMinutesClicked) - checkboxSettingsShowSeconds.setOnClick(viewModel::onShowSecondsClicked) - checkboxSettingsKeepScreenOn.setOnClick(viewModel::onKeepScreenOnClicked) - tvSettingsChangeCardSize.setOnClick(viewModel::onChangeCardSizeClick) + with(viewModel.displayDelegate) { + layoutSettingsDisplayTitle.setOnClick(viewModel::onSettingsDisplayClick) + spinnerSettingsDaysInCalendar.onPositionSelected = ::onDaysInCalendarSelected + spinnerSettingsRecordTypeSort.onPositionSelected = ::onRecordTypeOrderSelected + btnCardOrderManual.setOnClick(::onCardOrderManualClick) + checkboxSettingsShowUntrackedInRecords.setOnClick(::onShowUntrackedInRecordsClicked) + checkboxSettingsShowUntrackedInStatistics.setOnClick(::onShowUntrackedInStatisticsClicked) + groupSettingsIgnoreShortUntracked.setOnClick(::onIgnoreShortUntrackedClicked) + checkboxSettingsUntrackedRange.setOnClick(::onUntrackedRangeClicked) + tvSettingsUntrackedRangeStart.setOnClick(::onUntrackedRangeStartClicked) + tvSettingsUntrackedRangeEnd.setOnClick(::onUntrackedRangeEndClicked) + checkboxSettingsShowRecordsCalendar.setOnClick(::onShowRecordsCalendarClicked) + checkboxSettingsReverseOrderInCalendar.setOnClick(::onReverseOrderInCalendarClicked) + checkboxSettingsShowActivityFilters.setOnClick(::onShowActivityFiltersClicked) + checkboxSettingsShowGoalsSeparately.setOnClick(::onShowGoalsSeparatelyClicked) + checkboxSettingsUseMilitaryTime.setOnClick(::onUseMilitaryTimeClicked) + checkboxSettingsUseMonthDayTime.setOnClick(::onUseMonthDayTimeClicked) + checkboxSettingsUseProportionalMinutes.setOnClick(::onUseProportionalMinutesClicked) + checkboxSettingsShowSeconds.setOnClick(::onShowSecondsClicked) + checkboxSettingsKeepScreenOn.setOnClick(::onKeepScreenOnClicked) + tvSettingsChangeCardSize.setOnClick(::onChangeCardSizeClick) + } } + with(layoutSettingsAdditional) { - layoutSettingsAdditionalTitle.setOnClick(viewModel::onSettingsAdditionalClick) - spinnerSettingsFirstDayOfWeek.onPositionSelected = viewModel::onFirstDayOfWeekSelected - groupSettingsStartOfDay.setOnClick(viewModel::onStartOfDayClicked) - btnSettingsStartOfDaySign.setOnClick(viewModel::onStartOfDaySignClicked) - checkboxSettingsKeepStatisticsRange.setOnClick(viewModel::onKeepStatisticsRangeClicked) - groupSettingsIgnoreShortRecords.setOnClick(viewModel::onIgnoreShortRecordsClicked) - checkboxSettingsShowRecordTagSelection.setOnClick(viewModel::onShowRecordTagSelectionClicked) - checkboxSettingsRecordTagSelectionClose.setOnClick(viewModel::onRecordTagSelectionCloseClicked) - checkboxSettingsRecordTagSelectionGeneral.setOnClick(viewModel::onRecordTagSelectionGeneralClicked) - checkboxSettingsAutomatedTrackingSend.setOnClick(viewModel::onAutomatedTrackingSendEventsClicked) - btnSettingsAutomatedTracking.setOnClick(viewModel::onAutomatedTrackingHelpClick) + with(viewModel.additionalDelegate) { + layoutSettingsAdditionalTitle.setOnClick(viewModel::onSettingsAdditionalClick) + spinnerSettingsFirstDayOfWeek.onPositionSelected = ::onFirstDayOfWeekSelected + groupSettingsStartOfDay.setOnClick(::onStartOfDayClicked) + btnSettingsStartOfDaySign.setOnClick(::onStartOfDaySignClicked) + checkboxSettingsKeepStatisticsRange.setOnClick(::onKeepStatisticsRangeClicked) + groupSettingsIgnoreShortRecords.setOnClick(::onIgnoreShortRecordsClicked) + checkboxSettingsShowRecordTagSelection.setOnClick(::onShowRecordTagSelectionClicked) + checkboxSettingsRecordTagSelectionClose.setOnClick(::onRecordTagSelectionCloseClicked) + checkboxSettingsRecordTagSelectionGeneral.setOnClick(::onRecordTagSelectionGeneralClicked) + checkboxSettingsAutomatedTrackingSend.setOnClick(::onAutomatedTrackingSendEventsClicked) + btnSettingsAutomatedTracking.setOnClick(::onAutomatedTrackingHelpClick) + } } + with(layoutSettingsRating) { - layoutSettingsRate.setOnClick(viewModel::onRateClick) - layoutSettingsFeedback.setOnClick(viewModel::onFeedbackClick) + with(viewModel.ratingDelegate) { + layoutSettingsRate.setOnClick(::onRateClick) + layoutSettingsFeedback.setOnClick(::onFeedbackClick) + } } + with(layoutSettingsBackup) { layoutSettingsBackupTitle.setOnClick(viewModel::onSettingsBackupClick) layoutSettingsSaveBackup.setOnClick(backupViewModel::onSaveClick) @@ -155,16 +160,43 @@ override fun initViewModel(): Unit = with(binding) { with(viewModel) { + settingsNotificationsVisibility.observe { opened -> + layoutSettingsNotifications.layoutSettingsNotificationsContent.visible = opened + layoutSettingsNotifications.arrowSettingsNotifications + .apply { if (opened) rotateDown() else rotateUp() } + } + viewModel.settingsDisplayVisibility.observe { opened -> + layoutSettingsDisplay.layoutSettingsDisplayContent.visible = opened + layoutSettingsDisplay.arrowSettingsDisplay + .apply { if (opened) rotateDown() else rotateUp() } + } + viewModel.settingsAdditionalVisibility.observe { opened -> + layoutSettingsAdditional.layoutSettingsAdditionalContent.visible = opened + layoutSettingsAdditional.arrowSettingsAdditional + .apply { if (opened) rotateDown() else rotateUp() } + } + viewModel.settingsBackupVisibility.observe { opened -> + layoutSettingsBackup.layoutSettingsBackupContent.visible = opened + layoutSettingsBackup.arrowSettingsBackup + .apply { if (opened) rotateDown() else rotateUp() } + } + resetScreen.observe { + containerSettings.smoothScrollTo(0, 0) + mainTabsViewModel.onHandled() + } + } + + with(viewModel.mainDelegate) { with(layoutSettingsMain) { allowMultitaskingCheckbox.observe(checkboxSettingsAllowMultitasking::setChecked) darkModeViewData.observe(::updateDarkModeViewData) languageViewData.observe(::updateLanguageViewData) + themeChanged.observe(::changeTheme) } + } + + with(viewModel.notificationsDelegate) { with(layoutSettingsNotifications) { - settingsNotificationsVisibility.observe { opened -> - layoutSettingsNotificationsContent.visible = opened - arrowSettingsNotifications.apply { if (opened) rotateDown() else rotateUp() } - } showNotificationsCheckbox.observe(::updateShowNotifications) showNotificationsControlsCheckbox.observe(checkboxSettingsShowNotificationsControls::setChecked) inactivityReminderViewData.observe(::updateInactivityReminder) @@ -176,11 +208,10 @@ activityReminderDndStartViewData.observe(tvSettingsActivityReminderDndStart::setText) activityReminderDndEndViewData.observe(tvSettingsActivityReminderDndEnd::setText) } + } + + with(viewModel.displayDelegate) { with(layoutSettingsDisplay) { - settingsDisplayVisibility.observe { opened -> - layoutSettingsDisplayContent.visible = opened - arrowSettingsDisplay.apply { if (opened) rotateDown() else rotateUp() } - } btnCardOrderManualVisibility.observe(btnCardOrderManual::visible::set) showUntrackedInRecordsCheckbox.observe(checkboxSettingsShowUntrackedInRecords::setChecked) showUntrackedInStatisticsCheckbox.observe(checkboxSettingsShowUntrackedInStatistics::setChecked) @@ -201,11 +232,10 @@ cardOrderViewData.observe(::updateCardOrderViewData) keepScreenOnCheckbox.observe(::setKeepScreenOn) } + } + + with(viewModel.additionalDelegate) { with(layoutSettingsAdditional) { - settingsAdditionalVisibility.observe { opened -> - layoutSettingsAdditionalContent.visible = opened - arrowSettingsAdditional.apply { if (opened) rotateDown() else rotateUp() } - } keepStatisticsRangeCheckbox.observe(checkboxSettingsKeepStatisticsRange::setChecked) ignoreShortRecordsViewData.observe(tvSettingsIgnoreShortRecordsTime::setText) recordTagSelectionCloseCheckbox.observe(checkboxSettingsRecordTagSelectionClose::setChecked) @@ -215,22 +245,20 @@ startOfDayViewData.observe(::updateStartOfDayViewData) showRecordTagSelectionCheckbox.observe(::updateShowRecordTagSelectionChecked) } + } + + with(viewModel.ratingDelegate) { with(layoutSettingsRating) { versionName.observe(tvSettingsVersionName::setText) } - translatorsViewData.observe(translatorsAdapter::replaceAsNew) - themeChanged.observe(::changeTheme) - resetScreen.observe { - containerSettings.smoothScrollTo(0, 0) - mainTabsViewModel.onHandled() - } } + + with(viewModel.translatorsDelegate) { + translatorsViewData.observe(translatorsAdapter::replaceAsNew) + } + with(backupViewModel) { with(layoutSettingsBackup) { - viewModel.settingsBackupVisibility.observe { opened -> - layoutSettingsBackupContent.visible = opened - arrowSettingsBackup.apply { if (opened) rotateDown() else rotateUp() } - } automaticBackupCheckbox.observe(checkboxSettingsAutomaticBackup::setChecked) automaticBackupLastSaveTime.observe { tvSettingsAutomaticBackupLastSaveTime.visible = it.isNotEmpty() @@ -243,6 +271,7 @@ } } } + with(mainTabsViewModel) { tabReselected.observe(viewModel::onTabReselected) } diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt @@ -2,195 +2,74 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.util.simpletimetracker.core.BuildConfig +import com.example.util.simpletimetracker.core.base.BaseViewModel import com.example.util.simpletimetracker.core.base.SingleLiveEvent -import com.example.util.simpletimetracker.core.extension.lazySuspend import com.example.util.simpletimetracker.core.extension.set -import com.example.util.simpletimetracker.core.interactor.CheckExactAlarmPermissionInteractor -import com.example.util.simpletimetracker.core.interactor.CheckNotificationsPermissionInteractor -import com.example.util.simpletimetracker.core.interactor.LanguageInteractor -import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.model.NavigationTab -import com.example.util.simpletimetracker.core.provider.ApplicationDataProvider -import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.extension.flip import com.example.util.simpletimetracker.domain.extension.orFalse -import com.example.util.simpletimetracker.domain.interactor.NotificationActivityInteractor -import com.example.util.simpletimetracker.domain.interactor.NotificationGoalTimeInteractor -import com.example.util.simpletimetracker.domain.interactor.NotificationInactivityInteractor -import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInteractor -import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor -import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor -import com.example.util.simpletimetracker.domain.model.CardOrder -import com.example.util.simpletimetracker.domain.model.WidgetType -import com.example.util.simpletimetracker.feature_settings.R -import com.example.util.simpletimetracker.feature_settings.adapter.SettingsTranslatorViewData import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper -import com.example.util.simpletimetracker.feature_settings.viewData.CardOrderViewData -import com.example.util.simpletimetracker.feature_settings.viewData.DarkModeViewData -import com.example.util.simpletimetracker.feature_settings.viewData.DaysInCalendarViewData -import com.example.util.simpletimetracker.feature_settings.viewData.FirstDayOfWeekViewData -import com.example.util.simpletimetracker.feature_settings.viewData.LanguageViewData -import com.example.util.simpletimetracker.feature_settings.viewData.SettingsDurationViewData -import com.example.util.simpletimetracker.feature_settings.viewData.SettingsStartOfDayViewData -import com.example.util.simpletimetracker.feature_settings.viewData.SettingsUntrackedRangeViewData +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsAdditionalViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsDisplayViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsMainViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsNotificationsViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsParent +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsRatingViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsTranslatorsViewModelDelegate import com.example.util.simpletimetracker.navigation.Router -import com.example.util.simpletimetracker.navigation.params.action.OpenMarketParams -import com.example.util.simpletimetracker.navigation.params.action.SendEmailParams -import com.example.util.simpletimetracker.navigation.params.notification.SnackBarParams import com.example.util.simpletimetracker.navigation.params.screen.ArchiveParams -import com.example.util.simpletimetracker.navigation.params.screen.CardOrderDialogParams -import com.example.util.simpletimetracker.navigation.params.screen.CardSizeDialogParams import com.example.util.simpletimetracker.navigation.params.screen.CategoriesParams import com.example.util.simpletimetracker.navigation.params.screen.DataEditParams import com.example.util.simpletimetracker.navigation.params.screen.DateTimeDialogParams import com.example.util.simpletimetracker.navigation.params.screen.DateTimeDialogType -import com.example.util.simpletimetracker.navigation.params.screen.DurationDialogParams import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.coroutines.launch +import javax.inject.Inject @HiltViewModel class SettingsViewModel @Inject constructor( + val mainDelegate: SettingsMainViewModelDelegate, + val notificationsDelegate: SettingsNotificationsViewModelDelegate, + val displayDelegate: SettingsDisplayViewModelDelegate, + val additionalDelegate: SettingsAdditionalViewModelDelegate, + val ratingDelegate: SettingsRatingViewModelDelegate, + val translatorsDelegate: SettingsTranslatorsViewModelDelegate, private val router: Router, - private val timeMapper: TimeMapper, - private val resourceRepo: ResourceRepo, - private val prefsInteractor: PrefsInteractor, - private val languageInteractor: LanguageInteractor, private val settingsMapper: SettingsMapper, - private val applicationDataProvider: ApplicationDataProvider, - private val notificationTypeInteractor: NotificationTypeInteractor, - private val widgetInteractor: WidgetInteractor, - private val notificationInactivityInteractor: NotificationInactivityInteractor, - private val notificationActivityInteractor: NotificationActivityInteractor, - private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor, - private val checkExactAlarmPermissionInteractor: CheckExactAlarmPermissionInteractor, - private val checkNotificationsPermissionInteractor: CheckNotificationsPermissionInteractor, -) : ViewModel() { +) : BaseViewModel(), SettingsParent { - val daysInCalendarViewData: LiveData - by lazySuspend { loadDaysInCalendarViewData() } - val cardOrderViewData: LiveData - by lazySuspend { loadCardOrderViewData() } - val btnCardOrderManualVisibility: LiveData - by lazySuspend { prefsInteractor.getCardOrder() == CardOrder.MANUAL } - val firstDayOfWeekViewData: LiveData - by lazySuspend { loadFirstDayOfWeekViewData() } - val showUntrackedInRecordsCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowUntrackedInRecords() } - val showUntrackedInStatisticsCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowUntrackedInStatistics() } - val untrackedRangeViewData: LiveData - by lazySuspend { loadUntrackedRangeViewData() } - val showRecordsCalendarCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowRecordsCalendar() } - val reverseOrderInCalendarCheckbox: LiveData - by lazySuspend { prefsInteractor.getReverseOrderInCalendar() } - val showActivityFiltersCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowActivityFilters() } - val showGoalsSeparatelyCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowGoalsSeparately() } - val allowMultitaskingCheckbox: LiveData - by lazySuspend { prefsInteractor.getAllowMultitasking() } - val showNotificationsCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowNotifications() } - val showNotificationsControlsCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowNotificationsControls() } - val keepStatisticsRangeCheckbox: LiveData - by lazySuspend { prefsInteractor.getKeepStatisticsRange() } - val startOfDayViewData: LiveData - by lazySuspend { loadStartOfDayViewData() } - val inactivityReminderViewData: LiveData - by lazySuspend { loadInactivityReminderViewData() } - val inactivityReminderRecurrentCheckbox: LiveData - by lazySuspend { prefsInteractor.getInactivityReminderRecurrent() } - val inactivityReminderDndStartViewData: LiveData - by lazySuspend { loadInactivityReminderDndStartViewData() } - val inactivityReminderDndEndViewData: LiveData - by lazySuspend { loadInactivityReminderDndEndViewData() } - val activityReminderViewData: LiveData - by lazySuspend { loadActivityReminderViewData() } - val activityReminderRecurrentCheckbox: LiveData - by lazySuspend { prefsInteractor.getActivityReminderRecurrent() } - val activityReminderDndStartViewData: LiveData - by lazySuspend { loadActivityReminderDndStartViewData() } - val activityReminderDndEndViewData: LiveData - by lazySuspend { loadActivityReminderDndEndViewData() } - val ignoreShortRecordsViewData: LiveData - by lazySuspend { loadIgnoreShortRecordsViewData() } - val ignoreShortUntrackedViewData: LiveData - by lazySuspend { loadIgnoreShortUntrackedViewData() } - val darkModeViewData: LiveData - by lazySuspend { loadDarkModeViewData() } - val languageViewData: LiveData - by lazySuspend { loadLanguageViewData() } - val showRecordTagSelectionCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowRecordTagSelection() } - val recordTagSelectionCloseCheckbox: LiveData - by lazySuspend { prefsInteractor.getRecordTagSelectionCloseAfterOne() } - val recordTagSelectionForGeneralTagsCheckbox: LiveData - by lazySuspend { prefsInteractor.getRecordTagSelectionEvenForGeneralTags() } - val automatedTrackingSendEventsCheckbox: LiveData - by lazySuspend { prefsInteractor.getAutomatedTrackingSendEvents() } - val useMilitaryTimeCheckbox: LiveData - by lazySuspend { prefsInteractor.getUseMilitaryTimeFormat() } - val useMilitaryTimeHint: LiveData - by lazySuspend { loadUseMilitaryTimeViewData() } - val useMonthDayTimeCheckbox: LiveData by lazy { - MutableLiveData().let { initial -> - viewModelScope.launch { - initial.value = prefsInteractor.getUseMonthDayTimeFormat() - } - initial - } - } - - val useMonthDayTimeHint: LiveData by lazy { - MutableLiveData().let { initial -> - viewModelScope.launch { - initial.value = loadUseMonthDayTimeViewData() - } - initial - } - } - - val useProportionalMinutesCheckbox: LiveData - by lazySuspend { prefsInteractor.getUseProportionalMinutes() } - val showSecondsCheckbox: LiveData - by lazySuspend { prefsInteractor.getShowSeconds() } - val keepScreenOnCheckbox: LiveData - by lazySuspend { prefsInteractor.getKeepScreenOn() } - val useProportionalMinutesHint: LiveData - by lazySuspend { loadUseProportionalMinutesViewData() } - val translatorsViewData: LiveData> - by lazy { MutableLiveData(loadTranslatorsViewData()) } - val versionName: LiveData - by lazy { MutableLiveData(loadVersionName()) } - - val themeChanged: SingleLiveEvent = SingleLiveEvent() val settingsNotificationsVisibility: LiveData = MutableLiveData(false) val settingsDisplayVisibility: LiveData = MutableLiveData(false) val settingsAdditionalVisibility: LiveData = MutableLiveData(false) val settingsBackupVisibility: LiveData = MutableLiveData(false) val resetScreen: SingleLiveEvent = SingleLiveEvent() + init { + notificationsDelegate.init(this) + displayDelegate.init(this) + additionalDelegate.init(this) + } + + override fun onCleared() { + mainDelegate.clear() + notificationsDelegate.clear() + displayDelegate.clear() + additionalDelegate.clear() + ratingDelegate.clear() + translatorsDelegate.clear() + super.onCleared() + } + + override suspend fun onUseMilitaryTimeClicked() { + additionalDelegate.onUseMilitaryTimeClicked() + notificationsDelegate.onUseMilitaryTimeClicked() + } + fun onVisible() { - viewModelScope.launch { - // Need to update card order because it changes on card order dialog - updateCardOrderViewData() - - // Update can come from quick settings widget - allowMultitaskingCheckbox.set(prefsInteractor.getAllowMultitasking()) - showRecordTagSelectionCheckbox.set(prefsInteractor.getShowRecordTagSelection()) - - // Update can come from system settings - updateLanguageViewData() - - // Update after day changes - updateStartOfDayViewData() - } + mainDelegate.onVisible() + displayDelegate.onVisible() + additionalDelegate.onVisible() } fun onSettingsNotificationsClick() { @@ -213,560 +92,43 @@ settingsBackupVisibility.set(newValue) } - fun onRateClick() { - router.execute(OpenMarketParams(packageName = applicationDataProvider.getPackageName())) - } - - fun onFeedbackClick() { - router.execute( - data = SendEmailParams( - email = resourceRepo.getString(R.string.support_email), - subject = resourceRepo.getString(R.string.support_email_subject), - chooserTitle = resourceRepo.getString(R.string.settings_email_chooser_title), - notHandledCallback = { R.string.message_app_not_found.let(::showMessage) }, - ), - ) - } - - fun onDaysInCalendarSelected(position: Int) { - viewModelScope.launch { - val currentValue = prefsInteractor.getDaysInCalendar() - val newValue = settingsMapper.toDaysInCalendar(position) - if (newValue == currentValue) return@launch - prefsInteractor.setDaysInCalendar(newValue) - updateDaysInCalendarViewData() - } - } - - fun onRecordTypeOrderSelected(position: Int) { - viewModelScope.launch { - val currentOrder = prefsInteractor.getCardOrder() - val newOrder = settingsMapper.toCardOrder(position) - if (newOrder == currentOrder) return@launch - - btnCardOrderManualVisibility.set(newOrder == CardOrder.MANUAL) - if (newOrder == CardOrder.MANUAL) { - openCardOrderDialog(currentOrder) - } - prefsInteractor.setCardOrder(newOrder) - updateCardOrderViewData() - } - } - - fun onCardOrderManualClick() { - openCardOrderDialog(CardOrder.MANUAL) - } - - fun onFirstDayOfWeekSelected(position: Int) { - val newDayOfWeek = settingsMapper.toDayOfWeek(position) - - viewModelScope.launch { - prefsInteractor.setFirstDayOfWeek(newDayOfWeek) - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - notificationGoalTimeInteractor.checkAndReschedule() - updateFirstDayOfWeekViewData() - } - } - - fun onStartOfDayClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = START_OF_DAY_DIALOG_TAG, - timestamp = prefsInteractor.getStartOfDayShift(), - useMilitaryTime = true, - ) - } - } - - fun onStartOfDaySignClicked() { - viewModelScope.launch { - val newValue = prefsInteractor.getStartOfDayShift() * -1 - prefsInteractor.setStartOfDayShift(newValue) - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - notificationTypeInteractor.updateNotifications() - notificationGoalTimeInteractor.checkAndReschedule() - updateStartOfDayViewData() - } - } - - fun onShowUntrackedInRecordsClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowUntrackedInRecords() - prefsInteractor.setShowUntrackedInRecords(newValue) - showUntrackedInRecordsCheckbox.set(newValue) - } - } - - fun onShowUntrackedInStatisticsClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowUntrackedInStatistics() - prefsInteractor.setShowUntrackedInStatistics(newValue) - showUntrackedInStatisticsCheckbox.set(newValue) - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - } - } - - fun onUntrackedRangeClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getUntrackedRangeEnabled() - prefsInteractor.setUntrackedRangeEnabled(newValue) - updateUntrackedRangeViewData() - } - } - - fun onUntrackedRangeStartClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = UNTRACKED_RANGE_START_DIALOG_TAG, - timestamp = prefsInteractor.getUntrackedRangeStart(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onUntrackedRangeEndClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = UNTRACKED_RANGE_END_DIALOG_TAG, - timestamp = prefsInteractor.getUntrackedRangeEnd(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onShowRecordsCalendarClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowRecordsCalendar() - prefsInteractor.setShowRecordsCalendar(newValue) - (showRecordsCalendarCheckbox as MutableLiveData).value = newValue - } - } - - fun onReverseOrderInCalendarClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getReverseOrderInCalendar() - prefsInteractor.setReverseOrderInCalendar(newValue) - (reverseOrderInCalendarCheckbox as MutableLiveData).value = newValue - } - } - - fun onShowActivityFiltersClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowActivityFilters() - prefsInteractor.setShowActivityFilters(newValue) - (showActivityFiltersCheckbox as MutableLiveData).value = newValue - } - } - - fun onShowGoalsSeparatelyClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowGoalsSeparately() - prefsInteractor.setShowGoalsSeparately(newValue) - (showGoalsSeparatelyCheckbox as MutableLiveData).value = newValue - router.restartApp() - } - } - - fun onAllowMultitaskingClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getAllowMultitasking() - prefsInteractor.setAllowMultitasking(newValue) - widgetInteractor.updateWidgets(listOf(WidgetType.QUICK_SETTINGS)) - allowMultitaskingCheckbox.set(newValue) - } - } - - fun onShowNotificationsClicked() { - fun updateValue(newValue: Boolean) = viewModelScope.launch { - prefsInteractor.setShowNotifications(newValue) - showNotificationsCheckbox.set(newValue) - notificationTypeInteractor.updateNotifications() - } - - viewModelScope.launch { - if (prefsInteractor.getShowNotifications()) { - updateValue(false) - } else { - checkNotificationsPermissionInteractor.execute( - onEnabled = { updateValue(true) }, - onDisabled = { updateValue(false) }, - ) - } - } - } - - fun onShowNotificationsControlsClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowNotificationsControls() - prefsInteractor.setShowNotificationsControls(newValue) - showNotificationsControlsCheckbox.set(newValue) - notificationTypeInteractor.updateNotifications() - } - } - - fun onKeepStatisticsRangeClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getKeepStatisticsRange() - prefsInteractor.setKeepStatisticsRange(newValue) - keepStatisticsRangeCheckbox.set(newValue) - } - } - - fun onInactivityReminderClicked() = viewModelScope.launch { - val duration = prefsInteractor.getInactivityReminderDuration() - - fun openDialog() { - DurationDialogParams( - tag = INACTIVITY_DURATION_DIALOG_TAG, - duration = duration, - ).let(router::navigate) - } - - if (duration > 0) { - openDialog() - } else { - checkNotificationsPermissionInteractor.execute( - onEnabled = ::openDialog, - ) - } - } - - fun onInactivityReminderRecurrentClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getInactivityReminderRecurrent() - prefsInteractor.setInactivityReminderRecurrent(newValue) - inactivityReminderRecurrentCheckbox.set(newValue) - notificationInactivityInteractor.cancel() - notificationInactivityInteractor.checkAndSchedule() - } - } - - fun onInactivityReminderDoNotDisturbStartClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = INACTIVITY_REMINDER_DND_START_DIALOG_TAG, - timestamp = prefsInteractor.getInactivityReminderDoNotDisturbStart(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onInactivityReminderDoNotDisturbEndClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = INACTIVITY_REMINDER_DND_END_DIALOG_TAG, - timestamp = prefsInteractor.getInactivityReminderDoNotDisturbEnd(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onActivityReminderClicked() = viewModelScope.launch { - val duration = prefsInteractor.getActivityReminderDuration() - - fun openDialog() { - DurationDialogParams( - tag = ACTIVITY_DURATION_DIALOG_TAG, - duration = duration, - ).let(router::navigate) - } - - if (duration > 0) { - openDialog() - } else { - checkNotificationsPermissionInteractor.execute(onEnabled = ::openDialog) - } - } - - fun onActivityReminderRecurrentClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getActivityReminderRecurrent() - prefsInteractor.setActivityReminderRecurrent(newValue) - activityReminderRecurrentCheckbox.set(newValue) - notificationActivityInteractor.cancel() - notificationActivityInteractor.checkAndSchedule() - } - } - - fun onActivityReminderDoNotDisturbStartClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = ACTIVITY_REMINDER_DND_START_DIALOG_TAG, - timestamp = prefsInteractor.getActivityReminderDoNotDisturbStart(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onActivityReminderDoNotDisturbEndClicked() { - viewModelScope.launch { - openDateTimeDialog( - tag = ACTIVITY_REMINDER_DND_END_DIALOG_TAG, - timestamp = prefsInteractor.getActivityReminderDoNotDisturbEnd(), - useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), - ) - } - } - - fun onIgnoreShortRecordsClicked() { - viewModelScope.launch { - DurationDialogParams( - tag = IGNORE_SHORT_RECORDS_DIALOG_TAG, - duration = prefsInteractor.getIgnoreShortRecordsDuration(), - ).let(router::navigate) - } - } - - fun onIgnoreShortUntrackedClicked() { - viewModelScope.launch { - DurationDialogParams( - tag = IGNORE_SHORT_UNTRACKED_DIALOG_TAG, - duration = prefsInteractor.getIgnoreShortUntrackedDuration(), - ).let(router::navigate) - } - } - - fun onDarkModeSelected(position: Int) { - viewModelScope.launch { - val currentMode = prefsInteractor.getSelectedDarkMode() - val newMode = settingsMapper.toDarkMode(position) - if (newMode == currentMode) return@launch - - prefsInteractor.setDarkMode(newMode) - updateDarkModeViewData() - themeChanged.set(true) - } - } - - fun onLanguageSelected(position: Int) { - val newLanguage = settingsMapper.toLanguage(position) - languageInteractor.setLanguage(newLanguage) - updateLanguageViewData() - router.restartApp() - } - - fun onUseMilitaryTimeClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getUseMilitaryTimeFormat() - prefsInteractor.setUseMilitaryTimeFormat(newValue) - (useMilitaryTimeCheckbox as MutableLiveData).value = newValue - notificationTypeInteractor.updateNotifications() - updateUseMilitaryTimeViewData() - - updateStartOfDayViewData() - updateActivityReminderDndStartViewData() - updateActivityReminderDndEndViewData() - updateInactivityReminderDndStartViewData() - updateInactivityReminderDndEndViewData() - updateUntrackedRangeViewData() - } - } - - fun onUseMonthDayTimeClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getUseMonthDayTimeFormat() - prefsInteractor.setUseMonthDayTimeFormat(newValue) - (useMonthDayTimeCheckbox as MutableLiveData).value = newValue - updateUseMonthDayTimeViewData() - } - } - - fun onUseProportionalMinutesClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getUseProportionalMinutes() - prefsInteractor.setUseProportionalMinutes(newValue) - (useProportionalMinutesCheckbox as MutableLiveData).value = newValue - notificationTypeInteractor.updateNotifications() - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - updateUseProportionalMinutesViewData() - } - } - - fun onShowSecondsClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowSeconds() - prefsInteractor.setShowSeconds(newValue) - showSecondsCheckbox.set(newValue) - notificationTypeInteractor.updateNotifications() - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - } - } - - fun onKeepScreenOnClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getKeepScreenOn() - prefsInteractor.setKeepScreenOn(newValue) - (keepScreenOnCheckbox as MutableLiveData).value = newValue - } - } - - fun onShowRecordTagSelectionClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getShowRecordTagSelection() - prefsInteractor.setShowRecordTagSelection(newValue) - widgetInteractor.updateWidgets(listOf(WidgetType.QUICK_SETTINGS)) - showRecordTagSelectionCheckbox.set(newValue) - } - } - - fun onRecordTagSelectionCloseClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getRecordTagSelectionCloseAfterOne() - prefsInteractor.setRecordTagSelectionCloseAfterOne(newValue) - recordTagSelectionCloseCheckbox.set(newValue) - } - } - - fun onRecordTagSelectionGeneralClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getRecordTagSelectionEvenForGeneralTags() - prefsInteractor.setRecordTagSelectionEvenForGeneralTags(newValue) - recordTagSelectionForGeneralTagsCheckbox.set(newValue) - } - } - - fun onAutomatedTrackingSendEventsClicked() { - viewModelScope.launch { - val newValue = !prefsInteractor.getAutomatedTrackingSendEvents() - prefsInteractor.setAutomatedTrackingSendEvents(newValue) - automatedTrackingSendEventsCheckbox.set(newValue) - } - } - - fun onChangeCardSizeClick() { - router.navigate(CardSizeDialogParams) - } - - fun onEditCategoriesClick() { - router.navigate(CategoriesParams) - } - - fun onArchiveClick() { - router.navigate(ArchiveParams) - } - - fun onDataEditClick() { - router.navigate(DataEditParams) - } - fun onDurationSet(tag: String?, duration: Long) { when (tag) { - INACTIVITY_DURATION_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setInactivityReminderDuration(duration) - updateInactivityReminderViewData() - notificationInactivityInteractor.cancel() - notificationInactivityInteractor.checkAndSchedule() - checkExactAlarmPermissionInteractor.execute() - } - - ACTIVITY_DURATION_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setActivityReminderDuration(duration) - updateActivityReminderViewData() - notificationActivityInteractor.cancel() - notificationActivityInteractor.checkAndSchedule() - checkExactAlarmPermissionInteractor.execute() - } - - IGNORE_SHORT_RECORDS_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setIgnoreShortRecordsDuration(duration) - updateIgnoreShortRecordsViewData() - } - - IGNORE_SHORT_UNTRACKED_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setIgnoreShortUntrackedDuration(duration) - updateIgnoreShortUntrackedViewData() - } + INACTIVITY_DURATION_DIALOG_TAG, + ACTIVITY_DURATION_DIALOG_TAG, + -> notificationsDelegate.onDurationSet(tag, duration) + IGNORE_SHORT_RECORDS_DIALOG_TAG, + -> additionalDelegate.onDurationSet(tag, duration) + IGNORE_SHORT_UNTRACKED_DIALOG_TAG, + -> displayDelegate.onDurationSet(tag, duration) } } fun onDurationDisabled(tag: String?) { when (tag) { - INACTIVITY_DURATION_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setInactivityReminderDuration(0) - updateInactivityReminderViewData() - notificationInactivityInteractor.cancel() - } - - ACTIVITY_DURATION_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setActivityReminderDuration(0) - updateActivityReminderViewData() - notificationActivityInteractor.cancel() - } - - IGNORE_SHORT_RECORDS_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setIgnoreShortRecordsDuration(0) - updateIgnoreShortRecordsViewData() - } - - IGNORE_SHORT_UNTRACKED_DIALOG_TAG -> viewModelScope.launch { - prefsInteractor.setIgnoreShortUntrackedDuration(0) - updateIgnoreShortUntrackedViewData() - } + INACTIVITY_DURATION_DIALOG_TAG, + ACTIVITY_DURATION_DIALOG_TAG, + -> notificationsDelegate.onDurationDisabled(tag) + IGNORE_SHORT_RECORDS_DIALOG_TAG, + -> additionalDelegate.onDurationDisabled(tag) + IGNORE_SHORT_UNTRACKED_DIALOG_TAG, + -> displayDelegate.onDurationDisabled(tag) } } fun onDateTimeSet(timestamp: Long, tag: String?) = viewModelScope.launch { when (tag) { - START_OF_DAY_DIALOG_TAG -> { - val wasPositive = prefsInteractor.getStartOfDayShift() >= 0 - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive) - prefsInteractor.setStartOfDayShift(newValue) - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) - notificationTypeInteractor.updateNotifications() - notificationGoalTimeInteractor.checkAndReschedule() - updateStartOfDayViewData() - } - - INACTIVITY_REMINDER_DND_START_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setInactivityReminderDoNotDisturbStart(newValue) - updateInactivityReminderDndStartViewData() - notificationInactivityInteractor.cancel() - notificationInactivityInteractor.checkAndSchedule() - } - - INACTIVITY_REMINDER_DND_END_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setInactivityReminderDoNotDisturbEnd(newValue) - updateInactivityReminderDndEndViewData() - notificationInactivityInteractor.cancel() - notificationInactivityInteractor.checkAndSchedule() - } - - ACTIVITY_REMINDER_DND_START_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setActivityReminderDoNotDisturbStart(newValue) - updateActivityReminderDndStartViewData() - notificationActivityInteractor.cancel() - notificationActivityInteractor.checkAndSchedule() - } - - ACTIVITY_REMINDER_DND_END_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setActivityReminderDoNotDisturbEnd(newValue) - updateActivityReminderDndEndViewData() - notificationActivityInteractor.cancel() - notificationActivityInteractor.checkAndSchedule() - } - - UNTRACKED_RANGE_START_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setUntrackedRangeStart(newValue) - updateUntrackedRangeViewData() - } - - UNTRACKED_RANGE_END_DIALOG_TAG -> { - val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) - prefsInteractor.setUntrackedRangeEnd(newValue) - updateUntrackedRangeViewData() - } + START_OF_DAY_DIALOG_TAG, + -> additionalDelegate.onDateTimeSet(timestamp, tag) + INACTIVITY_REMINDER_DND_START_DIALOG_TAG, + INACTIVITY_REMINDER_DND_END_DIALOG_TAG, + ACTIVITY_REMINDER_DND_START_DIALOG_TAG, + ACTIVITY_REMINDER_DND_END_DIALOG_TAG, + -> notificationsDelegate.onDateTimeSet(timestamp, tag) + UNTRACKED_RANGE_START_DIALOG_TAG, + UNTRACKED_RANGE_END_DIALOG_TAG, + -> displayDelegate.onDateTimeSet(timestamp, tag) } - } - - fun onAutomatedTrackingHelpClick() { - router.navigate( - settingsMapper.toAutomatedTrackingHelpDialog(), - ) } fun onTabReselected(tab: NavigationTab?) { @@ -775,7 +137,7 @@ } } - private fun openDateTimeDialog( + override fun openDateTimeDialog( tag: String, timestamp: Long, useMilitaryTime: Boolean, @@ -788,258 +150,17 @@ ).let(router::navigate) } - private fun openCardOrderDialog(cardOrder: CardOrder) { - router.navigate( - CardOrderDialogParams(cardOrder), - ) - } - - private fun showMessage(stringResId: Int) { - val params = SnackBarParams(message = resourceRepo.getString(stringResId)) - router.show(params) - } - - private suspend fun updateDaysInCalendarViewData() { - val data = loadDaysInCalendarViewData() - daysInCalendarViewData.set(data) - } - - private suspend fun loadDaysInCalendarViewData(): DaysInCalendarViewData { - return prefsInteractor.getDaysInCalendar() - .let(settingsMapper::toDaysInCalendarViewData) - } - - private suspend fun updateCardOrderViewData() { - val data = loadCardOrderViewData() - (cardOrderViewData as MutableLiveData).value = data - } - - private suspend fun loadCardOrderViewData(): CardOrderViewData { - return prefsInteractor.getCardOrder() - .let(settingsMapper::toCardOrderViewData) - } - - private suspend fun updateFirstDayOfWeekViewData() { - val data = loadFirstDayOfWeekViewData() - (firstDayOfWeekViewData as MutableLiveData).value = data - } - - private suspend fun loadFirstDayOfWeekViewData(): FirstDayOfWeekViewData { - return prefsInteractor.getFirstDayOfWeek() - .let(settingsMapper::toFirstDayOfWeekViewData) - } - - private suspend fun updateDarkModeViewData() { - darkModeViewData.set(loadDarkModeViewData()) - } - - private suspend fun loadDarkModeViewData(): DarkModeViewData { - return prefsInteractor.getSelectedDarkMode() - .let(settingsMapper::toDarkModeViewData) - } - - private fun updateLanguageViewData() { - languageViewData.set(loadLanguageViewData()) - } - - private fun loadLanguageViewData(): LanguageViewData { - return languageInteractor.getCurrentLanguage() - .let(settingsMapper::toLanguageViewData) - } - - private suspend fun updateStartOfDayViewData() { - val data = loadStartOfDayViewData() - startOfDayViewData.set(data) - } - - private suspend fun loadStartOfDayViewData(): SettingsStartOfDayViewData { - val shift = prefsInteractor.getStartOfDayShift() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - - val hint = resourceRepo.getString( - R.string.settings_start_of_day_hint_value, - timeMapper.formatDateTime( - time = timeMapper.getStartOfDayTimeStamp() + shift, - useMilitaryTime = useMilitaryTime, - showSeconds = false, - ), - ) - - return SettingsStartOfDayViewData( - startOfDayValue = settingsMapper.toStartOfDayText(shift, useMilitaryTime = true), - startOfDaySign = settingsMapper.toStartOfDaySign(shift), - hint = hint, - ) - } - - private suspend fun updateInactivityReminderViewData() { - val data = loadInactivityReminderViewData() - inactivityReminderViewData.set(data) - } - - private suspend fun loadInactivityReminderViewData(): SettingsDurationViewData { - return prefsInteractor.getInactivityReminderDuration() - .let(settingsMapper::toDurationViewData) - } - - private suspend fun updateInactivityReminderDndStartViewData() { - val data = loadInactivityReminderDndStartViewData() - inactivityReminderDndStartViewData.set(data) - } - - private suspend fun loadInactivityReminderDndStartViewData(): String { - val shift = prefsInteractor.getInactivityReminderDoNotDisturbStart() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - return settingsMapper.toStartOfDayText(shift, useMilitaryTime) - } - - private suspend fun updateInactivityReminderDndEndViewData() { - val data = loadInactivityReminderDndEndViewData() - inactivityReminderDndEndViewData.set(data) - } - - private suspend fun loadInactivityReminderDndEndViewData(): String { - val shift = prefsInteractor.getInactivityReminderDoNotDisturbEnd() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - return settingsMapper.toStartOfDayText(shift, useMilitaryTime) - } - - private suspend fun updateActivityReminderViewData() { - val data = loadActivityReminderViewData() - activityReminderViewData.set(data) - } - - private suspend fun loadActivityReminderViewData(): SettingsDurationViewData { - return prefsInteractor.getActivityReminderDuration() - .let(settingsMapper::toDurationViewData) - } - - private suspend fun updateActivityReminderDndStartViewData() { - val data = loadActivityReminderDndStartViewData() - activityReminderDndStartViewData.set(data) - } - - private suspend fun loadActivityReminderDndStartViewData(): String { - val shift = prefsInteractor.getActivityReminderDoNotDisturbStart() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - return settingsMapper.toStartOfDayText(shift, useMilitaryTime) - } - - private suspend fun updateActivityReminderDndEndViewData() { - val data = loadActivityReminderDndEndViewData() - activityReminderDndEndViewData.set(data) - } - - private suspend fun loadActivityReminderDndEndViewData(): String { - val shift = prefsInteractor.getActivityReminderDoNotDisturbEnd() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - return settingsMapper.toStartOfDayText(shift, useMilitaryTime) - } - - private suspend fun updateIgnoreShortRecordsViewData() { - val data = loadIgnoreShortRecordsViewData() - ignoreShortRecordsViewData.set(data) - } - - private suspend fun loadIgnoreShortRecordsViewData(): String { - return prefsInteractor.getIgnoreShortRecordsDuration() - .let(settingsMapper::toDurationViewData) - .text - } - - private suspend fun updateIgnoreShortUntrackedViewData() { - val data = loadIgnoreShortUntrackedViewData() - ignoreShortUntrackedViewData.set(data) - } - - private suspend fun loadIgnoreShortUntrackedViewData(): String { - return prefsInteractor.getIgnoreShortUntrackedDuration() - .let(settingsMapper::toDurationViewData) - .text - } - - private suspend fun updateUntrackedRangeViewData() { - val data = loadUntrackedRangeViewData() - untrackedRangeViewData.set(data) - } - - private suspend fun loadUntrackedRangeViewData(): SettingsUntrackedRangeViewData { - val enabled = prefsInteractor.getUntrackedRangeEnabled() - - return if (enabled) { - val start = prefsInteractor.getUntrackedRangeStart() - val end = prefsInteractor.getUntrackedRangeEnd() - val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() - SettingsUntrackedRangeViewData.Enabled( - settingsMapper.toStartOfDayText(start, useMilitaryTime), - settingsMapper.toStartOfDayText(end, useMilitaryTime), - ) - } else { - SettingsUntrackedRangeViewData.Disabled - } - } - - private suspend fun updateUseMilitaryTimeViewData() { - val data = loadUseMilitaryTimeViewData() - useMilitaryTimeHint.set(data) - } - - private suspend fun updateUseMonthDayTimeViewData() { - val data = loadUseMonthDayTimeViewData() - useMonthDayTimeHint.set(data) - } - - private suspend fun updateUseProportionalMinutesViewData() { - val data = loadUseProportionalMinutesViewData() - useProportionalMinutesHint.set(data) - } - - private suspend fun loadUseMilitaryTimeViewData(): String { - return prefsInteractor.getUseMilitaryTimeFormat() - .let(settingsMapper::toUseMilitaryTimeHint) - } - - private suspend fun loadUseMonthDayTimeViewData(): String { - return prefsInteractor.getUseMonthDayTimeFormat() - .let(settingsMapper::toUseMonthDayTimeHint) - } - - private suspend fun loadUseProportionalMinutesViewData(): String { - return prefsInteractor.getUseProportionalMinutes() - .let(settingsMapper::toUseProportionalMinutesHint) - } - - private fun loadTranslatorsViewData(): List { - return settingsMapper.mapTranslatorsViewData() - } - - private fun loadVersionName(): String { - return applicationDataProvider.getAppVersion().let { - if (BuildConfig.DEBUG) { - "$it ${BuildConfig.BUILD_TYPE}" - } else { - it - } - } - } - companion object { - private const val INACTIVITY_DURATION_DIALOG_TAG = - "inactivity_duration_dialog_tag" - private const val INACTIVITY_REMINDER_DND_START_DIALOG_TAG = - "inactivity_reminder_dnd_start_dialog_tag" - private const val INACTIVITY_REMINDER_DND_END_DIALOG_TAG = - "inactivity_reminder_dnd_end_dialog_tag" - private const val ACTIVITY_DURATION_DIALOG_TAG = - "activity_duration_dialog_tag" - private const val ACTIVITY_REMINDER_DND_START_DIALOG_TAG = - "activity_reminder_dnd_start_dialog_tag" - private const val ACTIVITY_REMINDER_DND_END_DIALOG_TAG = - "activity_reminder_dnd_end_dialog_tag" - private const val IGNORE_SHORT_RECORDS_DIALOG_TAG = "ignore_short_records_dialog_tag" - private const val IGNORE_SHORT_UNTRACKED_DIALOG_TAG = "ignore_short_untracked_dialog_tag" - private const val UNTRACKED_RANGE_START_DIALOG_TAG = "untracked_range_start_dialog_tag" - private const val UNTRACKED_RANGE_END_DIALOG_TAG = "untracked_range_end_dialog_tag" - private const val START_OF_DAY_DIALOG_TAG = "start_of_day_dialog_tag" + const val INACTIVITY_DURATION_DIALOG_TAG = "inactivity_duration_dialog_tag" + const val INACTIVITY_REMINDER_DND_START_DIALOG_TAG = "inactivity_reminder_dnd_start_dialog_tag" + const val INACTIVITY_REMINDER_DND_END_DIALOG_TAG = "inactivity_reminder_dnd_end_dialog_tag" + const val ACTIVITY_DURATION_DIALOG_TAG = "activity_duration_dialog_tag" + const val ACTIVITY_REMINDER_DND_START_DIALOG_TAG = "activity_reminder_dnd_start_dialog_tag" + const val ACTIVITY_REMINDER_DND_END_DIALOG_TAG = "activity_reminder_dnd_end_dialog_tag" + const val IGNORE_SHORT_RECORDS_DIALOG_TAG = "ignore_short_records_dialog_tag" + const val IGNORE_SHORT_UNTRACKED_DIALOG_TAG = "ignore_short_untracked_dialog_tag" + const val UNTRACKED_RANGE_START_DIALOG_TAG = "untracked_range_start_dialog_tag" + const val UNTRACKED_RANGE_END_DIALOG_TAG = "untracked_range_end_dialog_tag" + const val START_OF_DAY_DIALOG_TAG = "start_of_day_dialog_tag" } } diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt @@ -0,0 +1,239 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.core.extension.lazySuspend +import com.example.util.simpletimetracker.core.extension.set +import com.example.util.simpletimetracker.core.mapper.TimeMapper +import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.interactor.NotificationGoalTimeInteractor +import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInteractor +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor +import com.example.util.simpletimetracker.domain.model.WidgetType +import com.example.util.simpletimetracker.feature_settings.R +import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.FirstDayOfWeekViewData +import com.example.util.simpletimetracker.feature_settings.viewData.SettingsStartOfDayViewData +import com.example.util.simpletimetracker.feature_settings.viewModel.SettingsViewModel +import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.params.screen.DurationDialogParams +import kotlinx.coroutines.launch +import javax.inject.Inject + +class SettingsAdditionalViewModelDelegate @Inject constructor( + private val router: Router, + private val timeMapper: TimeMapper, + private val resourceRepo: ResourceRepo, + private val prefsInteractor: PrefsInteractor, + private val settingsMapper: SettingsMapper, + private val notificationTypeInteractor: NotificationTypeInteractor, + private val widgetInteractor: WidgetInteractor, + private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor, +) : ViewModelDelegate() { + + val firstDayOfWeekViewData: LiveData + by lazySuspend { loadFirstDayOfWeekViewData() } + val keepStatisticsRangeCheckbox: LiveData + by lazySuspend { prefsInteractor.getKeepStatisticsRange() } + val startOfDayViewData: LiveData + by lazySuspend { loadStartOfDayViewData() } + val ignoreShortRecordsViewData: LiveData + by lazySuspend { loadIgnoreShortRecordsViewData() } + val showRecordTagSelectionCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowRecordTagSelection() } + val recordTagSelectionCloseCheckbox: LiveData + by lazySuspend { prefsInteractor.getRecordTagSelectionCloseAfterOne() } + val recordTagSelectionForGeneralTagsCheckbox: LiveData + by lazySuspend { prefsInteractor.getRecordTagSelectionEvenForGeneralTags() } + val automatedTrackingSendEventsCheckbox: LiveData + by lazySuspend { prefsInteractor.getAutomatedTrackingSendEvents() } + + private var parent: SettingsParent? = null + + fun init(parent: SettingsParent) { + this.parent = parent + } + + fun onVisible() { + delegateScope.launch { + // Update can come from quick settings widget + showRecordTagSelectionCheckbox.set(prefsInteractor.getShowRecordTagSelection()) + + // Update after day changes + updateStartOfDayViewData() + } + } + + suspend fun onUseMilitaryTimeClicked() { + updateStartOfDayViewData() + } + + fun onFirstDayOfWeekSelected(position: Int) { + val newDayOfWeek = settingsMapper.toDayOfWeek(position) + + delegateScope.launch { + prefsInteractor.setFirstDayOfWeek(newDayOfWeek) + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + notificationGoalTimeInteractor.checkAndReschedule() + updateFirstDayOfWeekViewData() + } + } + + fun onStartOfDayClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.START_OF_DAY_DIALOG_TAG, + timestamp = prefsInteractor.getStartOfDayShift(), + useMilitaryTime = true, + ) + } + } + + fun onStartOfDaySignClicked() { + delegateScope.launch { + val newValue = prefsInteractor.getStartOfDayShift() * -1 + prefsInteractor.setStartOfDayShift(newValue) + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + notificationTypeInteractor.updateNotifications() + notificationGoalTimeInteractor.checkAndReschedule() + updateStartOfDayViewData() + } + } + + fun onKeepStatisticsRangeClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getKeepStatisticsRange() + prefsInteractor.setKeepStatisticsRange(newValue) + keepStatisticsRangeCheckbox.set(newValue) + } + } + + fun onIgnoreShortRecordsClicked() { + delegateScope.launch { + DurationDialogParams( + tag = SettingsViewModel.IGNORE_SHORT_RECORDS_DIALOG_TAG, + duration = prefsInteractor.getIgnoreShortRecordsDuration(), + ).let(router::navigate) + } + } + + + fun onShowRecordTagSelectionClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowRecordTagSelection() + prefsInteractor.setShowRecordTagSelection(newValue) + widgetInteractor.updateWidgets(listOf(WidgetType.QUICK_SETTINGS)) + showRecordTagSelectionCheckbox.set(newValue) + } + } + + fun onRecordTagSelectionCloseClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getRecordTagSelectionCloseAfterOne() + prefsInteractor.setRecordTagSelectionCloseAfterOne(newValue) + recordTagSelectionCloseCheckbox.set(newValue) + } + } + + fun onRecordTagSelectionGeneralClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getRecordTagSelectionEvenForGeneralTags() + prefsInteractor.setRecordTagSelectionEvenForGeneralTags(newValue) + recordTagSelectionForGeneralTagsCheckbox.set(newValue) + } + } + + fun onAutomatedTrackingSendEventsClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getAutomatedTrackingSendEvents() + prefsInteractor.setAutomatedTrackingSendEvents(newValue) + automatedTrackingSendEventsCheckbox.set(newValue) + } + } + + fun onAutomatedTrackingHelpClick() { + router.navigate( + settingsMapper.toAutomatedTrackingHelpDialog(), + ) + } + + fun onDurationSet(tag: String?, duration: Long) { + when (tag) { + SettingsViewModel.IGNORE_SHORT_RECORDS_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setIgnoreShortRecordsDuration(duration) + updateIgnoreShortRecordsViewData() + } + } + } + + fun onDurationDisabled(tag: String?) { + when (tag) { + SettingsViewModel.IGNORE_SHORT_RECORDS_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setIgnoreShortRecordsDuration(0) + updateIgnoreShortRecordsViewData() + } + } + } + + fun onDateTimeSet(timestamp: Long, tag: String?) = delegateScope.launch { + when (tag) { + SettingsViewModel.START_OF_DAY_DIALOG_TAG -> { + val wasPositive = prefsInteractor.getStartOfDayShift() >= 0 + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive) + prefsInteractor.setStartOfDayShift(newValue) + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + notificationTypeInteractor.updateNotifications() + notificationGoalTimeInteractor.checkAndReschedule() + updateStartOfDayViewData() + } + } + } + + private suspend fun updateFirstDayOfWeekViewData() { + val data = loadFirstDayOfWeekViewData() + (firstDayOfWeekViewData as MutableLiveData).value = data + } + + private suspend fun loadFirstDayOfWeekViewData(): FirstDayOfWeekViewData { + return prefsInteractor.getFirstDayOfWeek() + .let(settingsMapper::toFirstDayOfWeekViewData) + } + + private suspend fun updateStartOfDayViewData() { + val data = loadStartOfDayViewData() + startOfDayViewData.set(data) + } + + private suspend fun loadStartOfDayViewData(): SettingsStartOfDayViewData { + val shift = prefsInteractor.getStartOfDayShift() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + + val hint = resourceRepo.getString( + R.string.settings_start_of_day_hint_value, + timeMapper.formatDateTime( + time = timeMapper.getStartOfDayTimeStamp() + shift, + useMilitaryTime = useMilitaryTime, + showSeconds = false, + ), + ) + + return SettingsStartOfDayViewData( + startOfDayValue = settingsMapper.toStartOfDayText(shift, useMilitaryTime = true), + startOfDaySign = settingsMapper.toStartOfDaySign(shift), + hint = hint, + ) + } + + private suspend fun updateIgnoreShortRecordsViewData() { + val data = loadIgnoreShortRecordsViewData() + ignoreShortRecordsViewData.set(data) + } + + private suspend fun loadIgnoreShortRecordsViewData(): String { + return prefsInteractor.getIgnoreShortRecordsDuration() + .let(settingsMapper::toDurationViewData) + .text + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsDisplayViewModelDelegate.kt @@ -0,0 +1,376 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.core.extension.lazySuspend +import com.example.util.simpletimetracker.core.extension.set +import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInteractor +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor +import com.example.util.simpletimetracker.domain.model.CardOrder +import com.example.util.simpletimetracker.domain.model.WidgetType +import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.CardOrderViewData +import com.example.util.simpletimetracker.feature_settings.viewData.DaysInCalendarViewData +import com.example.util.simpletimetracker.feature_settings.viewData.SettingsUntrackedRangeViewData +import com.example.util.simpletimetracker.feature_settings.viewModel.SettingsViewModel +import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.params.screen.CardOrderDialogParams +import com.example.util.simpletimetracker.navigation.params.screen.CardSizeDialogParams +import com.example.util.simpletimetracker.navigation.params.screen.DurationDialogParams +import kotlinx.coroutines.launch +import javax.inject.Inject + +class SettingsDisplayViewModelDelegate @Inject constructor( + private val router: Router, + private val prefsInteractor: PrefsInteractor, + private val settingsMapper: SettingsMapper, + private val notificationTypeInteractor: NotificationTypeInteractor, + private val widgetInteractor: WidgetInteractor, +) : ViewModelDelegate() { + + val cardOrderViewData: LiveData + by lazySuspend { loadCardOrderViewData() } + val btnCardOrderManualVisibility: LiveData + by lazySuspend { prefsInteractor.getCardOrder() == CardOrder.MANUAL } + val showUntrackedInRecordsCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowUntrackedInRecords() } + val showUntrackedInStatisticsCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowUntrackedInStatistics() } + val ignoreShortUntrackedViewData: LiveData + by lazySuspend { loadIgnoreShortUntrackedViewData() } + val untrackedRangeViewData: LiveData + by lazySuspend { loadUntrackedRangeViewData() } + val showRecordsCalendarCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowRecordsCalendar() } + val reverseOrderInCalendarCheckbox: LiveData + by lazySuspend { prefsInteractor.getReverseOrderInCalendar() } + val daysInCalendarViewData: LiveData + by lazySuspend { loadDaysInCalendarViewData() } + val showActivityFiltersCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowActivityFilters() } + val showGoalsSeparatelyCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowGoalsSeparately() } + val useMilitaryTimeCheckbox: LiveData + by lazySuspend { prefsInteractor.getUseMilitaryTimeFormat() } + val useMilitaryTimeHint: LiveData + by lazySuspend { loadUseMilitaryTimeViewData() } + val useMonthDayTimeCheckbox: LiveData + by lazySuspend { prefsInteractor.getUseMonthDayTimeFormat() } + val useMonthDayTimeHint: LiveData + by lazySuspend { loadUseMonthDayTimeViewData() } + val useProportionalMinutesCheckbox: LiveData + by lazySuspend { prefsInteractor.getUseProportionalMinutes() } + val useProportionalMinutesHint: LiveData + by lazySuspend { loadUseProportionalMinutesViewData() } + val showSecondsCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowSeconds() } + val keepScreenOnCheckbox: LiveData + by lazySuspend { prefsInteractor.getKeepScreenOn() } + + private var parent: SettingsParent? = null + + fun init(parent: SettingsParent) { + this.parent = parent + } + + fun onVisible() { + delegateScope.launch { + // Need to update card order because it changes on card order dialog + updateCardOrderViewData() + } + } + + fun onDaysInCalendarSelected(position: Int) { + delegateScope.launch { + val currentValue = prefsInteractor.getDaysInCalendar() + val newValue = settingsMapper.toDaysInCalendar(position) + if (newValue == currentValue) return@launch + prefsInteractor.setDaysInCalendar(newValue) + updateDaysInCalendarViewData() + } + } + + fun onRecordTypeOrderSelected(position: Int) { + delegateScope.launch { + val currentOrder = prefsInteractor.getCardOrder() + val newOrder = settingsMapper.toCardOrder(position) + if (newOrder == currentOrder) return@launch + + btnCardOrderManualVisibility.set(newOrder == CardOrder.MANUAL) + if (newOrder == CardOrder.MANUAL) { + openCardOrderDialog(currentOrder) + } + prefsInteractor.setCardOrder(newOrder) + updateCardOrderViewData() + } + } + + fun onCardOrderManualClick() { + openCardOrderDialog(CardOrder.MANUAL) + } + + fun onShowUntrackedInRecordsClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowUntrackedInRecords() + prefsInteractor.setShowUntrackedInRecords(newValue) + showUntrackedInRecordsCheckbox.set(newValue) + } + } + + fun onShowUntrackedInStatisticsClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowUntrackedInStatistics() + prefsInteractor.setShowUntrackedInStatistics(newValue) + showUntrackedInStatisticsCheckbox.set(newValue) + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + } + } + + fun onIgnoreShortUntrackedClicked() { + delegateScope.launch { + DurationDialogParams( + tag = SettingsViewModel.IGNORE_SHORT_UNTRACKED_DIALOG_TAG, + duration = prefsInteractor.getIgnoreShortUntrackedDuration(), + ).let(router::navigate) + } + } + + fun onUntrackedRangeClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getUntrackedRangeEnabled() + prefsInteractor.setUntrackedRangeEnabled(newValue) + updateUntrackedRangeViewData() + } + } + + fun onUntrackedRangeStartClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.UNTRACKED_RANGE_START_DIALOG_TAG, + timestamp = prefsInteractor.getUntrackedRangeStart(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onUntrackedRangeEndClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.UNTRACKED_RANGE_END_DIALOG_TAG, + timestamp = prefsInteractor.getUntrackedRangeEnd(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onShowRecordsCalendarClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowRecordsCalendar() + prefsInteractor.setShowRecordsCalendar(newValue) + (showRecordsCalendarCheckbox as MutableLiveData).value = newValue + } + } + + fun onReverseOrderInCalendarClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getReverseOrderInCalendar() + prefsInteractor.setReverseOrderInCalendar(newValue) + (reverseOrderInCalendarCheckbox as MutableLiveData).value = newValue + } + } + + fun onShowActivityFiltersClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowActivityFilters() + prefsInteractor.setShowActivityFilters(newValue) + (showActivityFiltersCheckbox as MutableLiveData).value = newValue + } + } + + fun onShowGoalsSeparatelyClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowGoalsSeparately() + prefsInteractor.setShowGoalsSeparately(newValue) + (showGoalsSeparatelyCheckbox as MutableLiveData).value = newValue + router.restartApp() + } + } + + fun onUseMilitaryTimeClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getUseMilitaryTimeFormat() + prefsInteractor.setUseMilitaryTimeFormat(newValue) + (useMilitaryTimeCheckbox as MutableLiveData).value = newValue + notificationTypeInteractor.updateNotifications() + updateUseMilitaryTimeViewData() + updateUntrackedRangeViewData() + parent?.onUseMilitaryTimeClicked() + } + } + + fun onUseMonthDayTimeClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getUseMonthDayTimeFormat() + prefsInteractor.setUseMonthDayTimeFormat(newValue) + (useMonthDayTimeCheckbox as MutableLiveData).value = newValue + updateUseMonthDayTimeViewData() + } + } + + fun onUseProportionalMinutesClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getUseProportionalMinutes() + prefsInteractor.setUseProportionalMinutes(newValue) + (useProportionalMinutesCheckbox as MutableLiveData).value = newValue + notificationTypeInteractor.updateNotifications() + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + updateUseProportionalMinutesViewData() + } + } + + fun onShowSecondsClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowSeconds() + prefsInteractor.setShowSeconds(newValue) + showSecondsCheckbox.set(newValue) + notificationTypeInteractor.updateNotifications() + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) + } + } + + fun onKeepScreenOnClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getKeepScreenOn() + prefsInteractor.setKeepScreenOn(newValue) + (keepScreenOnCheckbox as MutableLiveData).value = newValue + } + } + + fun onChangeCardSizeClick() { + router.navigate(CardSizeDialogParams) + } + + fun onDurationSet(tag: String?, duration: Long) { + when (tag) { + SettingsViewModel.IGNORE_SHORT_UNTRACKED_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setIgnoreShortUntrackedDuration(duration) + updateIgnoreShortUntrackedViewData() + } + } + } + + fun onDurationDisabled(tag: String?) { + when (tag) { + SettingsViewModel.IGNORE_SHORT_UNTRACKED_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setIgnoreShortUntrackedDuration(0) + updateIgnoreShortUntrackedViewData() + } + } + } + + fun onDateTimeSet(timestamp: Long, tag: String?) = delegateScope.launch { + when (tag) { + SettingsViewModel.UNTRACKED_RANGE_START_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setUntrackedRangeStart(newValue) + updateUntrackedRangeViewData() + } + + SettingsViewModel.UNTRACKED_RANGE_END_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setUntrackedRangeEnd(newValue) + updateUntrackedRangeViewData() + } + } + } + + private fun openCardOrderDialog(cardOrder: CardOrder) { + router.navigate( + CardOrderDialogParams(cardOrder), + ) + } + + private suspend fun updateDaysInCalendarViewData() { + val data = loadDaysInCalendarViewData() + daysInCalendarViewData.set(data) + } + + private suspend fun loadDaysInCalendarViewData(): DaysInCalendarViewData { + return prefsInteractor.getDaysInCalendar() + .let(settingsMapper::toDaysInCalendarViewData) + } + + private suspend fun updateCardOrderViewData() { + val data = loadCardOrderViewData() + (cardOrderViewData as MutableLiveData).value = data + } + + private suspend fun loadCardOrderViewData(): CardOrderViewData { + return prefsInteractor.getCardOrder() + .let(settingsMapper::toCardOrderViewData) + } + + private suspend fun updateIgnoreShortUntrackedViewData() { + val data = loadIgnoreShortUntrackedViewData() + ignoreShortUntrackedViewData.set(data) + } + + private suspend fun loadIgnoreShortUntrackedViewData(): String { + return prefsInteractor.getIgnoreShortUntrackedDuration() + .let(settingsMapper::toDurationViewData) + .text + } + + private suspend fun updateUntrackedRangeViewData() { + val data = loadUntrackedRangeViewData() + untrackedRangeViewData.set(data) + } + + private suspend fun loadUntrackedRangeViewData(): SettingsUntrackedRangeViewData { + val enabled = prefsInteractor.getUntrackedRangeEnabled() + + return if (enabled) { + val start = prefsInteractor.getUntrackedRangeStart() + val end = prefsInteractor.getUntrackedRangeEnd() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + SettingsUntrackedRangeViewData.Enabled( + settingsMapper.toStartOfDayText(start, useMilitaryTime), + settingsMapper.toStartOfDayText(end, useMilitaryTime), + ) + } else { + SettingsUntrackedRangeViewData.Disabled + } + } + + private suspend fun updateUseMilitaryTimeViewData() { + val data = loadUseMilitaryTimeViewData() + useMilitaryTimeHint.set(data) + } + + private suspend fun loadUseMilitaryTimeViewData(): String { + return prefsInteractor.getUseMilitaryTimeFormat() + .let(settingsMapper::toUseMilitaryTimeHint) + } + + private suspend fun updateUseMonthDayTimeViewData() { + val data = loadUseMonthDayTimeViewData() + useMonthDayTimeHint.set(data) + } + + private suspend fun loadUseMonthDayTimeViewData(): String { + return prefsInteractor.getUseMonthDayTimeFormat() + .let(settingsMapper::toUseMonthDayTimeHint) + } + + private suspend fun updateUseProportionalMinutesViewData() { + val data = loadUseProportionalMinutesViewData() + useProportionalMinutesHint.set(data) + } + + private suspend fun loadUseProportionalMinutesViewData(): String { + return prefsInteractor.getUseProportionalMinutes() + .let(settingsMapper::toUseProportionalMinutesHint) + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsMainViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsMainViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsMainViewModelDelegate.kt @@ -0,0 +1,105 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import com.example.util.simpletimetracker.core.base.SingleLiveEvent +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.core.extension.lazySuspend +import com.example.util.simpletimetracker.core.extension.set +import com.example.util.simpletimetracker.core.interactor.LanguageInteractor +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor +import com.example.util.simpletimetracker.domain.model.WidgetType +import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.DarkModeViewData +import com.example.util.simpletimetracker.feature_settings.viewData.LanguageViewData +import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.params.screen.ArchiveParams +import com.example.util.simpletimetracker.navigation.params.screen.CategoriesParams +import com.example.util.simpletimetracker.navigation.params.screen.DataEditParams +import kotlinx.coroutines.launch +import javax.inject.Inject + +class SettingsMainViewModelDelegate @Inject constructor( + private val router: Router, + private val prefsInteractor: PrefsInteractor, + private val languageInteractor: LanguageInteractor, + private val settingsMapper: SettingsMapper, + private val widgetInteractor: WidgetInteractor, +) : ViewModelDelegate() { + + val allowMultitaskingCheckbox: LiveData + by lazySuspend { prefsInteractor.getAllowMultitasking() } + val darkModeViewData: LiveData + by lazySuspend { loadDarkModeViewData() } + val languageViewData: LiveData + by lazySuspend { loadLanguageViewData() } + val themeChanged: SingleLiveEvent = SingleLiveEvent() + + fun onVisible() { + delegateScope.launch { + // Update can come from quick settings widget + allowMultitaskingCheckbox.set(prefsInteractor.getAllowMultitasking()) + + // Update can come from system settings + updateLanguageViewData() + } + } + + fun onEditCategoriesClick() { + router.navigate(CategoriesParams) + } + + fun onArchiveClick() { + router.navigate(ArchiveParams) + } + + fun onDataEditClick() { + router.navigate(DataEditParams) + } + + fun onAllowMultitaskingClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getAllowMultitasking() + prefsInteractor.setAllowMultitasking(newValue) + widgetInteractor.updateWidgets(listOf(WidgetType.QUICK_SETTINGS)) + allowMultitaskingCheckbox.set(newValue) + } + } + + fun onDarkModeSelected(position: Int) { + delegateScope.launch { + val currentMode = prefsInteractor.getSelectedDarkMode() + val newMode = settingsMapper.toDarkMode(position) + if (newMode == currentMode) return@launch + + prefsInteractor.setDarkMode(newMode) + updateDarkModeViewData() + themeChanged.set(true) + } + } + + fun onLanguageSelected(position: Int) { + val newLanguage = settingsMapper.toLanguage(position) + languageInteractor.setLanguage(newLanguage) + updateLanguageViewData() + router.restartApp() + } + + private suspend fun updateDarkModeViewData() { + darkModeViewData.set(loadDarkModeViewData()) + } + + private suspend fun loadDarkModeViewData(): DarkModeViewData { + return prefsInteractor.getSelectedDarkMode() + .let(settingsMapper::toDarkModeViewData) + } + + private fun updateLanguageViewData() { + languageViewData.set(loadLanguageViewData()) + } + + private fun loadLanguageViewData(): LanguageViewData { + return languageInteractor.getCurrentLanguage() + .let(settingsMapper::toLanguageViewData) + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsNotificationsViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsNotificationsViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsNotificationsViewModelDelegate.kt @@ -0,0 +1,324 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.core.extension.lazySuspend +import com.example.util.simpletimetracker.core.extension.set +import com.example.util.simpletimetracker.core.interactor.CheckExactAlarmPermissionInteractor +import com.example.util.simpletimetracker.core.interactor.CheckNotificationsPermissionInteractor +import com.example.util.simpletimetracker.domain.interactor.NotificationActivityInteractor +import com.example.util.simpletimetracker.domain.interactor.NotificationInactivityInteractor +import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInteractor +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.SettingsDurationViewData +import com.example.util.simpletimetracker.feature_settings.viewModel.SettingsViewModel +import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.params.screen.DurationDialogParams +import kotlinx.coroutines.launch +import javax.inject.Inject + +class SettingsNotificationsViewModelDelegate @Inject constructor( + private val router: Router, + private val prefsInteractor: PrefsInteractor, + private val settingsMapper: SettingsMapper, + private val notificationTypeInteractor: NotificationTypeInteractor, + private val notificationInactivityInteractor: NotificationInactivityInteractor, + private val notificationActivityInteractor: NotificationActivityInteractor, + private val checkExactAlarmPermissionInteractor: CheckExactAlarmPermissionInteractor, + private val checkNotificationsPermissionInteractor: CheckNotificationsPermissionInteractor, +) : ViewModelDelegate() { + + val showNotificationsCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowNotifications() } + val showNotificationsControlsCheckbox: LiveData + by lazySuspend { prefsInteractor.getShowNotificationsControls() } + val inactivityReminderViewData: LiveData + by lazySuspend { loadInactivityReminderViewData() } + val inactivityReminderRecurrentCheckbox: LiveData + by lazySuspend { prefsInteractor.getInactivityReminderRecurrent() } + val inactivityReminderDndStartViewData: LiveData + by lazySuspend { loadInactivityReminderDndStartViewData() } + val inactivityReminderDndEndViewData: LiveData + by lazySuspend { loadInactivityReminderDndEndViewData() } + val activityReminderViewData: LiveData + by lazySuspend { loadActivityReminderViewData() } + val activityReminderRecurrentCheckbox: LiveData + by lazySuspend { prefsInteractor.getActivityReminderRecurrent() } + val activityReminderDndStartViewData: LiveData + by lazySuspend { loadActivityReminderDndStartViewData() } + val activityReminderDndEndViewData: LiveData + by lazySuspend { loadActivityReminderDndEndViewData() } + + private var parent: SettingsParent? = null + + fun init(parent: SettingsParent) { + this.parent = parent + } + + suspend fun onUseMilitaryTimeClicked() { + updateActivityReminderDndStartViewData() + updateActivityReminderDndEndViewData() + updateInactivityReminderDndStartViewData() + updateInactivityReminderDndEndViewData() + } + + fun onShowNotificationsClicked() { + fun updateValue(newValue: Boolean) = delegateScope.launch { + prefsInteractor.setShowNotifications(newValue) + showNotificationsCheckbox.set(newValue) + notificationTypeInteractor.updateNotifications() + } + + delegateScope.launch { + if (prefsInteractor.getShowNotifications()) { + updateValue(false) + } else { + checkNotificationsPermissionInteractor.execute( + onEnabled = { updateValue(true) }, + onDisabled = { updateValue(false) }, + ) + } + } + } + + fun onShowNotificationsControlsClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getShowNotificationsControls() + prefsInteractor.setShowNotificationsControls(newValue) + showNotificationsControlsCheckbox.set(newValue) + notificationTypeInteractor.updateNotifications() + } + } + + fun onInactivityReminderClicked() = delegateScope.launch { + val duration = prefsInteractor.getInactivityReminderDuration() + + fun openDialog() { + DurationDialogParams( + tag = SettingsViewModel.INACTIVITY_DURATION_DIALOG_TAG, + duration = duration, + ).let(router::navigate) + } + + if (duration > 0) { + openDialog() + } else { + checkNotificationsPermissionInteractor.execute( + onEnabled = ::openDialog, + ) + } + } + + fun onInactivityReminderRecurrentClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getInactivityReminderRecurrent() + prefsInteractor.setInactivityReminderRecurrent(newValue) + inactivityReminderRecurrentCheckbox.set(newValue) + notificationInactivityInteractor.cancel() + notificationInactivityInteractor.checkAndSchedule() + } + } + + fun onInactivityReminderDoNotDisturbStartClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.INACTIVITY_REMINDER_DND_START_DIALOG_TAG, + timestamp = prefsInteractor.getInactivityReminderDoNotDisturbStart(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onInactivityReminderDoNotDisturbEndClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.INACTIVITY_REMINDER_DND_END_DIALOG_TAG, + timestamp = prefsInteractor.getInactivityReminderDoNotDisturbEnd(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onActivityReminderClicked() = delegateScope.launch { + val duration = prefsInteractor.getActivityReminderDuration() + + fun openDialog() { + DurationDialogParams( + tag = SettingsViewModel.ACTIVITY_DURATION_DIALOG_TAG, + duration = duration, + ).let(router::navigate) + } + + if (duration > 0) { + openDialog() + } else { + checkNotificationsPermissionInteractor.execute(onEnabled = ::openDialog) + } + } + + fun onActivityReminderRecurrentClicked() { + delegateScope.launch { + val newValue = !prefsInteractor.getActivityReminderRecurrent() + prefsInteractor.setActivityReminderRecurrent(newValue) + activityReminderRecurrentCheckbox.set(newValue) + notificationActivityInteractor.cancel() + notificationActivityInteractor.checkAndSchedule() + } + } + + fun onActivityReminderDoNotDisturbStartClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.ACTIVITY_REMINDER_DND_START_DIALOG_TAG, + timestamp = prefsInteractor.getActivityReminderDoNotDisturbStart(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onActivityReminderDoNotDisturbEndClicked() { + delegateScope.launch { + parent?.openDateTimeDialog( + tag = SettingsViewModel.ACTIVITY_REMINDER_DND_END_DIALOG_TAG, + timestamp = prefsInteractor.getActivityReminderDoNotDisturbEnd(), + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + ) + } + } + + fun onDurationSet(tag: String?, duration: Long) { + when (tag) { + SettingsViewModel.INACTIVITY_DURATION_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setInactivityReminderDuration(duration) + updateInactivityReminderViewData() + notificationInactivityInteractor.cancel() + notificationInactivityInteractor.checkAndSchedule() + checkExactAlarmPermissionInteractor.execute() + } + SettingsViewModel.ACTIVITY_DURATION_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setActivityReminderDuration(duration) + updateActivityReminderViewData() + notificationActivityInteractor.cancel() + notificationActivityInteractor.checkAndSchedule() + checkExactAlarmPermissionInteractor.execute() + } + } + } + + fun onDurationDisabled(tag: String?) { + when (tag) { + SettingsViewModel.INACTIVITY_DURATION_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setInactivityReminderDuration(0) + updateInactivityReminderViewData() + notificationInactivityInteractor.cancel() + } + + SettingsViewModel.ACTIVITY_DURATION_DIALOG_TAG -> delegateScope.launch { + prefsInteractor.setActivityReminderDuration(0) + updateActivityReminderViewData() + notificationActivityInteractor.cancel() + } + } + } + + fun onDateTimeSet(timestamp: Long, tag: String?) = delegateScope.launch { + when (tag) { + SettingsViewModel.INACTIVITY_REMINDER_DND_START_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setInactivityReminderDoNotDisturbStart(newValue) + updateInactivityReminderDndStartViewData() + notificationInactivityInteractor.cancel() + notificationInactivityInteractor.checkAndSchedule() + } + + SettingsViewModel.INACTIVITY_REMINDER_DND_END_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setInactivityReminderDoNotDisturbEnd(newValue) + updateInactivityReminderDndEndViewData() + notificationInactivityInteractor.cancel() + notificationInactivityInteractor.checkAndSchedule() + } + + SettingsViewModel.ACTIVITY_REMINDER_DND_START_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setActivityReminderDoNotDisturbStart(newValue) + updateActivityReminderDndStartViewData() + notificationActivityInteractor.cancel() + notificationActivityInteractor.checkAndSchedule() + } + + SettingsViewModel.ACTIVITY_REMINDER_DND_END_DIALOG_TAG -> { + val newValue = settingsMapper.toStartOfDayShift(timestamp, wasPositive = true) + prefsInteractor.setActivityReminderDoNotDisturbEnd(newValue) + updateActivityReminderDndEndViewData() + notificationActivityInteractor.cancel() + notificationActivityInteractor.checkAndSchedule() + } + } + } + + private suspend fun updateInactivityReminderViewData() { + val data = loadInactivityReminderViewData() + inactivityReminderViewData.set(data) + } + + private suspend fun loadInactivityReminderViewData(): SettingsDurationViewData { + return prefsInteractor.getInactivityReminderDuration() + .let(settingsMapper::toDurationViewData) + } + + private suspend fun updateInactivityReminderDndStartViewData() { + val data = loadInactivityReminderDndStartViewData() + inactivityReminderDndStartViewData.set(data) + } + + private suspend fun loadInactivityReminderDndStartViewData(): String { + val shift = prefsInteractor.getInactivityReminderDoNotDisturbStart() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + return settingsMapper.toStartOfDayText(shift, useMilitaryTime) + } + + private suspend fun updateInactivityReminderDndEndViewData() { + val data = loadInactivityReminderDndEndViewData() + inactivityReminderDndEndViewData.set(data) + } + + private suspend fun loadInactivityReminderDndEndViewData(): String { + val shift = prefsInteractor.getInactivityReminderDoNotDisturbEnd() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + return settingsMapper.toStartOfDayText(shift, useMilitaryTime) + } + + private suspend fun updateActivityReminderViewData() { + val data = loadActivityReminderViewData() + activityReminderViewData.set(data) + } + + private suspend fun loadActivityReminderViewData(): SettingsDurationViewData { + return prefsInteractor.getActivityReminderDuration() + .let(settingsMapper::toDurationViewData) + } + + private suspend fun updateActivityReminderDndStartViewData() { + val data = loadActivityReminderDndStartViewData() + activityReminderDndStartViewData.set(data) + } + + private suspend fun loadActivityReminderDndStartViewData(): String { + val shift = prefsInteractor.getActivityReminderDoNotDisturbStart() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + return settingsMapper.toStartOfDayText(shift, useMilitaryTime) + } + + private suspend fun updateActivityReminderDndEndViewData() { + val data = loadActivityReminderDndEndViewData() + activityReminderDndEndViewData.set(data) + } + + private suspend fun loadActivityReminderDndEndViewData(): String { + val shift = prefsInteractor.getActivityReminderDoNotDisturbEnd() + val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() + return settingsMapper.toStartOfDayText(shift, useMilitaryTime) + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsParent.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsParent.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsParent.kt @@ -0,0 +1,12 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +interface SettingsParent { + + fun openDateTimeDialog( + tag: String, + timestamp: Long, + useMilitaryTime: Boolean, + ) + + suspend fun onUseMilitaryTimeClicked() +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsRatingViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsRatingViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsRatingViewModelDelegate.kt @@ -0,0 +1,53 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.example.util.simpletimetracker.core.BuildConfig +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.core.provider.ApplicationDataProvider +import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.feature_settings.R +import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.params.action.OpenMarketParams +import com.example.util.simpletimetracker.navigation.params.action.SendEmailParams +import com.example.util.simpletimetracker.navigation.params.notification.SnackBarParams +import javax.inject.Inject + +class SettingsRatingViewModelDelegate @Inject constructor( + private val router: Router, + private val resourceRepo: ResourceRepo, + private val applicationDataProvider: ApplicationDataProvider, +) : ViewModelDelegate() { + + val versionName: LiveData + by lazy { MutableLiveData(loadVersionName()) } + + fun onRateClick() { + router.execute(OpenMarketParams(packageName = applicationDataProvider.getPackageName())) + } + + fun onFeedbackClick() { + router.execute( + data = SendEmailParams( + email = resourceRepo.getString(R.string.support_email), + subject = resourceRepo.getString(R.string.support_email_subject), + chooserTitle = resourceRepo.getString(R.string.settings_email_chooser_title), + notHandledCallback = { R.string.message_app_not_found.let(::showMessage) }, + ), + ) + } + + private fun showMessage(stringResId: Int) { + val params = SnackBarParams(message = resourceRepo.getString(stringResId)) + router.show(params) + } + + private fun loadVersionName(): String { + val appVersion = applicationDataProvider.getAppVersion() + return if (BuildConfig.DEBUG) { + "$appVersion ${BuildConfig.BUILD_TYPE}" + } else { + appVersion + } + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsTranslatorsViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsTranslatorsViewModelDelegate.kt new file mode 100644 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsTranslatorsViewModelDelegate.kt @@ -0,0 +1,20 @@ +package com.example.util.simpletimetracker.feature_settings.viewModel.delegate + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.example.util.simpletimetracker.core.base.ViewModelDelegate +import com.example.util.simpletimetracker.feature_settings.adapter.SettingsTranslatorViewData +import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import javax.inject.Inject + +class SettingsTranslatorsViewModelDelegate @Inject constructor( + private val settingsMapper: SettingsMapper, +) : ViewModelDelegate() { + + val translatorsViewData: LiveData> + by lazy { MutableLiveData(loadTranslatorsViewData()) } + + private fun loadTranslatorsViewData(): List { + return settingsMapper.mapTranslatorsViewData() + } +} \ No newline at end of file -- tangled.sh