Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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, "",
)
Expand All @@ -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,
)
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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_"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ interface PrefsRepo {

var automaticBackupTriggerTime: Long

var automaticBackupInterval: Long

var automaticExportUri: String

var automaticExportError: Boolean
Expand All @@ -191,6 +193,8 @@ interface PrefsRepo {

var automaticExportTriggerTime: Long

var automaticExportInterval: Long

var repeatButtonType: Int

var widgetBackgroundTransparencyPercent: Long
Expand Down
3 changes: 3 additions & 0 deletions features/feature_notification/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Long>,
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,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ enum class SettingsBlock {
BackupAutomatic,
BackupAutomaticHint,
BackupAutomaticTime,
BackupAutomaticInterval,
BackupRestore,
BackupCustomized,
BackupCustomizedPartialSave,
Expand All @@ -123,6 +124,7 @@ enum class SettingsBlock {
ExportSpreadsheetAutomatic,
ExportSpreadsheetAutomaticHint,
ExportSpreadsheetAutomaticTime,
ExportSpreadsheetAutomaticInterval,
ExportSpreadsheetDateTimeFormat,
ExportSpreadsheetDateTimeFormatHint,
ExportSpreadsheetImport,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
)
}

Expand Down Expand Up @@ -125,4 +138,9 @@ class SettingsBackupViewDataInteractor @Inject constructor(
useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(),
)
}

private suspend fun loadAutomaticBackupIntervalViewData(): AutomaticSaveIntervalViewData {
return prefsInteractor.getAutomaticBackupInterval()
.let(settingsMapper::toAutomaticSaveIntervalViewData)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
)
}

Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading