From 0882ef9693409a1932bc74c1139f84c8c2aff6de Mon Sep 17 00:00:00 2001 From: razeeman Date: Sat, 15 Jul 2023 21:52:45 +0300 Subject: [PATCH] support new model for goals --- .../GetRunningRecordViewDataMediator.kt | 9 +- .../core/interactor/StatisticsMediator.kt | 37 ++-- .../mapper/RunningRecordViewDataMapper.kt | 15 +- .../simpletimetracker/core/utils/TestUtils.kt | 22 ++- .../data_local/database/AppDatabase.kt | 2 + .../data_local/database/RecordTypeGoalDao.kt | 31 ++++ .../data_local/di/DataLocalModule.kt | 7 + .../data_local/di/DataLocalModuleBinds.kt | 6 + .../mapper/RecordTypeDataLocalMapper.kt | 4 - .../mapper/RecordTypeGoalDataLocalMapper.kt | 45 +++++ .../data_local/repo/RecordTypeGoalRepoImpl.kt | 49 +++++ .../data_local/resolver/BackupRepoImpl.kt | 168 ++++++++++++++---- .../data_local/resolver/CsvRepoImpl.kt | 4 - .../domain/extension/GoalsExtensions.kt | 43 +++++ .../domain/interactor/ClearDataInteractor.kt | 3 + .../interactor/RecordTypeGoalInteractor.kt | 26 +++ .../domain/interactor/RecordTypeInteractor.kt | 3 + .../domain/model/RecordType.kt | 4 - .../domain/model/RecordTypeGoal.kt | 6 +- .../domain/repo/RecordTypeGoalRepo.kt | 18 ++ .../viewModel/ChangeRecordTypeViewModel.kt | 70 ++++++-- .../ChangeRunningRecordViewDataInteractor.kt | 5 + .../GetDefaultRecordTypesInteractor.kt | 4 - .../NotificationGoalTimeInteractorImpl.kt | 25 ++- .../NotificationTypeInteractorImpl.kt | 14 +- .../interactor/RecordsViewDataInteractor.kt | 9 +- .../RecordsAllViewDataInteractor.kt | 8 +- .../RecordsFilterViewDataInteractor.kt | 3 + .../viewModel/RecordsFilterViewModel.kt | 27 ++- .../RunningRecordsViewDataInteractor.kt | 10 +- .../StatisticsViewDataInteractor.kt | 7 +- .../StatisticsDetailChartInteractor.kt | 16 +- .../WidgetStatisticsChartProvider.kt | 9 +- 33 files changed, 594 insertions(+), 115 deletions(-) create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTypeGoalDao.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeGoalDataLocalMapper.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTypeGoalRepoImpl.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/extension/GoalsExtensions.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeGoalInteractor.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTypeGoalRepo.kt diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/GetRunningRecordViewDataMediator.kt b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/GetRunningRecordViewDataMediator.kt index c8c34a43..d7640a57 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/GetRunningRecordViewDataMediator.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/GetRunningRecordViewDataMediator.kt @@ -1,8 +1,11 @@ package com.example.util.simpletimetracker.core.interactor import com.example.util.simpletimetracker.core.mapper.RunningRecordViewDataMapper +import com.example.util.simpletimetracker.domain.extension.hasMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.hasWeeklyDuration import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.runningRecord.RunningRecordViewData import javax.inject.Inject @@ -15,6 +18,7 @@ class GetRunningRecordViewDataMediator @Inject constructor( suspend fun execute( type: RecordType, tags: List, + goals: List, record: RunningRecord, nowIconVisible: Boolean, goalsVisible: Boolean, @@ -29,12 +33,12 @@ class GetRunningRecordViewDataMediator @Inject constructor( } else { null } - val weeklyCurrent = if (type.weeklyGoalTime > 0L && goalsVisible) { + val weeklyCurrent = if (goals.hasWeeklyDuration() && goalsVisible) { getCurrentRecordsDurationInteractor.getWeeklyCurrent(record).duration } else { 0L } - val monthlyCurrent = if (type.monthlyGoalTime > 0L && goalsVisible) { + val monthlyCurrent = if (goals.hasMonthlyDuration() && goalsVisible) { getCurrentRecordsDurationInteractor.getMonthlyCurrent(record).duration } else { 0L @@ -47,6 +51,7 @@ class GetRunningRecordViewDataMediator @Inject constructor( monthlyCurrent = monthlyCurrent, recordType = type, recordTags = tags, + goals = goals, isDarkTheme = isDarkTheme, useMilitaryTime = useMilitaryTime, showSeconds = showSeconds, diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/StatisticsMediator.kt b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/StatisticsMediator.kt index 213de175..cbf3b37f 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/StatisticsMediator.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/StatisticsMediator.kt @@ -4,9 +4,17 @@ import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.viewData.StatisticsDataHolder import com.example.util.simpletimetracker.domain.UNCATEGORIZED_ITEM_ID import com.example.util.simpletimetracker.domain.UNTRACKED_ITEM_ID +import com.example.util.simpletimetracker.domain.extension.getDailyDuration +import com.example.util.simpletimetracker.domain.extension.getMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration +import com.example.util.simpletimetracker.domain.extension.hasDailyDuration +import com.example.util.simpletimetracker.domain.extension.hasMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.hasWeeklyDuration +import com.example.util.simpletimetracker.domain.extension.value import com.example.util.simpletimetracker.domain.interactor.CategoryInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.StatisticsCategoryInteractor import com.example.util.simpletimetracker.domain.interactor.StatisticsInteractor @@ -15,6 +23,7 @@ import com.example.util.simpletimetracker.domain.model.ChartFilterType import com.example.util.simpletimetracker.domain.model.Range import com.example.util.simpletimetracker.domain.model.RangeLength import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.Statistics import javax.inject.Inject @@ -27,6 +36,7 @@ class StatisticsMediator @Inject constructor( private val timeMapper: TimeMapper, private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, ) { suspend fun getStatistics( @@ -56,6 +66,7 @@ class StatisticsMediator @Inject constructor( suspend fun getDataHolders( filterType: ChartFilterType, types: Map, + goals: Map>, ): Map { return when (filterType) { ChartFilterType.ACTIVITY -> { @@ -64,12 +75,13 @@ class StatisticsMediator @Inject constructor( name = type.name, color = type.color, icon = type.icon, - dailyGoalTime = type.dailyGoalTime, - weeklyGoalTime = type.weeklyGoalTime, - monthlyGoalTime = type.monthlyGoalTime, + dailyGoalTime = goals[type.id]?.getDailyDuration().value, + weeklyGoalTime = goals[type.id]?.getWeeklyDuration().value, + monthlyGoalTime = goals[type.id]?.getMonthlyDuration().value, ) } } + ChartFilterType.CATEGORY -> { val categories = categoryInteractor.getAll() categories.map { category -> @@ -83,6 +95,7 @@ class StatisticsMediator @Inject constructor( ) } } + ChartFilterType.RECORD_TAG -> { val tags = recordTagInteractor.getAll() tags.map { tag -> @@ -128,13 +141,15 @@ class StatisticsMediator @Inject constructor( return emptyList() } + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) return recordTypeInteractor.getAll() .filter { type -> + val typeGoals = goals[type.id].orEmpty() when { type.hidden -> false - rangeLength is RangeLength.Day -> type.dailyGoalTime > 0L - rangeLength is RangeLength.Week -> type.weeklyGoalTime > 0L - rangeLength is RangeLength.Month -> type.monthlyGoalTime > 0L + rangeLength is RangeLength.Day -> typeGoals.hasDailyDuration() + rangeLength is RangeLength.Week -> typeGoals.hasWeeklyDuration() + rangeLength is RangeLength.Month -> typeGoals.hasMonthlyDuration() else -> false } }.map { type -> @@ -142,7 +157,7 @@ class StatisticsMediator @Inject constructor( id = type.id, duration = statistics .filter { it.id == type.id } - .sumOf { it.duration } + .sumOf(Statistics::duration), ) } } @@ -157,21 +172,23 @@ class StatisticsMediator @Inject constructor( ChartFilterType.ACTIVITY -> { statisticsInteractor.getFromRange( range = range, - addUntracked = addUntracked + addUntracked = addUntracked, ) } + ChartFilterType.CATEGORY -> { statisticsCategoryInteractor.getFromRange( range = range, addUntracked = addUntracked, - addUncategorized = addUncategorized + addUncategorized = addUncategorized, ) } + ChartFilterType.RECORD_TAG -> { statisticsTagInteractor.getFromRange( range = range, addUntracked = addUntracked, - addUncategorized = addUncategorized + addUncategorized = addUncategorized, ) } } diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/mapper/RunningRecordViewDataMapper.kt b/core/src/main/java/com/example/util/simpletimetracker/core/mapper/RunningRecordViewDataMapper.kt index 0706bfe3..be8ad8c8 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/mapper/RunningRecordViewDataMapper.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/mapper/RunningRecordViewDataMapper.kt @@ -3,11 +3,17 @@ package com.example.util.simpletimetracker.core.mapper import com.example.util.simpletimetracker.core.R import com.example.util.simpletimetracker.core.interactor.GetCurrentRecordsDurationInteractor import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.extension.getDailyDuration import com.example.util.simpletimetracker.domain.extension.getFullName +import com.example.util.simpletimetracker.domain.extension.getMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.getSessionDuration +import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration import com.example.util.simpletimetracker.domain.extension.orZero +import com.example.util.simpletimetracker.domain.extension.value import com.example.util.simpletimetracker.domain.model.GoalTimeType import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.runningRecord.RunningRecordViewData import javax.inject.Inject @@ -27,6 +33,7 @@ class RunningRecordViewDataMapper @Inject constructor( monthlyCurrent: Long, recordType: RecordType, recordTags: List, + goals: List, isDarkTheme: Boolean, useMilitaryTime: Boolean, showSeconds: Boolean, @@ -59,25 +66,25 @@ class RunningRecordViewDataMapper @Inject constructor( useProportionalMinutes = useProportionalMinutes, ), goalTime = goalTimeMapper.map( - goalTime = recordType.goalTime, + goalTime = goals.getSessionDuration().value, current = currentDuration, type = GoalTimeType.Session, goalsVisible = goalsVisible, ), goalTime2 = goalTimeMapper.map( - goalTime = recordType.dailyGoalTime, + goalTime = goals.getDailyDuration().value, current = dailyCurrent?.duration.orZero(), type = GoalTimeType.Day, goalsVisible = goalsVisible, ), goalTime3 = goalTimeMapper.map( - goalTime = recordType.weeklyGoalTime, + goalTime = goals.getWeeklyDuration().value, current = weeklyCurrent, type = GoalTimeType.Week, goalsVisible = goalsVisible, ), goalTime4 = goalTimeMapper.map( - goalTime = recordType.monthlyGoalTime, + goalTime = goals.getMonthlyDuration().value, current = monthlyCurrent, type = GoalTimeType.Month, goalsVisible = goalsVisible, diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/utils/TestUtils.kt b/core/src/main/java/com/example/util/simpletimetracker/core/utils/TestUtils.kt index dd174b35..d2932628 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/utils/TestUtils.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/utils/TestUtils.kt @@ -10,6 +10,7 @@ import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor import com.example.util.simpletimetracker.domain.model.ActivityFilter @@ -19,6 +20,7 @@ import com.example.util.simpletimetracker.domain.model.DayOfWeek import com.example.util.simpletimetracker.domain.model.Record import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -32,6 +34,7 @@ class TestUtils @Inject constructor( private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor, private val recordTagInteractor: RecordTagInteractor, private val activityFilterInteractor: ActivityFilterInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val prefsInteractor: PrefsInteractor, private val iconImageMapper: IconImageMapper, private val clearDataInteractor: ClearDataInteractor, @@ -56,9 +59,7 @@ class TestUtils @Inject constructor( icon: Int? = null, text: String? = null, goalTime: Long? = null, - dailyGoalTime: Long? = null, - weeklyGoalTime: Long? = null, - monthlyGoalTime: Long? = null, + goalRange: RecordTypeGoal.Range? = null, archived: Boolean = false, categories: List = emptyList(), ) = runBlocking { @@ -79,10 +80,6 @@ class TestUtils @Inject constructor( color = AppColor(colorId = colorId, colorInt = colorInt?.toString().orEmpty()), icon = iconId, hidden = archived, - goalTime = goalTime.orZero(), - dailyGoalTime = dailyGoalTime.orZero(), - weeklyGoalTime = weeklyGoalTime.orZero(), - monthlyGoalTime = monthlyGoalTime.orZero(), ) val typeId = recordTypeInteractor.add(data) @@ -97,6 +94,16 @@ class TestUtils @Inject constructor( ?.let { categoryIds -> recordTypeCategoryInteractor.addCategories(typeId, categoryIds) } + + if (goalTime != null && goalRange != null) { + RecordTypeGoal( + typeId = typeId, + range = goalRange, + type = RecordTypeGoal.Type.Duration(goalTime), + ).let { + recordTypeGoalInteractor.add(it) + } + } } fun addRecord( @@ -192,6 +199,7 @@ class TestUtils @Inject constructor( is ActivityFilter.Type.Activity -> { availableTypes.firstOrNull { it.name == name }?.id } + is ActivityFilter.Type.Category -> { availableCategories.firstOrNull { it.name == name }?.id } diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt index 40b4fed1..21f13168 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt @@ -53,6 +53,8 @@ abstract class AppDatabase : RoomDatabase() { abstract fun favouriteCommentDao(): FavouriteCommentDao + abstract fun recordTypeGoalDao(): RecordTypeGoalDao + companion object { const val DATABASE_NAME = "simpleTimeTrackerDB" } diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTypeGoalDao.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTypeGoalDao.kt new file mode 100644 index 00000000..faf0ee5d --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTypeGoalDao.kt @@ -0,0 +1,31 @@ +package com.example.util.simpletimetracker.data_local.database + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.example.util.simpletimetracker.data_local.model.RecordTypeGoalDBO + +@Dao +interface RecordTypeGoalDao { + + @Query("SELECT * FROM recordTypeGoals") + suspend fun getAll(): List + + @Transaction + @Query("SELECT * FROM recordTypeGoals WHERE type_id = :typeId") + suspend fun getByType(typeId: Long): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(recordTypeGoal: RecordTypeGoalDBO): Long + + @Query("DELETE FROM recordTypeGoals WHERE id = :id") + suspend fun delete(id: Long) + + @Query("DELETE FROM recordTypeGoals WHERE type_id = :typeId") + suspend fun deleteByType(typeId: Long) + + @Query("DELETE FROM recordTypeGoals") + suspend fun clear() +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt index cf4287c7..addf4113 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt @@ -14,6 +14,7 @@ import com.example.util.simpletimetracker.data_local.database.RecordTagDao import com.example.util.simpletimetracker.data_local.database.RecordToRecordTagDao import com.example.util.simpletimetracker.data_local.database.RecordTypeCategoryDao import com.example.util.simpletimetracker.data_local.database.RecordTypeDao +import com.example.util.simpletimetracker.data_local.database.RecordTypeGoalDao import com.example.util.simpletimetracker.data_local.database.RunningRecordDao import com.example.util.simpletimetracker.data_local.database.RunningRecordToRecordTagDao import dagger.Module @@ -112,4 +113,10 @@ class DataLocalModule { fun getFavouriteCommentDao(database: AppDatabase): FavouriteCommentDao { return database.favouriteCommentDao() } + + @Provides + @Singleton + fun getRecordTypeGoalDao(database: AppDatabase): RecordTypeGoalDao { + return database.recordTypeGoalDao() + } } \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt index d2f85696..272b4d27 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt @@ -10,6 +10,7 @@ import com.example.util.simpletimetracker.data_local.repo.RecordRepoImpl import com.example.util.simpletimetracker.data_local.repo.RecordTagRepoImpl import com.example.util.simpletimetracker.data_local.repo.RecordToRecordTagRepoImpl import com.example.util.simpletimetracker.data_local.repo.RecordTypeCategoryRepoImpl +import com.example.util.simpletimetracker.data_local.repo.RecordTypeGoalRepoImpl import com.example.util.simpletimetracker.data_local.repo.RecordTypeRepoImpl import com.example.util.simpletimetracker.data_local.repo.RunningRecordRepoImpl import com.example.util.simpletimetracker.data_local.repo.RunningRecordToRecordTagRepoImpl @@ -25,6 +26,7 @@ import com.example.util.simpletimetracker.domain.repo.RecordRepo import com.example.util.simpletimetracker.domain.repo.RecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordToRecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeCategoryRepo +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeRepo import com.example.util.simpletimetracker.domain.repo.RunningRecordRepo import com.example.util.simpletimetracker.domain.repo.RunningRecordToRecordTagRepo @@ -102,6 +104,10 @@ interface DataLocalModuleBinds { @Singleton fun FavouriteCommentRepoImpl.bindFavouriteCommentRepo(): FavouriteCommentRepo + @Binds + @Singleton + fun RecordTypeGoalRepoImpl.bindRecordTypeGoalRepo(): RecordTypeGoalRepo + @Binds @Singleton fun DataEditRepoImpl.bindDataEditRepo(): DataEditRepo diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeDataLocalMapper.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeDataLocalMapper.kt index b6426775..445a3ddf 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeDataLocalMapper.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeDataLocalMapper.kt @@ -17,10 +17,6 @@ class RecordTypeDataLocalMapper @Inject constructor() { colorInt = dbo.colorInt ), hidden = dbo.hidden, - goalTime = 0, - dailyGoalTime = 0, - weeklyGoalTime = 0, - monthlyGoalTime = 0, ) } diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeGoalDataLocalMapper.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeGoalDataLocalMapper.kt new file mode 100644 index 00000000..9d75cdc5 --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/RecordTypeGoalDataLocalMapper.kt @@ -0,0 +1,45 @@ +package com.example.util.simpletimetracker.data_local.mapper + +import com.example.util.simpletimetracker.data_local.model.RecordTypeGoalDBO +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal +import javax.inject.Inject + +class RecordTypeGoalDataLocalMapper @Inject constructor() { + + fun map(dbo: RecordTypeGoalDBO): RecordTypeGoal { + return RecordTypeGoal( + id = dbo.id, + typeId = dbo.typeId, + range = when (dbo.range) { + 0L -> RecordTypeGoal.Range.Session + 1L -> RecordTypeGoal.Range.Daily + 2L -> RecordTypeGoal.Range.Weekly + 3L -> RecordTypeGoal.Range.Monthly + else -> RecordTypeGoal.Range.Session + }, + type = when (dbo.type) { + 0L -> RecordTypeGoal.Type.Duration(dbo.value) + 1L -> RecordTypeGoal.Type.Count(dbo.value) + else -> RecordTypeGoal.Type.Duration(dbo.value) + }, + ) + } + + fun map(domain: RecordTypeGoal): RecordTypeGoalDBO { + return RecordTypeGoalDBO( + id = domain.id, + typeId = domain.typeId, + range = when (domain.range) { + is RecordTypeGoal.Range.Session -> 0L + is RecordTypeGoal.Range.Daily -> 1L + is RecordTypeGoal.Range.Weekly -> 2L + is RecordTypeGoal.Range.Monthly -> 3L + }, + type = when (domain.type) { + is RecordTypeGoal.Type.Duration -> 0L + is RecordTypeGoal.Type.Count -> 1L + }, + value = domain.type.value, + ) + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTypeGoalRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTypeGoalRepoImpl.kt new file mode 100644 index 00000000..79975291 --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTypeGoalRepoImpl.kt @@ -0,0 +1,49 @@ +package com.example.util.simpletimetracker.data_local.repo + +import com.example.util.simpletimetracker.data_local.database.RecordTypeGoalDao +import com.example.util.simpletimetracker.data_local.mapper.RecordTypeGoalDataLocalMapper +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import timber.log.Timber + +class RecordTypeGoalRepoImpl @Inject constructor( + private val dao: RecordTypeGoalDao, + private val mapper: RecordTypeGoalDataLocalMapper, +) : RecordTypeGoalRepo { + + override suspend fun getAll(): List = withContext(Dispatchers.IO) { + Timber.d("getAll") + dao.getAll().map(mapper::map) + } + + override suspend fun getByType(typeId: Long): List = withContext(Dispatchers.IO) { + Timber.d("getByType") + dao.getByType(typeId) + .map(mapper::map) + } + + override suspend fun add(recordTypeGoal: RecordTypeGoal): Long = withContext(Dispatchers.IO) { + Timber.d("add") + return@withContext dao.insert( + recordTypeGoal.let(mapper::map), + ) + } + + override suspend fun remove(id: Long) = withContext(Dispatchers.IO) { + Timber.d("remove") + dao.delete(id) + } + + override suspend fun removeByType(typeId: Long) = withContext(Dispatchers.IO) { + Timber.d("removeByType") + dao.deleteByType(typeId) + } + + override suspend fun clear() = withContext(Dispatchers.IO) { + Timber.d("clear") + dao.clear() + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/BackupRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/BackupRepoImpl.kt index 89c57883..931f7cb4 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/BackupRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/BackupRepoImpl.kt @@ -16,6 +16,7 @@ import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordToRecordTag import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RecordTypeCategory +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo import com.example.util.simpletimetracker.domain.repo.CategoryRepo import com.example.util.simpletimetracker.domain.repo.FavouriteCommentRepo @@ -23,6 +24,7 @@ import com.example.util.simpletimetracker.domain.repo.RecordRepo import com.example.util.simpletimetracker.domain.repo.RecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordToRecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeCategoryRepo +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeRepo import com.example.util.simpletimetracker.domain.resolver.BackupRepo import com.example.util.simpletimetracker.domain.resolver.ResultCode @@ -52,12 +54,13 @@ class BackupRepoImpl @Inject constructor( private val recordTagRepo: RecordTagRepo, private val activityFilterRepo: ActivityFilterRepo, private val favouriteCommentRepo: FavouriteCommentRepo, + private val recordTypeGoalRepo: RecordTypeGoalRepo, private val clearDataInteractor: ClearDataInteractor, private val resourceRepo: ResourceRepo, ) : BackupRepo { override suspend fun saveBackupFile( - uriString: String + uriString: String, ): ResultCode = withContext(Dispatchers.IO) { var fileDescriptor: ParcelFileDescriptor? = null var fileOutputStream: FileOutputStream? = null @@ -74,42 +77,47 @@ class BackupRepoImpl @Inject constructor( // Write data recordTypeRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } recordRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } categoryRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } recordTypeCategoryRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } recordTagRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } recordToRecordTagRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } activityFilterRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), ) } favouriteCommentRepo.getAll().forEach { fileOutputStream?.write( - it.let(::toBackupString).toByteArray() + it.let(::toBackupString).toByteArray(), + ) + } + recordTypeGoalRepo.getAll().forEach { + fileOutputStream?.write( + it.let(::toBackupString).toByteArray(), ) } @@ -130,7 +138,7 @@ class BackupRepoImpl @Inject constructor( } override suspend fun restoreBackupFile( - uriString: String + uriString: String, ): ResultCode = withContext(Dispatchers.IO) { var inputStream: InputStream? = null var reader: BufferedReader? = null @@ -155,10 +163,14 @@ class BackupRepoImpl @Inject constructor( parts = line.split("\t") when (parts[0]) { ROW_RECORD_TYPE -> { - recordTypeFromBackupString(parts).let { - recordTypeRepo.add(it) + recordTypeFromBackupString(parts).let { (type, goals) -> + recordTypeRepo.add(type) + goals.forEach { + recordTypeGoalRepo.add(it) + } } } + ROW_RECORD -> { recordFromBackupString(parts).let { (record, recordToRecordTag) -> recordRepo.add(record) @@ -167,36 +179,48 @@ class BackupRepoImpl @Inject constructor( } } } + ROW_CATEGORY -> { categoryFromBackupString(parts).let { categoryRepo.add(it) } } + ROW_TYPE_CATEGORY -> { typeCategoryFromBackupString(parts).let { recordTypeCategoryRepo.add(it) } } + ROW_RECORD_TAG -> { recordTagFromBackupString(parts).let { recordTagRepo.add(it) } } + ROW_RECORD_TO_RECORD_TAG -> { recordToRecordTagFromBackupString(parts).let { recordToRecordTagRepo.add(it) } } + ROW_ACTIVITY_FILTER -> { activityFilterFromBackupString(parts).let { activityFilterRepo.add(it) } } + ROW_FAVOURITE_COMMENT -> { favouriteCommentFromBackupString(parts).let { favouriteCommentRepo.add(it) } } + + ROW_RECORD_TYPE_GOAL -> { + recordTypeGoalFromBackupString(parts).let { + recordTypeGoalRepo.add(it) + } + } } } ResultCode.Success(resourceRepo.getString(R.string.message_backup_restored)) @@ -221,11 +245,11 @@ class BackupRepoImpl @Inject constructor( recordType.icon, recordType.color.colorId.toString(), (if (recordType.hidden) 1 else 0).toString(), - recordType.goalTime.toString(), + "", // goal time is moved to separate table recordType.color.colorInt, - recordType.dailyGoalTime.toString(), - recordType.weeklyGoalTime.toString(), - recordType.monthlyGoalTime.toString(), + "", // daily goal time is moved to separate table + "", // weekly goal time is moved to separate table + "", // monthly goal time is moved to separate table ) } @@ -255,7 +279,7 @@ class BackupRepoImpl @Inject constructor( return String.format( "$ROW_TYPE_CATEGORY\t%s\t%s\n", recordTypeCategory.recordTypeId.toString(), - recordTypeCategory.categoryId.toString() + recordTypeCategory.categoryId.toString(), ) } @@ -275,7 +299,7 @@ class BackupRepoImpl @Inject constructor( return String.format( "$ROW_RECORD_TO_RECORD_TAG\t%s\t%s\n", recordToRecordTag.recordId.toString(), - recordToRecordTag.recordTagId.toString() + recordToRecordTag.recordTagId.toString(), ) } @@ -305,9 +329,70 @@ class BackupRepoImpl @Inject constructor( ) } - private fun recordTypeFromBackupString(parts: List): RecordType { + private fun toBackupString(recordTypeGoal: RecordTypeGoal): String { + val rangeString = when (recordTypeGoal.range) { + is RecordTypeGoal.Range.Session -> 0L + is RecordTypeGoal.Range.Daily -> 1L + is RecordTypeGoal.Range.Weekly -> 2L + is RecordTypeGoal.Range.Monthly -> 3L + }.toString() + val typeString = when (recordTypeGoal.type) { + is RecordTypeGoal.Type.Duration -> 0L + is RecordTypeGoal.Type.Count -> 1L + }.toString() + + return String.format( + "$ROW_RECORD_TYPE_GOAL\t%s\t%s\t%s\t%s\t%s\n", + recordTypeGoal.id.toString(), + recordTypeGoal.typeId.toString(), + rangeString, + typeString, + recordTypeGoal.type.value.toString(), + ) + } + + private fun recordTypeFromBackupString(parts: List): Pair> { + val typeId = parts.getOrNull(1)?.toLongOrNull().orZero() + + // goal times are moved to separate database, need to support old backup files. + val goalTime = parts.getOrNull(6)?.toLongOrNull().orZero() + val dailyGoalTime = parts.getOrNull(8)?.toLongOrNull().orZero() + val weeklyGoalTime = parts.getOrNull(9)?.toLongOrNull().orZero() + val monthlyGoalTime = parts.getOrNull(10)?.toLongOrNull().orZero() + + val goalTimes = mutableListOf().apply { + if (goalTime != 0L) { + RecordTypeGoal( + typeId = typeId, + range = RecordTypeGoal.Range.Session, + type = RecordTypeGoal.Type.Duration(goalTime), + ).let(::add) + } + if (dailyGoalTime != 0L) { + RecordTypeGoal( + typeId = typeId, + range = RecordTypeGoal.Range.Daily, + type = RecordTypeGoal.Type.Duration(dailyGoalTime), + ).let(::add) + } + if (weeklyGoalTime != 0L) { + RecordTypeGoal( + typeId = typeId, + range = RecordTypeGoal.Range.Weekly, + type = RecordTypeGoal.Type.Duration(weeklyGoalTime), + ).let(::add) + } + if (monthlyGoalTime != 0L) { + RecordTypeGoal( + typeId = typeId, + range = RecordTypeGoal.Range.Monthly, + type = RecordTypeGoal.Type.Duration(monthlyGoalTime), + ).let(::add) + } + } + return RecordType( - id = parts.getOrNull(1)?.toLongOrNull().orZero(), + id = typeId, name = parts.getOrNull(2).orEmpty(), icon = parts.getOrNull(3).orEmpty(), color = AppColor( @@ -315,11 +400,11 @@ class BackupRepoImpl @Inject constructor( colorInt = parts.getOrNull(7).orEmpty(), ), hidden = parts.getOrNull(5)?.toIntOrNull() == 1, - goalTime = parts.getOrNull(6)?.toLongOrNull().orZero(), - dailyGoalTime = parts.getOrNull(8)?.toLongOrNull().orZero(), - weeklyGoalTime = parts.getOrNull(9)?.toLongOrNull().orZero(), - monthlyGoalTime = parts.getOrNull(10)?.toLongOrNull().orZero(), - ) + // parts[6] - goal time is moved to separate table + // parts[8] - daily time is moved to separate table + // parts[9] - weekly time is moved to separate table + // parts[10] - monthly time is moved to separate table + ) to goalTimes } private fun recordFromBackupString(parts: List): Pair { @@ -341,7 +426,7 @@ class BackupRepoImpl @Inject constructor( // parts[6] - tag id is removed from record dbo. ) to RecordToRecordTag( recordId = recordId, - recordTagId = tagId + recordTagId = tagId, ).takeUnless { tagId == 0L } } @@ -352,14 +437,14 @@ class BackupRepoImpl @Inject constructor( color = AppColor( colorId = parts.getOrNull(3)?.toIntOrNull().orZero(), colorInt = parts.getOrNull(4).orEmpty(), - ) + ), ) } private fun typeCategoryFromBackupString(parts: List): RecordTypeCategory { return RecordTypeCategory( recordTypeId = parts.getOrNull(1)?.toLongOrNull().orZero(), - categoryId = parts.getOrNull(2)?.toLongOrNull().orZero() + categoryId = parts.getOrNull(2)?.toLongOrNull().orZero(), ) } @@ -372,14 +457,14 @@ class BackupRepoImpl @Inject constructor( color = AppColor( colorId = parts.getOrNull(5)?.toIntOrNull().orZero(), colorInt = parts.getOrNull(6).orEmpty(), - ) + ), ) } private fun recordToRecordTagFromBackupString(parts: List): RecordToRecordTag { return RecordToRecordTag( recordId = parts.getOrNull(1)?.toLongOrNull().orZero(), - recordTagId = parts.getOrNull(2)?.toLongOrNull().orZero() + recordTagId = parts.getOrNull(2)?.toLongOrNull().orZero(), ) } @@ -411,6 +496,28 @@ class BackupRepoImpl @Inject constructor( ) } + private fun recordTypeGoalFromBackupString(parts: List): RecordTypeGoal { + return RecordTypeGoal( + id = parts.getOrNull(1)?.toLongOrNull().orZero(), + typeId = parts.getOrNull(2)?.toLongOrNull() ?: 1L, + range = when (parts.getOrNull(3)?.toLongOrNull()) { + 0L -> RecordTypeGoal.Range.Session + 1L -> RecordTypeGoal.Range.Daily + 2L -> RecordTypeGoal.Range.Weekly + 3L -> RecordTypeGoal.Range.Monthly + else -> RecordTypeGoal.Range.Session + }, + type = run { + val value = parts.getOrNull(5)?.toLongOrNull().orZero() + when (parts.getOrNull(4)?.toLongOrNull()) { + 0L -> RecordTypeGoal.Type.Duration(value) + 1L -> RecordTypeGoal.Type.Count(value) + else -> RecordTypeGoal.Type.Duration(value) + } + }, + ) + } + private fun String.clean() = cleanTabs().cleanNewline() @@ -436,5 +543,6 @@ class BackupRepoImpl @Inject constructor( private const val ROW_RECORD_TO_RECORD_TAG = "recordToRecordTag" private const val ROW_ACTIVITY_FILTER = "activityFilter" private const val ROW_FAVOURITE_COMMENT = "favouriteComment" + private const val ROW_RECORD_TYPE_GOAL = "recordTypeGoal" } } diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/CsvRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/CsvRepoImpl.kt index 2f142e50..92ce987a 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/CsvRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/resolver/CsvRepoImpl.kt @@ -145,10 +145,6 @@ class CsvRepoImpl @Inject constructor( colorInt = "", ), hidden = false, - goalTime = 0, - dailyGoalTime = 0, - weeklyGoalTime = 0, - monthlyGoalTime = 0, ) val newTypeId = recordTypeRepo.add(newType) newType.copy(id = newTypeId).let(newAddedTypes::add) diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/extension/GoalsExtensions.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/extension/GoalsExtensions.kt new file mode 100644 index 00000000..ab0cf177 --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/extension/GoalsExtensions.kt @@ -0,0 +1,43 @@ +package com.example.util.simpletimetracker.domain.extension + +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal.Range +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal.Type + +fun List.getSessionDuration(): RecordTypeGoal? { + return firstOrNull { + it.range is Range.Session && it.type is Type.Duration + } +} + +fun List.getDailyDuration(): RecordTypeGoal? { + return firstOrNull { + it.range is Range.Daily && it.type is Type.Duration + } +} + +fun List.getWeeklyDuration(): RecordTypeGoal? { + return firstOrNull { + it.range is Range.Weekly && it.type is Type.Duration + } +} + +fun List.getMonthlyDuration(): RecordTypeGoal? { + return firstOrNull { + it.range is Range.Monthly && it.type is Type.Duration + } +} + +fun List.hasDailyDuration(): Boolean { + return getDailyDuration() != null +} + +fun List.hasWeeklyDuration(): Boolean { + return getWeeklyDuration() != null +} + +fun List.hasMonthlyDuration(): Boolean { + return getMonthlyDuration() != null +} + +val RecordTypeGoal?.value: Long get() = this?.type?.value.orZero() diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ClearDataInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ClearDataInteractor.kt index c0c3b457..be0c3fbb 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ClearDataInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ClearDataInteractor.kt @@ -7,6 +7,7 @@ import com.example.util.simpletimetracker.domain.repo.RecordRepo import com.example.util.simpletimetracker.domain.repo.RecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordToRecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeCategoryRepo +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeRepo import com.example.util.simpletimetracker.domain.repo.RunningRecordRepo import com.example.util.simpletimetracker.domain.repo.RunningRecordToRecordTagRepo @@ -23,6 +24,7 @@ class ClearDataInteractor @Inject constructor( private val runningRecordRepo: RunningRecordRepo, private val runningRecordToRecordTagRepo: RunningRecordToRecordTagRepo, private val favouriteCommentRepo: FavouriteCommentRepo, + private val recordTypeGoalRepo: RecordTypeGoalRepo, ) { suspend fun execute() { @@ -36,5 +38,6 @@ class ClearDataInteractor @Inject constructor( runningRecordRepo.clear() runningRecordToRecordTagRepo.clear() favouriteCommentRepo.clear() + recordTypeGoalRepo.clear() } } \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeGoalInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeGoalInteractor.kt new file mode 100644 index 00000000..c09c06fc --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeGoalInteractor.kt @@ -0,0 +1,26 @@ +package com.example.util.simpletimetracker.domain.interactor + +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo +import javax.inject.Inject + +class RecordTypeGoalInteractor @Inject constructor( + private val repo: RecordTypeGoalRepo, +) { + + suspend fun getAll(): List { + return repo.getAll() + } + + suspend fun getByType(typeId: Long): List { + return repo.getByType(typeId) + } + + suspend fun add(recordTypeGoal: RecordTypeGoal) { + repo.add(recordTypeGoal) + } + + suspend fun remove(id: Long) { + repo.remove(id) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeInteractor.kt index 12ac7d01..d913bef0 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeInteractor.kt @@ -8,6 +8,7 @@ import com.example.util.simpletimetracker.domain.repo.RecordRepo import com.example.util.simpletimetracker.domain.repo.RecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordToRecordTagRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeCategoryRepo +import com.example.util.simpletimetracker.domain.repo.RecordTypeGoalRepo import com.example.util.simpletimetracker.domain.repo.RecordTypeRepo import java.util.Locale import javax.inject.Inject @@ -19,6 +20,7 @@ class RecordTypeInteractor @Inject constructor( private val recordToRecordTagRepo: RecordToRecordTagRepo, private val recordTypeCategoryRepo: RecordTypeCategoryRepo, private val recordTagRepo: RecordTagRepo, + private val recordTypeGoalRepo: RecordTypeGoalRepo, private val prefsInteractor: PrefsInteractor, private val appColorMapper: AppColorMapper, ) { @@ -64,6 +66,7 @@ class RecordTypeInteractor @Inject constructor( recordRepo.removeByType(id) recordTypeCategoryRepo.removeAllByType(id) recordTagRepo.removeByType(id) + recordTypeGoalRepo.removeByType(id) recordTypeRepo.remove(id) } diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordType.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordType.kt index d8f47016..c4056970 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordType.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordType.kt @@ -6,8 +6,4 @@ data class RecordType( val icon: String, val color: AppColor, val hidden: Boolean = false, - val goalTime: Long, // Seconds. - val dailyGoalTime: Long, // Seconds. - val weeklyGoalTime: Long, // Seconds. - val monthlyGoalTime: Long, // Seconds. ) \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordTypeGoal.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordTypeGoal.kt index 8baa6f08..4fb17c44 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordTypeGoal.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/RecordTypeGoal.kt @@ -15,7 +15,9 @@ data class RecordTypeGoal( } sealed interface Type { - data class Duration(val seconds: Long) : Type - data class Count(val times: Long) : Type + val value: Long + + data class Duration(override val value: Long) : Type + data class Count(override val value: Long) : Type } } \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTypeGoalRepo.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTypeGoalRepo.kt new file mode 100644 index 00000000..87ee1668 --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTypeGoalRepo.kt @@ -0,0 +1,18 @@ +package com.example.util.simpletimetracker.domain.repo + +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal + +interface RecordTypeGoalRepo { + + suspend fun getAll(): List + + suspend fun getByType(typeId: Long): List + + suspend fun add(recordTypeGoal: RecordTypeGoal): Long + + suspend fun remove(id: Long) + + suspend fun removeByType(typeId: Long) + + suspend fun clear() +} \ No newline at end of file diff --git a/features/feature_change_record_type/src/main/java/com/example/util/simpletimetracker/feature_change_record_type/viewModel/ChangeRecordTypeViewModel.kt b/features/feature_change_record_type/src/main/java/com/example/util/simpletimetracker/feature_change_record_type/viewModel/ChangeRecordTypeViewModel.kt index 5a04768e..cf3b2fd2 100644 --- a/features/feature_change_record_type/src/main/java/com/example/util/simpletimetracker/feature_change_record_type/viewModel/ChangeRecordTypeViewModel.kt +++ b/features/feature_change_record_type/src/main/java/com/example/util/simpletimetracker/feature_change_record_type/viewModel/ChangeRecordTypeViewModel.kt @@ -13,11 +13,17 @@ import com.example.util.simpletimetracker.core.mapper.RecordTypeViewDataMapper import com.example.util.simpletimetracker.core.repo.PermissionRepo import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.core.view.buttonsRowView.ButtonsRowViewData +import com.example.util.simpletimetracker.domain.extension.getDailyDuration +import com.example.util.simpletimetracker.domain.extension.getMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.getSessionDuration +import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration import com.example.util.simpletimetracker.domain.extension.orZero +import com.example.util.simpletimetracker.domain.extension.value 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.RecordTypeCategoryInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RemoveRunningRecordMediator import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor @@ -25,6 +31,7 @@ import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor import com.example.util.simpletimetracker.domain.model.AppColor import com.example.util.simpletimetracker.domain.model.IconType import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.category.CategoryViewData import com.example.util.simpletimetracker.feature_base_adapter.color.ColorViewData @@ -58,6 +65,7 @@ class ChangeRecordTypeViewModel @Inject constructor( private val permissionRepo: PermissionRepo, private val removeRunningRecordMediator: RemoveRunningRecordMediator, private val recordTypeInteractor: RecordTypeInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val runningRecordInteractor: RunningRecordInteractor, private val viewDataInteractor: ChangeRecordTypeViewDataInteractor, private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor, @@ -469,6 +477,7 @@ class ChangeRecordTypeViewModel @Inject constructor( viewModelScope.launch { val addedId = saveRecordType() saveCategories(addedId) + saveGoals(addedId) notificationTypeInteractor.updateNotifications() notificationGoalTimeInteractor.checkAndReschedule(recordTypeId) widgetInteractor.updateWidgets() @@ -518,10 +527,6 @@ class ChangeRecordTypeViewModel @Inject constructor( name = newName, icon = newIconName, color = newColor, - goalTime = newGoalTime, - dailyGoalTime = newDailyGoalTime, - weeklyGoalTime = newWeeklyGoalTime, - monthlyGoalTime = newMonthlyGoalTime, ) return recordTypeInteractor.add(recordType) @@ -535,16 +540,61 @@ class ChangeRecordTypeViewModel @Inject constructor( recordTypeCategoryInteractor.removeCategories(typeId, removedCategories) } + private suspend fun saveGoals(typeId: Long) { + val goals = recordTypeGoalInteractor.getByType(typeId) + + suspend fun processGoal( + goalId: Long, + goalTime: Long, + range: RecordTypeGoal.Range, + ) { + if (goalTime == 0L) { + recordTypeGoalInteractor.remove(goalId) + } else { + RecordTypeGoal( + id = goalId, + typeId = typeId, + range = range, + type = RecordTypeGoal.Type.Duration(goalTime) + ).let { + recordTypeGoalInteractor.add(it) + } + } + } + + processGoal( + goalId = goals.getSessionDuration()?.id.orZero(), + goalTime = newGoalTime, + range = RecordTypeGoal.Range.Session, + ) + processGoal( + goalId = goals.getDailyDuration()?.id.orZero(), + goalTime = newDailyGoalTime, + range = RecordTypeGoal.Range.Daily, + ) + processGoal( + goalId = goals.getWeeklyDuration()?.id.orZero(), + goalTime = newWeeklyGoalTime, + range = RecordTypeGoal.Range.Weekly, + ) + processGoal( + goalId = goals.getMonthlyDuration()?.id.orZero(), + goalTime = newMonthlyGoalTime, + range = RecordTypeGoal.Range.Monthly, + ) + } + private suspend fun initializeRecordTypeData() { recordTypeInteractor.get(recordTypeId) ?.let { + val goals = recordTypeGoalInteractor.getByType(it.id) newName = it.name newIconName = it.icon newColor = it.color - newGoalTime = it.goalTime - newDailyGoalTime = it.dailyGoalTime - newWeeklyGoalTime = it.weeklyGoalTime - newMonthlyGoalTime = it.monthlyGoalTime + newGoalTime = goals.getSessionDuration().value + newDailyGoalTime = goals.getDailyDuration().value + newWeeklyGoalTime = goals.getWeeklyDuration().value + newMonthlyGoalTime = goals.getMonthlyDuration().value updateIcons() updateColors() updateSessionGoalTimeViewData() @@ -574,10 +624,6 @@ class ChangeRecordTypeViewModel @Inject constructor( name = newName, icon = newIconName, color = newColor, - goalTime = 0, - dailyGoalTime = 0, - weeklyGoalTime = 0, - monthlyGoalTime = 0, ).let { recordTypeViewDataMapper.map(it, isDarkTheme) } } diff --git a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/interactor/ChangeRunningRecordViewDataInteractor.kt b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/interactor/ChangeRunningRecordViewDataInteractor.kt index 28242eb8..7ac83a04 100644 --- a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/interactor/ChangeRunningRecordViewDataInteractor.kt +++ b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/interactor/ChangeRunningRecordViewDataInteractor.kt @@ -2,8 +2,10 @@ package com.example.util.simpletimetracker.feature_change_running_record.interac import com.example.util.simpletimetracker.core.interactor.GetRunningRecordViewDataMediator import com.example.util.simpletimetracker.core.mapper.TimeMapper +import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_change_running_record.viewData.ChangeRunningRecordViewData @@ -14,6 +16,7 @@ class ChangeRunningRecordViewDataInteractor @Inject constructor( private val prefsInteractor: PrefsInteractor, private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val timeMapper: TimeMapper, private val getRunningRecordViewDataMediator: GetRunningRecordViewDataMediator, ) { @@ -23,6 +26,7 @@ class ChangeRunningRecordViewDataInteractor @Inject constructor( params: ChangeRunningRecordParams, ): ChangeRunningRecordViewData { val type = recordTypeInteractor.get(record.id) + val goals = recordTypeGoalInteractor.getByType(type?.id.orZero()) val isDarkTheme = prefsInteractor.getDarkMode() val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() val showSeconds = prefsInteractor.getShowSeconds() @@ -33,6 +37,7 @@ class ChangeRunningRecordViewDataInteractor @Inject constructor( getRunningRecordViewDataMediator.execute( type = type, tags = recordTagInteractor.getAll().filter { it.id in record.tagIds }, + goals = goals, record = record, nowIconVisible = fromRecords, goalsVisible = !fromRecords, diff --git a/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/defaultTypesSelection/interactor/GetDefaultRecordTypesInteractor.kt b/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/defaultTypesSelection/interactor/GetDefaultRecordTypesInteractor.kt index f10e08da..df6d3602 100644 --- a/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/defaultTypesSelection/interactor/GetDefaultRecordTypesInteractor.kt +++ b/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/defaultTypesSelection/interactor/GetDefaultRecordTypesInteractor.kt @@ -20,10 +20,6 @@ class GetDefaultRecordTypesInteractor @Inject constructor( name = type.name, icon = type.icon, color = AppColor(colorId = type.colorId, colorInt = ""), - goalTime = 0, - dailyGoalTime = 0, - weeklyGoalTime = 0, - monthlyGoalTime = 0, ) } } diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt index 19059b44..50c16083 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt @@ -5,8 +5,14 @@ import com.example.util.simpletimetracker.core.mapper.ColorMapper import com.example.util.simpletimetracker.core.mapper.IconMapper import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.extension.getDailyDuration +import com.example.util.simpletimetracker.domain.extension.getMonthlyDuration +import com.example.util.simpletimetracker.domain.extension.getSessionDuration +import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration +import com.example.util.simpletimetracker.domain.extension.value import com.example.util.simpletimetracker.domain.interactor.NotificationGoalTimeInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor import com.example.util.simpletimetracker.domain.model.GoalTimeType @@ -22,6 +28,7 @@ import kotlinx.coroutines.launch class NotificationGoalTimeInteractorImpl @Inject constructor( private val resourceRepo: ResourceRepo, private val recordTypeInteractor: RecordTypeInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val runningRecordInteractor: RunningRecordInteractor, private val getCurrentRecordsDurationInteractor: GetCurrentRecordsDurationInteractor, private val prefsInteractor: PrefsInteractor, @@ -42,6 +49,7 @@ class NotificationGoalTimeInteractorImpl @Inject constructor( override suspend fun checkAndReschedule(typeId: Long) { val recordType = recordTypeInteractor.get(typeId) val runningRecord = runningRecordInteractor.get(typeId) + val goals = recordTypeGoalInteractor.getByType(typeId) if (recordType == null || runningRecord == null) return @@ -55,13 +63,13 @@ class NotificationGoalTimeInteractorImpl @Inject constructor( // Session val sessionCurrent = System.currentTimeMillis() - runningRecord.timeStarted - val sessionGoalTime = recordType.goalTime * 1000 + val sessionGoalTime = goals.getSessionDuration().value * 1000 if (sessionGoalTime > 0L && sessionGoalTime > sessionCurrent) { scheduler.schedule(sessionGoalTime - sessionCurrent, typeId, GoalTimeType.Session) } // Daily - val dailyGoalTime = recordType.dailyGoalTime * 1000 + val dailyGoalTime = goals.getDailyDuration().value * 1000 if (dailyGoalTime > 0) { val dailyCurrent = getCurrentRecordsDurationInteractor.getDailyCurrent(runningRecord) @@ -76,7 +84,7 @@ class NotificationGoalTimeInteractorImpl @Inject constructor( } // Weekly - val weeklyGoalTime = recordType.weeklyGoalTime * 1000 + val weeklyGoalTime = goals.getWeeklyDuration().value * 1000 if (weeklyGoalTime > 0) { val weeklyCurrent = getCurrentRecordsDurationInteractor.getWeeklyCurrent(runningRecord) if (weeklyGoalTime > weeklyCurrent.duration) { @@ -90,7 +98,7 @@ class NotificationGoalTimeInteractorImpl @Inject constructor( } // Monthly - val monthlyGoalTime = recordType.monthlyGoalTime * 1000 + val monthlyGoalTime = goals.getMonthlyDuration().value * 1000 if (monthlyGoalTime > 0) { val monthlyCurrent = getCurrentRecordsDurationInteractor.getMonthlyCurrent(runningRecord) if (monthlyGoalTime > monthlyCurrent.duration) { @@ -119,13 +127,14 @@ class NotificationGoalTimeInteractorImpl @Inject constructor( override fun show(typeId: Long, goalTimeType: GoalTimeType) { GlobalScope.launch { val recordType = recordTypeInteractor.get(typeId) ?: return@launch + val goals = recordTypeGoalInteractor.getByType(typeId) val isDarkTheme = prefsInteractor.getDarkMode() val goalTimeString = when (goalTimeType) { - is GoalTimeType.Session -> recordType.goalTime - is GoalTimeType.Day -> recordType.dailyGoalTime - is GoalTimeType.Week -> recordType.weeklyGoalTime - is GoalTimeType.Month -> recordType.monthlyGoalTime + is GoalTimeType.Session -> goals.getSessionDuration().value + is GoalTimeType.Day -> goals.getDailyDuration().value + is GoalTimeType.Week -> goals.getWeeklyDuration().value + is GoalTimeType.Month -> goals.getMonthlyDuration().value }.let(timeMapper::formatDuration) val goalTimeTypeString = when (goalTimeType) { is GoalTimeType.Session -> R.string.change_record_type_session_goal_time diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/NotificationTypeInteractorImpl.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/NotificationTypeInteractorImpl.kt index 7957083c..c3c07dbf 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/NotificationTypeInteractorImpl.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recordType/interactor/NotificationTypeInteractorImpl.kt @@ -6,13 +6,17 @@ import com.example.util.simpletimetracker.core.mapper.IconMapper import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.extension.getFullName +import com.example.util.simpletimetracker.domain.extension.getSessionDuration +import com.example.util.simpletimetracker.domain.extension.value import com.example.util.simpletimetracker.domain.interactor.NotificationTypeInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_notification.R import com.example.util.simpletimetracker.feature_notification.recordType.manager.NotificationTypeManager @@ -23,6 +27,7 @@ import javax.inject.Inject class NotificationTypeInteractorImpl @Inject constructor( private val notificationTypeManager: NotificationTypeManager, private val recordTypeInteractor: RecordTypeInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val runningRecordInteractor: RunningRecordInteractor, private val recordTagInteractor: RecordTagInteractor, private val getCurrentRecordsDurationInteractor: GetCurrentRecordsDurationInteractor, @@ -42,6 +47,7 @@ class NotificationTypeInteractorImpl @Inject constructor( if (!prefsInteractor.getShowNotifications()) return val recordType = recordTypeInteractor.get(typeId) + val goals = recordTypeGoalInteractor.getByType(typeId) val recordTypes = recordTypeInteractor.getAll() val runningRecord = runningRecordInteractor.get(typeId) val recordTags = recordTagInteractor.getAll() @@ -71,6 +77,7 @@ class NotificationTypeInteractorImpl @Inject constructor( show( recordType = recordType, + goalTime = goals.getSessionDuration(), runningRecord = runningRecord ?: return, recordTags = recordTags.filter { it.id in runningRecord.tagIds }, dailyCurrent = getCurrentRecordsDurationInteractor.getDailyCurrent(runningRecord), @@ -96,7 +103,8 @@ class NotificationTypeInteractorImpl @Inject constructor( } private suspend fun showAll() { - val recordTypes = recordTypeInteractor.getAll().associateBy { it.id } + val recordTypes = recordTypeInteractor.getAll().associateBy(RecordType::id) + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) val recordTags = recordTagInteractor.getAll() val isDarkTheme = prefsInteractor.getDarkMode() val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() @@ -115,6 +123,7 @@ class NotificationTypeInteractorImpl @Inject constructor( .forEach { runningRecord -> show( recordType = recordTypes[runningRecord.id], + goalTime = goals[runningRecord.id]?.getSessionDuration(), runningRecord = runningRecord, recordTags = recordTags.filter { it.id in runningRecord.tagIds }, dailyCurrent = getCurrentRecordsDurationInteractor.getDailyCurrent(runningRecord), @@ -134,6 +143,7 @@ class NotificationTypeInteractorImpl @Inject constructor( private fun show( recordType: RecordType?, + goalTime: RecordTypeGoal?, runningRecord: RunningRecord, recordTags: List, dailyCurrent: GetCurrentRecordsDurationInteractor.Result, @@ -159,7 +169,7 @@ class NotificationTypeInteractorImpl @Inject constructor( totalDuration = dailyCurrent.let { if (it.durationDiffersFromCurrent) it.duration else null }, - goalTime = recordType.goalTime + goalTime = goalTime.value .takeIf { it > 0 } ?.let(timeMapper::formatDuration) ?.let { resourceRepo.getString(R.string.notification_record_type_goal_time, it) } diff --git a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt index aebeca04..cb5ff5a8 100644 --- a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt +++ b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt @@ -8,6 +8,7 @@ import com.example.util.simpletimetracker.domain.interactor.GetUntrackedRecordsI import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor import com.example.util.simpletimetracker.domain.model.DayOfWeek @@ -16,6 +17,7 @@ import com.example.util.simpletimetracker.domain.model.RangeLength import com.example.util.simpletimetracker.domain.model.Record import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.domain.model.count import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType @@ -37,6 +39,7 @@ class RecordsViewDataInteractor @Inject constructor( private val runningRecordInteractor: RunningRecordInteractor, private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val prefsInteractor: PrefsInteractor, private val getUntrackedRecordsInteractor: GetUntrackedRecordsInteractor, private val recordsViewDataMapper: RecordsViewDataMapper, @@ -54,8 +57,9 @@ class RecordsViewDataInteractor @Inject constructor( val startOfDayShift = prefsInteractor.getStartOfDayShift() val showUntrackedInRecords = prefsInteractor.getShowUntrackedInRecords() val reverseOrder = prefsInteractor.getReverseOrderInCalendar() - val recordTypes = recordTypeInteractor.getAll().associateBy { it.id } + val recordTypes = recordTypeInteractor.getAll().associateBy(RecordType::id) val recordTags = recordTagInteractor.getAll() + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) val runningRecords = runningRecordInteractor.getAll() val isCalendarView = prefsInteractor.getShowRecordsCalendar() val calendarDayCount = prefsInteractor.getDaysInCalendar().count @@ -77,6 +81,7 @@ class RecordsViewDataInteractor @Inject constructor( runningRecords = runningRecords, recordTypes = recordTypes, recordTags = recordTags, + goals = goals, range = range, isDarkTheme = isDarkTheme, useMilitaryTime = useMilitaryTime, @@ -168,6 +173,7 @@ class RecordsViewDataInteractor @Inject constructor( runningRecords: List, recordTypes: Map, recordTags: List, + goals: Map>, range: Range, isDarkTheme: Boolean, useMilitaryTime: Boolean, @@ -202,6 +208,7 @@ class RecordsViewDataInteractor @Inject constructor( getRunningRecordViewDataMediator.execute( type = recordTypes[runningRecord.id] ?: return@mapNotNull null, tags = recordTags.filter { it.id in runningRecord.tagIds }, + goals = goals[runningRecord.id].orEmpty(), record = runningRecord, nowIconVisible = true, goalsVisible = false, diff --git a/features/feature_records_all/src/main/java/com/example/util/simpletimetracker/feature_records_all/interactor/RecordsAllViewDataInteractor.kt b/features/feature_records_all/src/main/java/com/example/util/simpletimetracker/feature_records_all/interactor/RecordsAllViewDataInteractor.kt index a1fc90e2..ae345f99 100644 --- a/features/feature_records_all/src/main/java/com/example/util/simpletimetracker/feature_records_all/interactor/RecordsAllViewDataInteractor.kt +++ b/features/feature_records_all/src/main/java/com/example/util/simpletimetracker/feature_records_all/interactor/RecordsAllViewDataInteractor.kt @@ -8,9 +8,12 @@ import com.example.util.simpletimetracker.core.mapper.RecordViewDataMapper import com.example.util.simpletimetracker.domain.UNTRACKED_ITEM_ID import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.model.MultitaskRecord import com.example.util.simpletimetracker.domain.model.Record +import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RecordsFilter import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType @@ -22,6 +25,7 @@ import kotlinx.coroutines.withContext class RecordsAllViewDataInteractor @Inject constructor( private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val prefsInteractor: PrefsInteractor, private val recordFilterInteractor: RecordFilterInteractor, private val recordViewDataMapper: RecordViewDataMapper, @@ -38,8 +42,9 @@ class RecordsAllViewDataInteractor @Inject constructor( val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() val useProportionalMinutes = prefsInteractor.getUseProportionalMinutes() val showSeconds = prefsInteractor.getShowSeconds() - val recordTypes = recordTypeInteractor.getAll().associateBy { it.id } + val recordTypes = recordTypeInteractor.getAll().associateBy(RecordType::id) val recordTags = recordTagInteractor.getAll() + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) // Show empty records if no filters other than date. val records = filter @@ -75,6 +80,7 @@ class RecordsAllViewDataInteractor @Inject constructor( is RunningRecord -> getRunningRecordViewDataMediator.execute( type = recordTypes[record.id] ?: return@mapNotNull null, tags = recordTags.filter { it.id in record.tagIds }, + goals = goals[record.id].orEmpty(), record = record, nowIconVisible = true, goalsVisible = false, diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt index 911f1724..cf5e19a8 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt @@ -41,6 +41,7 @@ import com.example.util.simpletimetracker.domain.model.Record import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RecordTypeCategory +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RecordsFilter import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType @@ -129,6 +130,7 @@ class RecordsFilterViewDataInteractor @Inject constructor( filters: List, recordTypes: Map, recordTags: List, + goals: Map>, ): RecordsFilterSelectedRecordsViewData = withContext(Dispatchers.Default) { val isDarkTheme = prefsInteractor.getDarkMode() val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() @@ -189,6 +191,7 @@ class RecordsFilterViewDataInteractor @Inject constructor( is RunningRecord -> getRunningRecordViewDataMediator.execute( type = recordTypes[record.id] ?: return@mapNotNull null, tags = recordTags.filter { it.id in record.tagIds }, + goals = goals[record.id].orEmpty(), record = record, nowIconVisible = true, goalsVisible = false, diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt index 01201690..56d009ac 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt @@ -30,6 +30,7 @@ import com.example.util.simpletimetracker.domain.interactor.CategoryInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.model.Category import com.example.util.simpletimetracker.domain.model.DayOfWeek @@ -37,6 +38,7 @@ import com.example.util.simpletimetracker.domain.model.Range import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RecordTypeCategory +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RecordsFilter import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.category.CategoryViewData @@ -73,6 +75,7 @@ class RecordsFilterViewModel @Inject constructor( private val categoryInteractor: CategoryInteractor, private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val prefsInteractor: PrefsInteractor, private val timeMapper: TimeMapper, private val router: Router, @@ -127,6 +130,7 @@ class RecordsFilterViewModel @Inject constructor( private var recordTypeCategories: List? = null private var categories: List? = null private var recordTags: List? = null + private var goals: List? = null fun init(extra: RecordsFilterParams) { this.extra = extra @@ -256,7 +260,7 @@ class RecordsFilterViewModel @Inject constructor( fun onRecordClick( item: RecordViewData, - @Suppress("UNUSED_PARAMETER") sharedElements: Pair + @Suppress("UNUSED_PARAMETER") sharedElements: Pair, ) { if (item is RecordViewData.Untracked) return // TODO manually filter untracked records? @@ -291,7 +295,7 @@ class RecordsFilterViewModel @Inject constructor( val currentIds = filters.getTypeIds().toMutableList() val currentIdsFromCategories = filters.getTypeIdsFromCategories( recordTypes = getTypesCache(), - recordTypeCategories = getRecordTypeCategoriesCache() + recordTypeCategories = getRecordTypeCategoriesCache(), ) filterSelectionState = RecordsFilterSelectionState.Visible(RecordFilterViewData.Type.ACTIVITY) @@ -445,7 +449,7 @@ class RecordsFilterViewModel @Inject constructor( // Update tags according to selected activities val newTypeIds: List = filters.getAllTypeIds( recordTypes = getTypesCache(), - recordTypeCategories = getRecordTypeCategoriesCache() + recordTypeCategories = getRecordTypeCategoriesCache(), ) suspend fun update(tags: List): List { @@ -649,6 +653,12 @@ class RecordsFilterViewModel @Inject constructor( } } + private suspend fun getGoalsCache(): List { + return goals ?: run { + recordTypeGoalInteractor.getAll().also { goals = it } + } + } + private fun updateFilterSelectionVisibility() { val data = loadFilterSelectionVisibility() filterSelectionVisibility.set(data) @@ -663,7 +673,7 @@ class RecordsFilterViewModel @Inject constructor( RecordsFilterResultParams( tag = extra.tag, filters = filters, - ) + ), ) val data = loadFiltersViewData() filtersViewData.set(data) @@ -673,7 +683,7 @@ class RecordsFilterViewModel @Inject constructor( return viewDataInteractor.getFiltersViewData( extra = extra, selectionState = filterSelectionState, - filters = filters + filters = filters, ) } @@ -692,6 +702,7 @@ class RecordsFilterViewModel @Inject constructor( filters = filters, recordTypes = getTypesCache().associateBy(RecordType::id), recordTags = getTagsCache(), + goals = getGoalsCache().groupBy(RecordTypeGoal::typeId), ) } @@ -707,7 +718,8 @@ class RecordsFilterViewModel @Inject constructor( RecordFilterViewData.Type.UNTRACKED, RecordFilterViewData.Type.MULTITASK, RecordFilterViewData.Type.ACTIVITY, - RecordFilterViewData.Type.CATEGORY -> { + RecordFilterViewData.Type.CATEGORY, + -> { viewDataInteractor.getActivityFilterSelectionViewData( extra = extra, filters = filters, @@ -722,7 +734,8 @@ class RecordsFilterViewModel @Inject constructor( ) } RecordFilterViewData.Type.SELECTED_TAGS, - RecordFilterViewData.Type.FILTERED_TAGS -> { + RecordFilterViewData.Type.FILTERED_TAGS, + -> { viewDataInteractor.getTagsFilterSelectionViewData( type = type, filters = filters, diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt index 4364db36..0f6414bc 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt @@ -4,8 +4,11 @@ import com.example.util.simpletimetracker.core.interactor.ActivityFilterViewData import com.example.util.simpletimetracker.core.interactor.GetRunningRecordViewDataMediator import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor +import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData @@ -16,6 +19,7 @@ class RunningRecordsViewDataInteractor @Inject constructor( private val prefsInteractor: PrefsInteractor, private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val runningRecordInteractor: RunningRecordInteractor, private val activityFilterViewDataInteractor: ActivityFilterViewDataInteractor, private val mapper: RunningRecordsViewDataMapper, @@ -24,10 +28,11 @@ class RunningRecordsViewDataInteractor @Inject constructor( suspend fun getViewData(): List { val recordTypes = recordTypeInteractor.getAll() - val recordTypesMap = recordTypes.associateBy { it.id } + val recordTypesMap = recordTypes.associateBy(RecordType::id) + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) val recordTags = recordTagInteractor.getAll() val runningRecords = runningRecordInteractor.getAll() - val recordTypesRunning = runningRecords.map { it.id } + val recordTypesRunning = runningRecords.map(RunningRecord::id) val numberOfCards = prefsInteractor.getNumberOfCards() val isDarkTheme = prefsInteractor.getDarkMode() val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() @@ -46,6 +51,7 @@ class RunningRecordsViewDataInteractor @Inject constructor( getRunningRecordViewDataMediator.execute( type = recordTypesMap[runningRecord.id] ?: return@mapNotNull null, tags = recordTags.filter { it.id in runningRecord.tagIds }, + goals = goals[runningRecord.id].orEmpty(), record = runningRecord, nowIconVisible = false, goalsVisible = true, diff --git a/features/feature_statistics/src/main/java/com/example/util/simpletimetracker/feature_statistics/interactor/StatisticsViewDataInteractor.kt b/features/feature_statistics/src/main/java/com/example/util/simpletimetracker/feature_statistics/interactor/StatisticsViewDataInteractor.kt index 6a6bea9b..bc60f8d7 100644 --- a/features/feature_statistics/src/main/java/com/example/util/simpletimetracker/feature_statistics/interactor/StatisticsViewDataInteractor.kt +++ b/features/feature_statistics/src/main/java/com/example/util/simpletimetracker/feature_statistics/interactor/StatisticsViewDataInteractor.kt @@ -7,10 +7,12 @@ import com.example.util.simpletimetracker.core.mapper.RangeViewDataMapper import com.example.util.simpletimetracker.domain.UNCATEGORIZED_ITEM_ID import com.example.util.simpletimetracker.domain.UNTRACKED_ITEM_ID import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.model.ChartFilterType import com.example.util.simpletimetracker.domain.model.RangeLength import com.example.util.simpletimetracker.domain.model.RecordType +import com.example.util.simpletimetracker.domain.model.RecordTypeGoal import com.example.util.simpletimetracker.domain.model.RecordsFilter import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData @@ -24,6 +26,7 @@ import kotlinx.coroutines.withContext class StatisticsViewDataInteractor @Inject constructor( private val recordTypeInteractor: RecordTypeInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, private val statisticsMediator: StatisticsMediator, private val statisticsChartViewDataInteractor: StatisticsChartViewDataInteractor, private val prefsInteractor: PrefsInteractor, @@ -82,6 +85,7 @@ class StatisticsViewDataInteractor @Inject constructor( val showSeconds = prefsInteractor.getShowSeconds() val showDuration = rangeLength !is RangeLength.All val types = recordTypeInteractor.getAll().associateBy(RecordType::id) + val goals = recordTypeGoalInteractor.getAll().groupBy(RecordTypeGoal::typeId) val filteredIds = when (filterType) { ChartFilterType.ACTIVITY -> prefsInteractor.getFilteredTypes() @@ -92,7 +96,8 @@ class StatisticsViewDataInteractor @Inject constructor( // Get data. val dataHolders = statisticsMediator.getDataHolders( filterType = filterType, - types = types + types = types, + goals = goals, ) val statistics = statisticsMediator.getStatistics( filterType = filterType, diff --git a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailChartInteractor.kt b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailChartInteractor.kt index 79f22803..84379a19 100644 --- a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailChartInteractor.kt +++ b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailChartInteractor.kt @@ -3,10 +3,14 @@ package com.example.util.simpletimetracker.feature_statistics_detail.interactor import com.example.util.simpletimetracker.core.extension.setToStartOfDay import com.example.util.simpletimetracker.core.extension.setWeekToFirstDay import com.example.util.simpletimetracker.core.mapper.TimeMapper +import com.example.util.simpletimetracker.domain.extension.getDailyDuration +import com.example.util.simpletimetracker.domain.extension.getMonthlyDuration import com.example.util.simpletimetracker.domain.extension.getTypeIds +import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration import com.example.util.simpletimetracker.domain.extension.hasActivityFilter +import com.example.util.simpletimetracker.domain.extension.value import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor -import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.mapper.RangeMapper import com.example.util.simpletimetracker.domain.model.DayOfWeek import com.example.util.simpletimetracker.domain.model.Range @@ -31,7 +35,7 @@ class StatisticsDetailChartInteractor @Inject constructor( private val rangeMapper: RangeMapper, private val statisticsDetailViewDataMapper: StatisticsDetailViewDataMapper, private val prefsInteractor: PrefsInteractor, - private val recordTypeInteractor: RecordTypeInteractor, + private val recordTypeGoalInteractor: RecordTypeGoalInteractor, ) { suspend fun getChartViewData( @@ -114,12 +118,12 @@ class StatisticsDetailChartInteractor @Inject constructor( val typeIds = filter.getTypeIds() if (typeIds.size != 1) return 0 val typeId = typeIds.firstOrNull() ?: return 0 - val type = recordTypeInteractor.get(typeId) ?: return 0 + val goals = recordTypeGoalInteractor.getByType(typeId) return when (appliedChartGrouping) { - ChartGrouping.DAILY -> type.dailyGoalTime - ChartGrouping.WEEKLY -> type.weeklyGoalTime - ChartGrouping.MONTHLY -> type.monthlyGoalTime + ChartGrouping.DAILY -> goals.getDailyDuration().value + ChartGrouping.WEEKLY -> goals.getWeeklyDuration().value + ChartGrouping.MONTHLY -> goals.getMonthlyDuration().value ChartGrouping.YEARLY -> 0 } * 1000 } diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/statistics/WidgetStatisticsChartProvider.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/statistics/WidgetStatisticsChartProvider.kt index c0a472de..e0bd21d1 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/statistics/WidgetStatisticsChartProvider.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/statistics/WidgetStatisticsChartProvider.kt @@ -31,11 +31,11 @@ import com.example.util.simpletimetracker.feature_widget.R import com.example.util.simpletimetracker.feature_widget.statistics.customView.WidgetStatisticsChartView import com.example.util.simpletimetracker.navigation.Router import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject @AndroidEntryPoint class WidgetStatisticsChartProvider : AppWidgetProvider() { @@ -130,13 +130,14 @@ class WidgetStatisticsChartProvider : AppWidgetProvider() { val dataHolders = statisticsMediator.getDataHolders( filterType = filterType, - types = types + types = types, + goals = emptyMap(), // Not used on pie chart. ) val statistics = statisticsMediator.getStatistics( filterType = filterType, filteredIds = filteredIds, rangeLength = rangeLength, - shift = 0 + shift = 0, ) val chart = statisticsChartViewDataInteractor.getChart( filterType = filterType, @@ -144,7 +145,7 @@ class WidgetStatisticsChartProvider : AppWidgetProvider() { statistics = statistics, dataHolders = dataHolders, types = types, - isDarkTheme = isDarkTheme + isDarkTheme = isDarkTheme, ) val total: String = statisticsMediator.getStatisticsTotalTracked( statistics = statistics, -- 2.51.2