From d4de2ddfc61761a8786e500c90866524b7af2775 Mon Sep 17 00:00:00 2001 From: Adharsh Babu Date: Sun, 18 Oct 2026 20:41:04 -0700 Subject: [PATCH] Add repeat interval to automatic backup and export Automatic backup and spreadsheet export can now repeat every 1, 2, 3, 4, 6, 8, 12 or 24 hours, starting from the selected save time. Defaults to 24 hours, which keeps the existing once a day behavior. --- .../data_local/prefs/PrefsRepoImpl.kt | 11 ++ .../prefs/interactor/PrefsInteractor.kt | 16 +++ .../domain/prefs/repo/PrefsRepo.kt | 4 + .../feature_notification/build.gradle.kts | 3 + .../AutomaticBackupInteractorImpl.kt | 3 +- .../AutomaticExportInteractorImpl.kt | 3 +- .../core/GetTimeLeftToTimestampInteractor.kt | 20 +++- .../GetTimeLeftToTimestampInteractorTest.kt | 106 ++++++++++++++++++ .../feature_settings/api/SettingsBlock.kt | 2 + .../SettingsBackupViewDataInteractor.kt | 20 +++- .../SettingsExportViewDataInteractor.kt | 19 +++- .../feature_settings/mapper/SettingsMapper.kt | 19 ++++ .../viewData/AutomaticSaveIntervalViewData.kt | 8 ++ .../SettingsBackupViewModelDelegate.kt | 26 +++++ resources/src/main/res/values/strings.xml | 3 +- 15 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 features/feature_notification/src/test/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractorTest.kt create mode 100644 features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewData/AutomaticSaveIntervalViewData.kt diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/prefs/PrefsRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/prefs/PrefsRepoImpl.kt index 28f9fd49fb..944302762f 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/prefs/PrefsRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/prefs/PrefsRepoImpl.kt @@ -419,6 +419,10 @@ class PrefsRepoImpl @Inject constructor( KEY_AUTOMATIC_BACKUP_TRIGGER_TIME, 0, ) + override var automaticBackupInterval: Long by prefs.delegate( + KEY_AUTOMATIC_BACKUP_INTERVAL, AUTOMATIC_SAVE_INTERVAL_DEFAULT, + ) + override var automaticExportUri: String by prefs.delegate( KEY_AUTOMATIC_EXPORT_URI, "", ) @@ -435,6 +439,10 @@ class PrefsRepoImpl @Inject constructor( KEY_AUTOMATIC_EXPORT_TRIGGER_TIME, 0, ) + override var automaticExportInterval: Long by prefs.delegate( + KEY_AUTOMATIC_EXPORT_INTERVAL, AUTOMATIC_SAVE_INTERVAL_DEFAULT, + ) + override var repeatButtonType: Int by prefs.delegate( KEY_REPEAT_BUTTON_TYPE, 0, ) @@ -743,6 +751,7 @@ class PrefsRepoImpl @Inject constructor( private const val POMODORO_DEFAULT_LONG_BREAK_TIME_SEC: Long = 60 * 15 // 15 min private const val POMODORO_DEFAULT_UNTIL_LONG_BREAK: Long = 4 private const val RANGE_LAST_DAYS_DEFAULT: Int = 7 + private const val AUTOMATIC_SAVE_INTERVAL_DEFAULT: Long = 1000 * 60 * 60 * 24 // 1 day const val KEY_RECORD_TYPES_FILTERED_ON_LIST = "recordTypesFilteredOnList" const val KEY_CATEGORIES_TYPES_FILTERED_ON_LIST = "categoriesFilteredOnList" @@ -858,10 +867,12 @@ class PrefsRepoImpl @Inject constructor( private const val KEY_AUTOMATIC_BACKUP_ERROR = "automaticBackupError" private const val KEY_AUTOMATIC_BACKUP_LAST_SAVE_TIME = "automaticBackupLastSaveTime" private const val KEY_AUTOMATIC_BACKUP_TRIGGER_TIME = "automaticBackupTriggerTime" + private const val KEY_AUTOMATIC_BACKUP_INTERVAL = "automaticBackupInterval" private const val KEY_AUTOMATIC_EXPORT_URI = "automaticExportUri" private const val KEY_AUTOMATIC_EXPORT_ERROR = "automaticExportError" private const val KEY_AUTOMATIC_EXPORT_LAST_SAVE_TIME = "automaticExportLastSaveTime" private const val KEY_AUTOMATIC_EXPORT_TRIGGER_TIME = "automaticExportTriggerTime" + private const val KEY_AUTOMATIC_EXPORT_INTERVAL = "automaticExportInterval" private const val KEY_POMODORO_MODE_STARTED_TIMESTAMP = "pomodoroModeStartedTimestamp" private const val KEY_POMODORO_MODE_PAUSED_TIMESTAMP = "pomodoroModePausedTimestamp" private const val KEY_WIDGET = "widget_" diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/interactor/PrefsInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/interactor/PrefsInteractor.kt index 86ff1c9a83..ac2475a5cf 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/interactor/PrefsInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/interactor/PrefsInteractor.kt @@ -975,6 +975,14 @@ class PrefsInteractor @Inject constructor( prefsRepo.automaticBackupTriggerTime } + suspend fun setAutomaticBackupInterval(value: Long) = withContext(Dispatchers.IO) { + prefsRepo.automaticBackupInterval = value + } + + suspend fun getAutomaticBackupInterval(): Long = withContext(Dispatchers.IO) { + prefsRepo.automaticBackupInterval + } + suspend fun setAutomaticExportUri(uri: String) = withContext(Dispatchers.IO) { prefsRepo.automaticExportUri = uri } @@ -1007,6 +1015,14 @@ class PrefsInteractor @Inject constructor( prefsRepo.automaticExportTriggerTime } + suspend fun setAutomaticExportInterval(value: Long) = withContext(Dispatchers.IO) { + prefsRepo.automaticExportInterval = value + } + + suspend fun getAutomaticExportInterval(): Long = withContext(Dispatchers.IO) { + prefsRepo.automaticExportInterval + } + suspend fun getRepeatButtonType(): RepeatButtonType = withContext(Dispatchers.IO) { when (prefsRepo.repeatButtonType) { 0 -> RepeatButtonType.RepeatLast diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/repo/PrefsRepo.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/repo/PrefsRepo.kt index d59ac0a10c..47765f2833 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/repo/PrefsRepo.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/repo/PrefsRepo.kt @@ -183,6 +183,8 @@ interface PrefsRepo { var automaticBackupTriggerTime: Long + var automaticBackupInterval: Long + var automaticExportUri: String var automaticExportError: Boolean @@ -191,6 +193,8 @@ interface PrefsRepo { var automaticExportTriggerTime: Long + var automaticExportInterval: Long + var repeatButtonType: Int var widgetBackgroundTransparencyPercent: Long diff --git a/features/feature_notification/build.gradle.kts b/features/feature_notification/build.gradle.kts index 5197196f0f..c052b83ecd 100644 --- a/features/feature_notification/build.gradle.kts +++ b/features/feature_notification/build.gradle.kts @@ -20,4 +20,7 @@ dependencies { implementation(libs.google.gson) implementation(libs.google.dagger) ksp(libs.kapt.dagger) + + testImplementation(libs.test.junit) + testImplementation(libs.test.mockito) } diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticBackup/interactor/AutomaticBackupInteractorImpl.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticBackup/interactor/AutomaticBackupInteractorImpl.kt index 9a5eea01fb..d90d442271 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticBackup/interactor/AutomaticBackupInteractorImpl.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticBackup/interactor/AutomaticBackupInteractorImpl.kt @@ -21,7 +21,8 @@ class AutomaticBackupInteractorImpl @Inject constructor( override suspend fun schedule() { val triggerTime = prefsInteractor.getAutomaticBackupTriggerTime() - val timestamp = getTimeLeftToTimestampInteractor.execute(triggerTime) + val interval = prefsInteractor.getAutomaticBackupInterval() + val timestamp = getTimeLeftToTimestampInteractor.execute(triggerTime, interval) scheduler.schedule(timestamp) } diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticExport/interactor/AutomaticExportInteractorImpl.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticExport/interactor/AutomaticExportInteractorImpl.kt index 54b5ec318b..26097b6aef 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticExport/interactor/AutomaticExportInteractorImpl.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/automaticExport/interactor/AutomaticExportInteractorImpl.kt @@ -20,7 +20,8 @@ class AutomaticExportInteractorImpl @Inject constructor( override suspend fun schedule() { val triggerTime = prefsInteractor.getAutomaticExportTriggerTime() - val timestamp = getTimeLeftToTimestampInteractor.execute(triggerTime) + val interval = prefsInteractor.getAutomaticExportInterval() + val timestamp = getTimeLeftToTimestampInteractor.execute(triggerTime, interval) scheduler.schedule(timestamp) } diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractor.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractor.kt index cc764b4d4f..41cb1f3189 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractor.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractor.kt @@ -2,21 +2,37 @@ package com.example.util.simpletimetracker.feature_notification.core import com.example.util.simpletimetracker.core.extension.shiftTimeStamp import com.example.util.simpletimetracker.core.mapper.TimeMapper +import com.example.util.simpletimetracker.domain.base.CurrentTimestampProvider import java.util.Calendar +import java.util.concurrent.TimeUnit import javax.inject.Inject class GetTimeLeftToTimestampInteractor @Inject constructor( private val timeMapper: TimeMapper, + private val currentTimestampProvider: CurrentTimestampProvider, ) { + // Interval should divide a day evenly, + // so trigger times are repeated at the same time every day. fun execute( dayTimestamp: Long, + interval: Long, + ): Long { + val dayDuration = TimeUnit.DAYS.toMillis(1) + + return (0 until dayDuration / interval) + .map { (dayTimestamp + it * interval) % dayDuration } + .minOf(::getTimeLeft) + } + + private fun getTimeLeft( + dayTimestamp: Long, ): Long { val calendar = Calendar.getInstance() - val current = System.currentTimeMillis() + val current = currentTimestampProvider.get() val triggerTime = calendar.shiftTimeStamp( - timestamp = timeMapper.getStartOfDayTimeStamp(), + timestamp = timeMapper.getStartOfDayTimeStamp(current), shift = dayTimestamp, ).let { if (it > current) { diff --git a/features/feature_notification/src/test/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractorTest.kt b/features/feature_notification/src/test/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractorTest.kt new file mode 100644 index 0000000000..019ec446d3 --- /dev/null +++ b/features/feature_notification/src/test/java/com/example/util/simpletimetracker/feature_notification/core/GetTimeLeftToTimestampInteractorTest.kt @@ -0,0 +1,106 @@ +package com.example.util.simpletimetracker.feature_notification.core + +import com.example.util.simpletimetracker.core.mapper.TimeMapper +import com.example.util.simpletimetracker.core.provider.LocaleProvider +import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.base.CurrentTimestampProvider +import org.junit.AfterClass +import org.junit.Assert.assertEquals +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import java.util.TimeZone +import java.util.concurrent.TimeUnit + +@RunWith(Parameterized::class) +class GetTimeLeftToTimestampInteractorTest( + private val input: List, + private val output: Long, +) { + + private val currentTimestampProvider = mock(CurrentTimestampProvider::class.java) + private val timeMapper = TimeMapper( + mock(LocaleProvider::class.java), + mock(ResourceRepo::class.java), + currentTimestampProvider, + ) + + @Test + fun test() { + `when`(currentTimestampProvider.get()).thenReturn(input[0]) + + val subject = GetTimeLeftToTimestampInteractor(timeMapper, currentTimestampProvider) + + assertEquals( + "Test failed for params $input", + output, + subject.execute( + dayTimestamp = input[1], + interval = input[2], + ), + ) + } + + companion object { + private val timezoneDefault: TimeZone = TimeZone.getDefault() + private val minuteInMs = TimeUnit.MINUTES.toMillis(1) + private val hourInMs = TimeUnit.HOURS.toMillis(1) + private val dayInMs = TimeUnit.DAYS.toMillis(1) + private val startOfDay = 1767225600000L // 2026-01-01 00:00 UTC + + @JvmStatic + @BeforeClass + fun beforeClass() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + @JvmStatic + @AfterClass + fun afterClass() { + TimeZone.setDefault(timezoneDefault) + } + + @JvmStatic + @Parameterized.Parameters + fun data() = listOf( + // Every day, trigger time is later today. + arrayOf( + listOf(startOfDay + 10 * hourInMs, 15 * hourInMs, dayInMs), + 5 * hourInMs, + ), + // Every day, trigger time passed, next is tomorrow. + arrayOf( + listOf(startOfDay + 10 * hourInMs + 30 * minuteInMs, 0L, dayInMs), + 13 * hourInMs + 30 * minuteInMs, + ), + // Every 6 hours from 00:00, next is 12:00. + arrayOf( + listOf(startOfDay + 10 * hourInMs + 30 * minuteInMs, 0L, 6 * hourInMs), + 1 * hourInMs + 30 * minuteInMs, + ), + // Every 8 hours from 20:00, next is 12:00, before trigger time. + arrayOf( + listOf(startOfDay + 10 * hourInMs + 30 * minuteInMs, 20 * hourInMs, 8 * hourInMs), + 1 * hourInMs + 30 * minuteInMs, + ), + // Every hour from 00:15, next is 11:15. + arrayOf( + listOf(startOfDay + 10 * hourInMs + 30 * minuteInMs, 15 * minuteInMs, hourInMs), + 45 * minuteInMs, + ), + // Every 12 hours from 23:00, after last trigger of the day, next is tomorrow 11:00. + arrayOf( + listOf(startOfDay + 23 * hourInMs + 30 * minuteInMs, 23 * hourInMs, 12 * hourInMs), + 11 * hourInMs + 30 * minuteInMs, + ), + // Exactly at trigger time, next is the following one. + arrayOf( + listOf(startOfDay + 12 * hourInMs, 0L, 6 * hourInMs), + 6 * hourInMs, + ), + ) + } +} diff --git a/features/feature_settings/api/src/main/java/com/example/util/simpletimetracker/feature_settings/api/SettingsBlock.kt b/features/feature_settings/api/src/main/java/com/example/util/simpletimetracker/feature_settings/api/SettingsBlock.kt index 18ec525b2c..2d86a2ea50 100644 --- a/features/feature_settings/api/src/main/java/com/example/util/simpletimetracker/feature_settings/api/SettingsBlock.kt +++ b/features/feature_settings/api/src/main/java/com/example/util/simpletimetracker/feature_settings/api/SettingsBlock.kt @@ -109,6 +109,7 @@ enum class SettingsBlock { BackupAutomatic, BackupAutomaticHint, BackupAutomaticTime, + BackupAutomaticInterval, BackupRestore, BackupCustomized, BackupCustomizedPartialSave, @@ -123,6 +124,7 @@ enum class SettingsBlock { ExportSpreadsheetAutomatic, ExportSpreadsheetAutomaticHint, ExportSpreadsheetAutomaticTime, + ExportSpreadsheetAutomaticInterval, ExportSpreadsheetDateTimeFormat, ExportSpreadsheetDateTimeFormatHint, ExportSpreadsheetImport, diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsBackupViewDataInteractor.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsBackupViewDataInteractor.kt index 870b5f9ca0..4b96bf92cc 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsBackupViewDataInteractor.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsBackupViewDataInteractor.kt @@ -6,11 +6,13 @@ import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_settings.R import com.example.util.simpletimetracker.feature_settings.api.SettingsBlock import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.AutomaticSaveIntervalViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsBottomViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsCheckboxViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsCollapseViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsHintViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsSelectorViewData +import com.example.util.simpletimetracker.feature_settings.views.SettingsSpinnerViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsTextColor import com.example.util.simpletimetracker.feature_settings.views.SettingsTextViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsTopViewData @@ -86,7 +88,18 @@ class SettingsBackupViewDataInteractor @Inject constructor( subtitle = "", selectedValue = loadAutomaticBackupTriggerTime(), bottomSpaceIsVisible = true, - dividerIsVisible = true, + dividerIsVisible = false, + ) + val automaticBackupIntervalViewData = loadAutomaticBackupIntervalViewData() + result += SettingsSpinnerViewData( + block = SettingsBlock.BackupAutomaticInterval, + title = resourceRepo.getString(R.string.settings_automatic_save_interval), + value = automaticBackupIntervalViewData.items + .getOrNull(automaticBackupIntervalViewData.selectedPosition)?.text.orEmpty(), + items = automaticBackupIntervalViewData.items, + selectedPosition = automaticBackupIntervalViewData.selectedPosition, + processSameItemSelected = false, + topSpaceIsVisible = false, ) } @@ -125,4 +138,9 @@ class SettingsBackupViewDataInteractor @Inject constructor( useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), ) } + + private suspend fun loadAutomaticBackupIntervalViewData(): AutomaticSaveIntervalViewData { + return prefsInteractor.getAutomaticBackupInterval() + .let(settingsMapper::toAutomaticSaveIntervalViewData) + } } \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsExportViewDataInteractor.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsExportViewDataInteractor.kt index f49e88479c..597dffac50 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsExportViewDataInteractor.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/interactor/SettingsExportViewDataInteractor.kt @@ -6,6 +6,7 @@ import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_settings.R import com.example.util.simpletimetracker.feature_settings.api.SettingsBlock import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper +import com.example.util.simpletimetracker.feature_settings.viewData.AutomaticSaveIntervalViewData import com.example.util.simpletimetracker.feature_settings.viewData.ExportDateTimeFormatViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsBottomViewData import com.example.util.simpletimetracker.feature_settings.views.SettingsCheckboxViewData @@ -84,7 +85,18 @@ class SettingsExportViewDataInteractor @Inject constructor( subtitle = "", selectedValue = loadAutomaticExportTriggerTime(), bottomSpaceIsVisible = true, - dividerIsVisible = true, + dividerIsVisible = false, + ) + val automaticExportIntervalViewData = loadAutomaticExportIntervalViewData() + result += SettingsSpinnerViewData( + block = SettingsBlock.ExportSpreadsheetAutomaticInterval, + title = resourceRepo.getString(R.string.settings_automatic_save_interval), + value = automaticExportIntervalViewData.items + .getOrNull(automaticExportIntervalViewData.selectedPosition)?.text.orEmpty(), + items = automaticExportIntervalViewData.items, + selectedPosition = automaticExportIntervalViewData.selectedPosition, + processSameItemSelected = false, + topSpaceIsVisible = false, ) } @@ -174,6 +186,11 @@ class SettingsExportViewDataInteractor @Inject constructor( ) } + private suspend fun loadAutomaticExportIntervalViewData(): AutomaticSaveIntervalViewData { + return prefsInteractor.getAutomaticExportInterval() + .let(settingsMapper::toAutomaticSaveIntervalViewData) + } + private suspend fun loadDateTimeFormatViewData(): ExportDateTimeFormatViewData { return prefsInteractor.getCsvExportDateTimeFormat() .let(settingsMapper::toCsvExportDateTimeFormat) diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/mapper/SettingsMapper.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/mapper/SettingsMapper.kt index 9183f5ced2..c3c1e9387e 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/mapper/SettingsMapper.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/mapper/SettingsMapper.kt @@ -21,6 +21,7 @@ import com.example.util.simpletimetracker.feature_settings.R import com.example.util.simpletimetracker.feature_settings.api.CardOrderViewData import com.example.util.simpletimetracker.feature_settings.api.SettingsCardOrderMapper import com.example.util.simpletimetracker.feature_settings.model.CustomizeOptionsMenuListItem +import com.example.util.simpletimetracker.feature_settings.viewData.AutomaticSaveIntervalViewData 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.DurationFormatViewData @@ -106,6 +107,10 @@ class SettingsMapper @Inject constructor( private val exportDateTimeFormatList: List = fileExportDateTimeFormatMapper.getAvailableFormats() + // Should divide a day evenly. + private val automaticSaveIntervalList: List = listOf(1L, 2, 3, 4, 6, 8, 12, 24) + .map(TimeUnit.HOURS::toMillis) + override fun toCardOrderViewData(currentOrder: CardOrder): CardOrderViewData { return CardOrderViewData( items = cardOrderList @@ -258,6 +263,20 @@ class SettingsMapper @Inject constructor( return exportDateTimeFormatList.getOrNull(position) ?: exportDateTimeFormatList.first() } + fun toAutomaticSaveIntervalViewData(currentValue: Long): AutomaticSaveIntervalViewData { + return AutomaticSaveIntervalViewData( + items = automaticSaveIntervalList + .map { timeMapper.formatDuration(TimeUnit.MILLISECONDS.toSeconds(it)) } + .map(CustomSpinner::CustomSpinnerTextItem), + selectedPosition = automaticSaveIntervalList.indexOf(currentValue) + .takeUnless { it == -1 } ?: automaticSaveIntervalList.lastIndex, + ) + } + + fun toAutomaticSaveInterval(position: Int): Long { + return automaticSaveIntervalList.getOrNull(position) ?: automaticSaveIntervalList.last() + } + fun toDurationViewData(duration: Long): SettingsDurationViewData { return if (duration > 0) { SettingsDurationViewData( diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewData/AutomaticSaveIntervalViewData.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewData/AutomaticSaveIntervalViewData.kt new file mode 100644 index 0000000000..6db39cb7e0 --- /dev/null +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewData/AutomaticSaveIntervalViewData.kt @@ -0,0 +1,8 @@ +package com.example.util.simpletimetracker.feature_settings.viewData + +import com.example.util.simpletimetracker.feature_views.spinner.CustomSpinner + +data class AutomaticSaveIntervalViewData( + val items: List, + val selectedPosition: Int, +) diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsBackupViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsBackupViewModelDelegate.kt index 5b79389007..2f2a694407 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsBackupViewModelDelegate.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsBackupViewModelDelegate.kt @@ -86,6 +86,18 @@ class SettingsBackupViewModelDelegate @Inject constructor( } } + override fun onSpinnerPositionSelected(block: SettingsBlock, position: Int) { + when (block) { + SettingsBlock.BackupAutomaticInterval -> + onAutoBackupIntervalSelected(position) + SettingsBlock.ExportSpreadsheetAutomaticInterval -> + onAutoExportIntervalSelected(position) + else -> { + // Do nothing + } + } + } + override fun onDateTimeSet(timestamp: Long, tag: String?) { onDateTimeSetDelegate(timestamp, tag) } @@ -133,6 +145,20 @@ class SettingsBackupViewModelDelegate @Inject constructor( } } + private fun onAutoBackupIntervalSelected(position: Int) = delegateScope.launch { + val newValue = settingsMapper.toAutomaticSaveInterval(position) + prefsInteractor.setAutomaticBackupInterval(newValue) + automaticBackupInteractor.schedule() + parent?.updateContent() + } + + private fun onAutoExportIntervalSelected(position: Int) = delegateScope.launch { + val newValue = settingsMapper.toAutomaticSaveInterval(position) + prefsInteractor.setAutomaticExportInterval(newValue) + automaticExportInteractor.schedule() + parent?.updateContent() + } + private fun onDateTimeSetDelegate(timestamp: Long, tag: String?) = delegateScope.launch { when (tag) { SettingsDialogTags.AUTO_BACKUP_TRIGGER_TIME_DIALOG_TAG -> { diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index dcb7ebee67..cbe68a3dd5 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -480,9 +480,10 @@ If \"json\" - a json encoded list with additional data, same as used in the Wear Before saving, make sure that you have the latest version of the app installed to avoid conflicts. Enable automatic backup Enable automatic spreadsheet export - File will be saved in the selected location overwriting previously saved file every day at midnight. + File will be saved in the selected location overwriting previously saved file. Last save: Save time + Repeat every Spreadsheet time format Local UTC