From 4626c4ab1d39e9c5804ad81c5c1709eafaea5e50 Mon Sep 17 00:00:00 2001 From: razeeman Date: Sat, 26 Oct 2024 17:15:42 +0300 Subject: [PATCH] add retroactive tracking mode support to widgets, notifications --- .../data_local/repo/RecordRepoImpl.kt | 1 + .../interactor/AddRunningRecordMediator.kt | 61 ++++++++++--- .../domain/interactor/RecordInteractor.kt | 3 +- .../interactor/RemoveRunningRecordMediator.kt | 9 +- .../UpdateExternalViewsInteractor.kt | 28 +++++- .../ChangeRecordActionsDelegateMapper.kt | 10 +-- .../ChangeRecordActionsContinueDelegate.kt | 9 +- .../viewModel/ChangeRecordActionsDelegate.kt | 8 +- .../ChangeRecordActionsDuplicateDelegate.kt | 4 +- .../ChangeRecordActionsRepeatDelegate.kt | 21 +++-- .../viewModel/ChangeRecordBaseViewModel.kt | 12 +-- .../viewModel/ChangeRecordViewModel.kt | 5 +- .../viewModel/ChangeRunningRecordViewModel.kt | 5 +- .../viewModel/RecordQuickActionsViewModel.kt | 13 ++- ...icationActivitySwitchControlsInteractor.kt | 4 +- ...otificationActivitySwitchInteractorImpl.kt | 71 ++++++++++++++- .../NotificationActivitySwitchManager.kt | 58 +++++++++--- .../NotificationActivitySwitchParams.kt | 7 ++ .../manager/NotificationControlsManager.kt | 3 +- .../manager/NotificationControlsParams.kt | 2 +- .../NotificationTypeInteractorImpl.kt | 7 +- .../notification_activity_switch_layout.xml | 77 ++++++++++++++-- .../notification_switch_controls_layout.xml | 5 +- .../RunningRecordsViewDataInteractor.kt | 2 - .../mapper/RunningRecordsViewDataMapper.kt | 12 +-- .../SettingsAdditionalViewModelDelegate.kt | 5 +- .../WidgetInteractorImpl.kt | 2 +- .../{interactor => common}/WidgetManager.kt | 2 +- .../WidgetViewsHolder.kt | 2 +- .../feature_widget/di/WidgetModule.kt | 2 +- .../single/WidgetSingleProvider.kt | 63 ++++++++++--- .../WidgetStatisticsChartProvider.kt | 2 +- .../universal/WidgetUniversalProvider.kt | 90 +++++++++++++++---- .../viewModel/WidgetUniversalViewModel.kt | 6 +- .../mapper/WidgetUniversalViewDataMapper.kt | 43 ++++++++- resources/src/main/res/values/strings.xml | 4 + 36 files changed, 518 insertions(+), 140 deletions(-) rename features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/{interactor => common}/WidgetInteractorImpl.kt (93%) rename features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/{interactor => common}/WidgetManager.kt (97%) rename features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/{interactor => common}/WidgetViewsHolder.kt (96%) diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordRepoImpl.kt index 7041444ce..05b5cfd0e 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordRepoImpl.kt @@ -182,6 +182,7 @@ class RecordRepoImpl @Inject constructor( private fun clearCache() { getFromRangeCache.evictAll() getFromRangeByTypeCache.evictAll() + recordCache.evictAll() isEmpty = null } diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/AddRunningRecordMediator.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/AddRunningRecordMediator.kt index f98f1fda2..cd8f5cff7 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/AddRunningRecordMediator.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/AddRunningRecordMediator.kt @@ -62,13 +62,31 @@ class AddRunningRecordMediator @Inject constructor( timeStarted: Long? = null, updateNotificationSwitch: Boolean = true, ) { + val actualTimeStarted = timeStarted ?: System.currentTimeMillis() + val retroactiveTrackingMode = prefsInteractor.getRetroactiveTrackingMode() + val prevRecord = if (retroactiveTrackingMode) { + recordInteractor.getPrev(actualTimeStarted).firstOrNull() + } else { + null + } val rulesResult = processRules( typeId = typeId, - timeStarted = timeStarted ?: System.currentTimeMillis(), + timeStarted = if ( + retroactiveTrackingMode && + prevRecord != null && + shouldMergeWithPrevRecord(typeId, prevRecord) + ) { + // If will merge - it will be one record, + // so need to check rules from original start. + prevRecord.timeStarted + } else { + actualTimeStarted + }, ) processMultitasking( typeId = typeId, isMultitaskingAllowedByRules = rulesResult.isMultitaskingAllowed, + timeEnded = actualTimeStarted, ) val actualTags = getAllTags( typeId = typeId, @@ -84,11 +102,11 @@ class AddRunningRecordMediator @Inject constructor( typeId = typeId, comment = comment, tagIds = actualTags, - timeStarted = timeStarted ?: System.currentTimeMillis(), + timeStarted = actualTimeStarted, updateNotificationSwitch = updateNotificationSwitch, ) - if (prefsInteractor.getRetroactiveTrackingMode()) { - addRetroactiveModeInternal(startParams) + if (retroactiveTrackingMode) { + addRetroactiveModeInternal(startParams, prevRecord) } else { addInternal(startParams) } @@ -126,9 +144,11 @@ class AddRunningRecordMediator @Inject constructor( } } - private suspend fun addRetroactiveModeInternal(params: StartParams) { + private suspend fun addRetroactiveModeInternal( + params: StartParams, + prevRecord: Record?, + ) { val type = recordTypeInteractor.get(params.typeId) ?: return - val prevRecord = recordInteractor.getPrev(params.timeStarted).firstOrNull() if (type.defaultDuration > 0L) { val newTimeStarted = prevRecord?.timeEnded @@ -186,18 +206,17 @@ class AddRunningRecordMediator @Inject constructor( params: StartParams, prevRecord: Record?, ) { - val shouldMergeWithPrevRecord = prevRecord != null && - prevRecord.typeId == params.typeId && - prevRecord.tagIds == params.tagIds - - val record = if (prevRecord != null && shouldMergeWithPrevRecord) { + val shouldMerge = shouldMergeWithPrevRecord(params.typeId, prevRecord) + val record = if (prevRecord != null && shouldMerge) { Record( id = prevRecord.id, // Updates existing record. typeId = params.typeId, timeStarted = prevRecord.timeStarted, timeEnded = params.timeStarted, - comment = params.comment, - tagIds = params.tagIds, + comment = params.comment.takeUnless { it.isEmpty() } + ?: prevRecord.comment, + tagIds = params.tagIds.takeUnless { it.isEmpty() } + ?: prevRecord.tagIds, ) } else { val newTimeStarted = prevRecord?.timeEnded @@ -255,6 +274,7 @@ class AddRunningRecordMediator @Inject constructor( private suspend fun processMultitasking( typeId: Long, isMultitaskingAllowedByRules: ResultContainer, + timeEnded: Long, ) { val isMultitaskingAllowedByDefault = prefsInteractor.getAllowMultitasking() val isMultitaskingAllowed = isMultitaskingAllowedByRules.getValueOrNull() @@ -265,7 +285,13 @@ class AddRunningRecordMediator @Inject constructor( // Widgets will update on adding. runningRecordInteractor.getAll() .filter { it.id != typeId } - .forEach { removeRunningRecordMediator.removeWithRecordAdd(it, updateWidgets = false) } + .forEach { + removeRunningRecordMediator.removeWithRecordAdd( + runningRecord = it, + updateWidgets = false, + timeEnded = timeEnded, + ) + } } } @@ -278,6 +304,13 @@ class AddRunningRecordMediator @Inject constructor( return (tagIds + defaultTags + tagIdsFromRules).toSet().toList() } + private fun shouldMergeWithPrevRecord( + typeId: Long, + prevRecord: Record?, + ): Boolean { + return prevRecord != null && prevRecord.typeId == typeId + } + private data class StartParams( val typeId: Long, val timeStarted: Long, diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordInteractor.kt index be6c5a39b..bab8c4f72 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordInteractor.kt @@ -81,11 +81,12 @@ class RecordInteractor @Inject constructor( suspend fun addFromRunning( runningRecord: RunningRecord, + timeEnded: Long, ) { Record( typeId = runningRecord.id, timeStarted = runningRecord.timeStarted, - timeEnded = System.currentTimeMillis(), + timeEnded = timeEnded, comment = runningRecord.comment, tagIds = runningRecord.tagIds, ).let { diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RemoveRunningRecordMediator.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RemoveRunningRecordMediator.kt index 374005b36..ec5fa3f23 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RemoveRunningRecordMediator.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RemoveRunningRecordMediator.kt @@ -17,14 +17,19 @@ class RemoveRunningRecordMediator @Inject constructor( runningRecord: RunningRecord, updateWidgets: Boolean = true, updateNotificationSwitch: Boolean = true, + timeEnded: Long? = null, // null - take current time. ) { + val recordTimeEnded = timeEnded ?: System.currentTimeMillis() val durationToIgnore = prefsInteractor.getIgnoreShortRecordsDuration() val duration = TimeUnit.MILLISECONDS - .toSeconds(System.currentTimeMillis() - runningRecord.timeStarted) + .toSeconds(recordTimeEnded - runningRecord.timeStarted) if (duration > durationToIgnore || durationToIgnore == 0L) { // No need to update widgets and notification because it will be done in running record remove. - recordInteractor.addFromRunning(runningRecord) + recordInteractor.addFromRunning( + runningRecord = runningRecord, + timeEnded = recordTimeEnded + ) } activityStartedStoppedBroadcastInteractor.onActivityStopped( typeId = runningRecord.id, diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/UpdateExternalViewsInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/UpdateExternalViewsInteractor.kt index 9a46fc785..39998a409 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/UpdateExternalViewsInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/UpdateExternalViewsInteractor.kt @@ -14,6 +14,7 @@ class UpdateExternalViewsInteractor @Inject constructor( private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor, private val widgetInteractor: WidgetInteractor, private val wearInteractor: WearInteractor, + private val prefsInteractor: PrefsInteractor, ) { // Also removes running records and records. @@ -28,10 +29,12 @@ class UpdateExternalViewsInteractor @Inject constructor( Update.GoalCancel(RecordTypeGoal.IdData.Type(typeId)), Update.GoalReschedule(runningRecordIds + typeId), Update.WidgetStatistics, - Update.WidgetSingleType(typeId), + Update.WidgetSingleTypes.takeIf { getRetroactiveTrackingMode() } + ?: Update.WidgetSingleType(typeId), + Update.WidgetUniversal.takeIf { getRetroactiveTrackingMode() }, Update.Wear.takeIf { !fromArchive }, Update.NotificationTypes.takeIf { !fromArchive }, - Update.NotificationWithControls.takeIf { !fromArchive } + Update.NotificationWithControls.takeIf { !fromArchive }, ) } @@ -133,7 +136,9 @@ class UpdateExternalViewsInteractor @Inject constructor( Update.NotificationWithControls, Update.GoalReschedule(listOf(typeId)), Update.WidgetStatistics, - Update.WidgetSingleType(typeId), + Update.WidgetSingleTypes.takeIf { getRetroactiveTrackingMode() } + ?: Update.WidgetSingleType(typeId), + Update.WidgetUniversal.takeIf { getRetroactiveTrackingMode() }, ) } @@ -146,7 +151,9 @@ class UpdateExternalViewsInteractor @Inject constructor( Update.NotificationWithControls.takeIf { updateNotificationSwitch }, Update.GoalReschedule(listOf(typeId)), Update.WidgetStatistics, - Update.WidgetSingleType(typeId), + Update.WidgetSingleTypes.takeIf { getRetroactiveTrackingMode() } + ?: Update.WidgetSingleType(typeId), + Update.WidgetUniversal.takeIf { getRetroactiveTrackingMode() }, ) } @@ -308,6 +315,15 @@ class UpdateExternalViewsInteractor @Inject constructor( ) } + suspend fun onRetroactiveTrackingModeChange() { + runUpdates( + Update.WidgetSingleTypes, + Update.WidgetUniversal, + Update.NotificationWithControls, + Update.Wear + ) + } + // Update all widgets. suspend fun onWidgetsTransparencyChange() { runUpdates( @@ -377,6 +393,10 @@ class UpdateExternalViewsInteractor @Inject constructor( ) } + private suspend fun getRetroactiveTrackingMode(): Boolean { + return prefsInteractor.getRetroactiveTrackingMode() + } + private suspend fun runUpdates(vararg updates: Update?) { updates.filterNotNull().forEach { runUpdate(it) } } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/mapper/ChangeRecordActionsDelegateMapper.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/mapper/ChangeRecordActionsDelegateMapper.kt index c820eb4c9..2d028c830 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/mapper/ChangeRecordActionsDelegateMapper.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/mapper/ChangeRecordActionsDelegateMapper.kt @@ -86,8 +86,8 @@ class ChangeRecordActionsDelegateMapper @Inject constructor() { updateViewData() } - override suspend fun onSaveClickDelegate() { - parent?.onSaveClickDelegate() + override suspend fun onSaveClickDelegate(doAfter: suspend () -> Unit) { + parent?.onSaveClickDelegate(doAfter) } } } @@ -163,7 +163,7 @@ class ChangeRecordActionsDelegateMapper @Inject constructor() { newTimeStarted = baseParams.newTimeStarted, newComment = baseParams.newComment, newCategoryIds = baseParams.newCategoryIds, - isAdditionalActionsAvailable = continueParams.isAdditionalActionsAvailable, + isAvailable = continueParams.isAvailable, isButtonEnabled = baseParams.isButtonEnabled, ) } @@ -173,7 +173,7 @@ class ChangeRecordActionsDelegateMapper @Inject constructor() { newTypeId = baseParams.newTypeId, newComment = baseParams.newComment, newCategoryIds = baseParams.newCategoryIds, - isAdditionalActionsAvailable = repeatParams.isAdditionalActionsAvailable, + isAvailable = repeatParams.isAvailable, isButtonEnabled = baseParams.isButtonEnabled, ) } @@ -185,7 +185,7 @@ class ChangeRecordActionsDelegateMapper @Inject constructor() { newTimeEnded = baseParams.newTimeEnded, newComment = baseParams.newComment, newCategoryIds = baseParams.newCategoryIds, - isAdditionalActionsAvailable = duplicateParams.isAdditionalActionsAvailable, + isAvailable = duplicateParams.isAvailable, isButtonEnabled = baseParams.isButtonEnabled, ) } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsContinueDelegate.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsContinueDelegate.kt index 36d0cf43c..7aa314cfe 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsContinueDelegate.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsContinueDelegate.kt @@ -1,6 +1,7 @@ package com.example.util.simpletimetracker.feature_change_record.viewModel import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordActionContinueMediator import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.hint.HintViewData @@ -13,6 +14,7 @@ import javax.inject.Inject class ChangeRecordActionsContinueDelegate @Inject constructor( private val router: Router, private val resourceRepo: ResourceRepo, + private val prefsInteractor: PrefsInteractor, private val recordActionContinueMediator: RecordActionContinueMediator, ) : ChangeRecordActionsSubDelegate { @@ -57,10 +59,11 @@ class ChangeRecordActionsContinueDelegate @Inject constructor( } } - private fun loadContinueViewData(): List { + private suspend fun loadContinueViewData(): List { val params = parent?.getViewDataParams() ?: return emptyList() - if (!params.isAdditionalActionsAvailable) return emptyList() + if (!params.isAvailable) return emptyList() + if (prefsInteractor.getRetroactiveTrackingMode()) return emptyList() val result = mutableListOf() result += HintViewData( @@ -89,7 +92,7 @@ class ChangeRecordActionsContinueDelegate @Inject constructor( val newTimeStarted: Long, val newComment: String, val newCategoryIds: List, - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, val isButtonEnabled: Boolean, ) } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDelegate.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDelegate.kt index 9e09ff61e..88541a0c8 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDelegate.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDelegate.kt @@ -17,7 +17,7 @@ interface ChangeRecordActionsDelegate { checkTypeSelected: Boolean = true, ) - suspend fun onSaveClickDelegate() + suspend fun onSaveClickDelegate(doAfter: suspend () -> Unit = {}) suspend fun onSplitComplete() @@ -49,16 +49,16 @@ interface ChangeRecordActionsDelegate { ) data class DuplicateParams( - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, ) data class ContinueParams( val originalRecordId: Long, - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, ) data class RepeatParams( - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, ) data class AdjustParams( diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDuplicateDelegate.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDuplicateDelegate.kt index a6dc83c60..1c6f62ba9 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDuplicateDelegate.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsDuplicateDelegate.kt @@ -45,7 +45,7 @@ class ChangeRecordActionsDuplicateDelegate @Inject constructor( private fun loadDuplicateViewData(): List { val params = parent?.getViewDataParams() ?: return emptyList() - if (!params.isAdditionalActionsAvailable) return emptyList() + if (!params.isAvailable) return emptyList() val result = mutableListOf() result += HintViewData( @@ -73,7 +73,7 @@ class ChangeRecordActionsDuplicateDelegate @Inject constructor( val newTimeEnded: Long, val newComment: String, val newCategoryIds: List, - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, val isButtonEnabled: Boolean, ) } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsRepeatDelegate.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsRepeatDelegate.kt index e22a76b02..f7adbdaac 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsRepeatDelegate.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsRepeatDelegate.kt @@ -32,19 +32,22 @@ class ChangeRecordActionsRepeatDelegate @Inject constructor( suspend fun onRepeatClickDelegate() { val params = parent?.getViewDataParams() ?: return - recordActionRepeatMediator.execute( - typeId = params.newTypeId, - comment = params.newComment, - tagIds = params.newCategoryIds, - ) // Exit. - parent?.onSaveClickDelegate() + parent?.onSaveClickDelegate( + doAfter = { + recordActionRepeatMediator.execute( + typeId = params.newTypeId, + comment = params.newComment, + tagIds = params.newCategoryIds, + ) + } + ) } private fun loadRepeatViewData(): List { val params = parent?.getViewDataParams() ?: return emptyList() - if (!params.isAdditionalActionsAvailable) return emptyList() + if (!params.isAvailable) return emptyList() val result = mutableListOf() result += HintViewData( @@ -64,13 +67,13 @@ class ChangeRecordActionsRepeatDelegate @Inject constructor( fun getViewDataParams(): ViewDataParams? fun update() - suspend fun onSaveClickDelegate() + suspend fun onSaveClickDelegate(doAfter: suspend () -> Unit) data class ViewDataParams( val newTypeId: Long, val newComment: String, val newCategoryIds: List, - val isAdditionalActionsAvailable: Boolean, + val isAvailable: Boolean, val isButtonEnabled: Boolean, ) } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt index c786357da..5eee67abb 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt @@ -132,7 +132,7 @@ abstract class ChangeRecordBaseViewModel( protected abstract suspend fun updatePreview() protected abstract fun getChangeCategoryParams(data: ChangeTagData): ChangeRecordTagFromScreen - protected abstract suspend fun onSaveClickDelegate() + protected abstract suspend fun onSaveClickDelegate(doAfter: suspend () -> Unit = {}) protected open suspend fun sendPreviewUpdate(fullUpdate: Boolean) {} protected abstract val forceSecondsInDurationDialog: Boolean protected abstract val mergeAvailable: Boolean @@ -776,14 +776,14 @@ abstract class ChangeRecordBaseViewModel( showTimeEndedOnSplitPreview = showTimeEndedOnSplitPreview, ), duplicateParams = ChangeRecordActionsDelegate.Parent.ViewDataParams.DuplicateParams( - isAdditionalActionsAvailable = isAdditionalActionsAvailable, + isAvailable = isAdditionalActionsAvailable, ), continueParams = ChangeRecordActionsDelegate.Parent.ViewDataParams.ContinueParams( originalRecordId = originalRecordId, - isAdditionalActionsAvailable = isAdditionalActionsAvailable, + isAvailable = isAdditionalActionsAvailable, ), repeatParams = ChangeRecordActionsDelegate.Parent.ViewDataParams.RepeatParams( - isAdditionalActionsAvailable = isAdditionalActionsAvailable, + isAvailable = isAdditionalActionsAvailable, ), adjustParams = ChangeRecordActionsDelegate.Parent.ViewDataParams.AdjustParams( originalRecordId = originalRecordId, @@ -816,8 +816,8 @@ abstract class ChangeRecordBaseViewModel( ) } - override suspend fun onSaveClickDelegate() { - this@ChangeRecordBaseViewModel.onSaveClickDelegate() + override suspend fun onSaveClickDelegate(doAfter: suspend () -> Unit) { + this@ChangeRecordBaseViewModel.onSaveClickDelegate(doAfter) } override suspend fun onSplitComplete() { diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt index 3af99ef84..348425a03 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt @@ -120,7 +120,9 @@ class ChangeRecordViewModel @Inject constructor( ) } - override suspend fun onSaveClickDelegate() { + override suspend fun onSaveClickDelegate( + doAfter: suspend () -> Unit, + ) { // Zero id creates new record val id = recordId.orZero() Record( @@ -135,6 +137,7 @@ class ChangeRecordViewModel @Inject constructor( if (newTypeId != originalTypeId) { externalViewsInteractor.onRecordChangeType(originalTypeId) } + doAfter() warmupCache(extra.daysFromToday) router.back() } diff --git a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt index ac6cbb96a..63be7af55 100644 --- a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt +++ b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt @@ -127,7 +127,9 @@ class ChangeRunningRecordViewModel @Inject constructor( ) } - override suspend fun onSaveClickDelegate() { + override suspend fun onSaveClickDelegate( + doAfter: suspend () -> Unit, + ) { // Widgets will update on adding. removeRunningRecordMediator.remove(extra.id, updateWidgets = false) addRunningRecordMediator.addAfterChange( @@ -136,6 +138,7 @@ class ChangeRunningRecordViewModel @Inject constructor( comment = newComment, tagIds = newCategoryIds, ) + doAfter() sendPreviewUpdate(fullUpdate = true) router.back() } diff --git a/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/recordQuickActions/viewModel/RecordQuickActionsViewModel.kt b/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/recordQuickActions/viewModel/RecordQuickActionsViewModel.kt index abf0d64f8..62a68e724 100644 --- a/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/recordQuickActions/viewModel/RecordQuickActionsViewModel.kt +++ b/features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/recordQuickActions/viewModel/RecordQuickActionsViewModel.kt @@ -11,6 +11,7 @@ import com.example.util.simpletimetracker.core.extension.toViewData import com.example.util.simpletimetracker.core.interactor.StatisticsDetailNavigationInteractor import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.UNTRACKED_ITEM_ID +import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordActionContinueMediator import com.example.util.simpletimetracker.domain.interactor.RecordActionDuplicateMediator import com.example.util.simpletimetracker.domain.interactor.RecordActionMergeMediator @@ -33,6 +34,7 @@ import javax.inject.Inject class RecordQuickActionsViewModel @Inject constructor( private val router: Router, private val resourceRepo: ResourceRepo, + private val prefsInteractor: PrefsInteractor, private val recordInteractor: RecordInteractor, private val statisticsDetailNavigationInteractor: StatisticsDetailNavigationInteractor, private val recordActionDuplicateMediator: RecordActionDuplicateMediator, @@ -200,14 +202,17 @@ class RecordQuickActionsViewModel @Inject constructor( router.back() } - private fun loadState(): RecordQuickActionsState { + private suspend fun loadState(): RecordQuickActionsState { + val retroactiveTrackingModeEnabled = prefsInteractor.getRetroactiveTrackingMode() + val buttons = when (extra.type) { - is Type.RecordTracked -> listOf( + is Type.RecordTracked -> listOfNotNull( RecordQuickActionsState.Button.Statistics(false), RecordQuickActionsState.Button.Delete(false), - RecordQuickActionsState.Button.Continue(false), + RecordQuickActionsState.Button.Continue(false) + .takeIf { !retroactiveTrackingModeEnabled }, RecordQuickActionsState.Button.Repeat(false), - RecordQuickActionsState.Button.Duplicate(true), + RecordQuickActionsState.Button.Duplicate(!retroactiveTrackingModeEnabled), ) is Type.RecordUntracked -> listOf( RecordQuickActionsState.Button.Statistics(false), diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/GetNotificationActivitySwitchControlsInteractor.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/GetNotificationActivitySwitchControlsInteractor.kt index 9fca990dd..96f32b440 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/GetNotificationActivitySwitchControlsInteractor.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/GetNotificationActivitySwitchControlsInteractor.kt @@ -27,7 +27,7 @@ class GetNotificationActivitySwitchControlsInteractor @Inject constructor( ) { fun getControls( - hintIsVisible: Boolean, + hint: String, isDarkTheme: Boolean, types: List, runningRecords: List = emptyList(), @@ -103,7 +103,7 @@ class GetNotificationActivitySwitchControlsInteractor @Inject constructor( } } return NotificationControlsParams.Enabled( - hintIsVisible = hintIsVisible, + hint = hint, types = repeatButtonViewData + typesViewData, typesShift = typesShift, tags = tagsViewData, diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/NotificationActivitySwitchInteractorImpl.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/NotificationActivitySwitchInteractorImpl.kt index 9bf6599e3..c66e68d46 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/NotificationActivitySwitchInteractorImpl.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/interactor/NotificationActivitySwitchInteractorImpl.kt @@ -2,11 +2,14 @@ package com.example.util.simpletimetracker.feature_notification.activitySwitch.i import com.example.util.simpletimetracker.core.interactor.FilterGoalsByDayOfWeekInteractor import com.example.util.simpletimetracker.core.interactor.GetCurrentRecordsDurationInteractor +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.interactor.GetSelectableTagsInteractor import com.example.util.simpletimetracker.domain.interactor.NotificationActivitySwitchInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor @@ -15,9 +18,13 @@ import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.feature_notification.R import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationActivitySwitchManager import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationActivitySwitchParams +import com.example.util.simpletimetracker.feature_notification.activitySwitch.manager.NotificationControlsParams +import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import javax.inject.Inject class NotificationActivitySwitchInteractorImpl @Inject constructor( + private val iconMapper: IconMapper, + private val colorMapper: ColorMapper, private val manager: NotificationActivitySwitchManager, private val prefsInteractor: PrefsInteractor, private val resourceRepo: ResourceRepo, @@ -29,6 +36,7 @@ class NotificationActivitySwitchInteractorImpl @Inject constructor( private val filterGoalsByDayOfWeekInteractor: FilterGoalsByDayOfWeekInteractor, private val getSelectableTagsInteractor: GetSelectableTagsInteractor, private val getNotificationActivitySwitchControlsInteractor: GetNotificationActivitySwitchControlsInteractor, + private val recordInteractor: RecordInteractor, ) : NotificationActivitySwitchInteractor { override suspend fun updateNotification( @@ -60,6 +68,7 @@ class NotificationActivitySwitchInteractorImpl @Inject constructor( val showRepeatButton = prefsInteractor.getEnableRepeatButton() val firstDayOfWeek = prefsInteractor.getFirstDayOfWeek() val startOfDayShift = prefsInteractor.getStartOfDayShift() + val retroactiveTrackingModeEnabled = prefsInteractor.getRetroactiveTrackingMode() val runningRecords = runningRecordInteractor.getAll() val range = timeMapper.getRangeStartAndEnd( rangeLength = RangeLength.Day, @@ -74,6 +83,12 @@ class NotificationActivitySwitchInteractorImpl @Inject constructor( emptyList() } val recordTypes = recordTypeInteractor.getAll().associateBy(RecordType::id) + val prevRecord = if (retroactiveTrackingModeEnabled) { + recordInteractor.getPrev(timeStarted = System.currentTimeMillis()).firstOrNull() + } else { + null + } + val prevRecordType = prevRecord?.typeId?.let(recordTypes::get) val goals = filterGoalsByDayOfWeekInteractor.execute( goals = recordTypeGoalInteractor.getAllTypeGoals(), range = range, @@ -89,7 +104,7 @@ class NotificationActivitySwitchInteractorImpl @Inject constructor( emptyMap() } val controls = getNotificationActivitySwitchControlsInteractor.getControls( - hintIsVisible = false, + hint = "", // Replaced later. isDarkTheme = isDarkTheme, types = recordTypes.values.toList(), runningRecords = runningRecords, @@ -101,11 +116,61 @@ class NotificationActivitySwitchInteractorImpl @Inject constructor( goals = goals, allDailyCurrents = allDailyCurrents, ) + val hint: String + val icon: RecordTypeIcon? + val color: Int? + val title: String + val subtitle: String + val untrackedTimeStarted: Long? + val prevRecordDuration: Long? + when { + retroactiveTrackingModeEnabled && prevRecord != null && prevRecordType != null -> { + hint = resourceRepo.getString(R.string.retroactive_tracking_mode_hint) + icon = prevRecordType.icon.let(iconMapper::mapIcon) + color = colorMapper.mapToColorInt(prevRecordType.color, isDarkTheme) + title = resourceRepo.getString(R.string.statistics_detail_last_record) + + " - " + + prevRecordType.name + subtitle = timeMapper.formatTime( + time = prevRecord.timeEnded, + useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat(), + showSeconds = prefsInteractor.getShowSeconds(), + ).let { resourceRepo.getString(R.string.notification_time_ended, it) } + untrackedTimeStarted = prevRecord.timeEnded + prevRecordDuration = prevRecord.timeEnded - prevRecord.timeStarted + } + retroactiveTrackingModeEnabled -> { + hint = "" + icon = RecordTypeIcon.Image(R.drawable.unknown) + color = colorMapper.toUntrackedColor(isDarkTheme) + title = resourceRepo.getString(R.string.retroactive_tracking_mode_hint) + subtitle = "" + untrackedTimeStarted = null + prevRecordDuration = null + } + else -> { + hint = "" + icon = RecordTypeIcon.Image(R.drawable.app_ic_launcher_monochrome) + color = colorMapper.toUntrackedColor(isDarkTheme) + title = resourceRepo.getString(R.string.running_records_empty) + subtitle = "" + untrackedTimeStarted = null + prevRecordDuration = null + } + } NotificationActivitySwitchParams( - title = resourceRepo.getString(R.string.running_records_empty), + icon = icon, + color = color, + title = title, + subtitle = subtitle, isDarkTheme = prefsInteractor.getDarkMode(), - controls = controls, + untrackedStartedTimeStamp = untrackedTimeStarted, + prevRecordDuration = prevRecordDuration, + controls = when (controls) { + is NotificationControlsParams.Disabled -> controls + is NotificationControlsParams.Enabled -> controls.copy(hint = hint) + }, ).let(manager::show) } diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchManager.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchManager.kt index e06ce0ed7..bcf88c6b4 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchManager.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchManager.kt @@ -6,19 +6,22 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.os.Build +import android.os.SystemClock import android.view.ContextThemeWrapper +import android.view.View import android.widget.RemoteViews import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.example.util.simpletimetracker.core.extension.allowVmViolations -import com.example.util.simpletimetracker.feature_views.extension.getBitmapFromView -import com.example.util.simpletimetracker.feature_views.extension.measureExactly import com.example.util.simpletimetracker.core.mapper.ColorMapper import com.example.util.simpletimetracker.core.utils.PendingIntents -import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import com.example.util.simpletimetracker.feature_notification.R import com.example.util.simpletimetracker.feature_notification.recordType.customView.NotificationIconView +import com.example.util.simpletimetracker.feature_views.extension.getBitmapFromView +import com.example.util.simpletimetracker.feature_views.extension.measureExactly +import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import com.example.util.simpletimetracker.navigation.Router import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject @@ -95,15 +98,37 @@ class NotificationActivitySwitchManager @Inject constructor( params: NotificationActivitySwitchParams, isBig: Boolean, ): RemoteViews { - val iconBitmap = iconView.apply { - itemIcon = RecordTypeIcon.Image(R.drawable.app_ic_launcher_monochrome) - itemColor = colorMapper.toUntrackedColor(params.isDarkTheme) - measureExactly(iconSize) - }.getBitmapFromView() - return RemoteViews(context.packageName, R.layout.notification_activity_switch_layout).apply { - setImageViewBitmap(R.id.ivNotificationActivitySwitchIcon, iconBitmap) - setTextViewText(R.id.tvNotificationActivitySwitchText, params.title) + setImageViewBitmap(R.id.ivNotificationActivitySwitchIcon, getIconBitmap(params.icon, params.color)) + setTextViewText(R.id.tvNotificationActivitySwitchTitle, params.title) + + if (params.subtitle.isNotEmpty()) { + setTextViewText(R.id.tvNotificationActivitySwitchSubtitle, params.subtitle) + setViewVisibility(R.id.tvNotificationActivitySwitchSubtitle, View.VISIBLE) + } else { + setViewVisibility(R.id.tvNotificationActivitySwitchSubtitle, View.GONE) + } + + if (params.untrackedStartedTimeStamp != null || params.prevRecordDuration != null) { + if (params.untrackedStartedTimeStamp != null) { + val base = SystemClock.elapsedRealtime() - + (System.currentTimeMillis() - params.untrackedStartedTimeStamp) + setChronometer(R.id.timerNotificationActivitySwitchTimer, base, null, true) + setViewVisibility(R.id.timerNotificationActivitySwitchTimer, View.VISIBLE) + } else { + setViewVisibility(R.id.timerNotificationActivitySwitchTimer, View.GONE) + } + if (params.prevRecordDuration != null) { + val base = SystemClock.elapsedRealtime() - params.prevRecordDuration + setChronometer(R.id.timerNotificationActivitySwitchTimer2, base, null, false) + setViewVisibility(R.id.timerNotificationActivitySwitchTimer2, View.VISIBLE) + } else { + setViewVisibility(R.id.timerNotificationActivitySwitchTimer2, View.GONE) + } + setViewVisibility(R.id.containerNotificationActivitySwitchTimes, View.VISIBLE) + } else { + setViewVisibility(R.id.containerNotificationActivitySwitchTimes, View.GONE) + } controlsManager.getControlsView( from = NotificationControlsManager.From.ActivitySwitch, @@ -115,6 +140,17 @@ class NotificationActivitySwitchManager @Inject constructor( } } + private fun getIconBitmap( + icon: RecordTypeIcon, + color: Int, + ): Bitmap = synchronized(iconView) { + return iconView.apply { + itemIcon = icon + itemColor = color + measureExactly(iconSize) + }.getBitmapFromView() + } + companion object { private const val NOTIFICATIONS_CHANNEL_ID = "ACTIVITY_SWITCH" private const val NOTIFICATIONS_CHANNEL_NAME = "Activity Switch" diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchParams.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchParams.kt index a1b23eed0..51c6ee8c7 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchParams.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationActivitySwitchParams.kt @@ -1,7 +1,14 @@ package com.example.util.simpletimetracker.feature_notification.activitySwitch.manager +import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon + data class NotificationActivitySwitchParams( + val icon: RecordTypeIcon, + val color: Int, val title: String, + val subtitle: String, val isDarkTheme: Boolean, + val untrackedStartedTimeStamp: Long?, + val prevRecordDuration: Long?, val controls: NotificationControlsParams, ) diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsManager.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsManager.kt index b6fbf6588..f227451c8 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsManager.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsManager.kt @@ -43,8 +43,9 @@ class NotificationControlsManager @Inject constructor( val tagsControlsVisible: Boolean = controls.tags.isNotEmpty() return RemoteViews(context.packageName, R.layout.notification_switch_controls_layout).apply { - val hintVisibility = if (controls.hintIsVisible) View.VISIBLE else View.GONE + val hintVisibility = if (controls.hint.isNotEmpty()) View.VISIBLE else View.GONE setViewVisibility(R.id.tvNotificationControlsHint, hintVisibility) + setTextViewText(R.id.tvNotificationControlsHint, controls.hint) addTypeControls(from, controls) if (tagsControlsVisible) addTagControls(from, controls) diff --git a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsParams.kt b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsParams.kt index f9c58bf5f..11544dfe2 100644 --- a/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsParams.kt +++ b/features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/activitySwitch/manager/NotificationControlsParams.kt @@ -6,7 +6,7 @@ sealed interface NotificationControlsParams { object Disabled : NotificationControlsParams data class Enabled( - val hintIsVisible: Boolean, + val hint: String, val types: List, val typesShift: Int, val tags: List, 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 025915a77..16a7003a2 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 @@ -104,7 +104,7 @@ class NotificationTypeInteractorImpl @Inject constructor( emptyMap() } getNotificationActivitySwitchControlsInteractor.getControls( - hintIsVisible = true, + hint = resourceRepo.getString(R.string.running_records_empty), isDarkTheme = isDarkTheme, types = recordTypes.values.toList(), showRepeatButton = showRepeatButton, @@ -169,7 +169,7 @@ class NotificationTypeInteractorImpl @Inject constructor( emptyMap() } getNotificationActivitySwitchControlsInteractor.getControls( - hintIsVisible = true, + hint = resourceRepo.getString(R.string.running_records_empty), isDarkTheme = isDarkTheme, types = recordTypes.values.toList(), showRepeatButton = showRepeatButton, @@ -226,8 +226,7 @@ class NotificationTypeInteractorImpl @Inject constructor( icon = recordType.icon.let(iconMapper::mapIcon), color = colorMapper.mapToColorInt(recordType.color, isDarkTheme), text = getNotificationText(recordType, recordTags), - timeStarted = - timeMapper.formatTime( + timeStarted = timeMapper.formatTime( time = runningRecord.timeStarted, useMilitaryTime = useMilitaryTime, showSeconds = showSeconds, diff --git a/features/feature_notification/src/main/res/layout/notification_activity_switch_layout.xml b/features/feature_notification/src/main/res/layout/notification_activity_switch_layout.xml index c570832e1..616f4f666 100644 --- a/features/feature_notification/src/main/res/layout/notification_activity_switch_layout.xml +++ b/features/feature_notification/src/main/res/layout/notification_activity_switch_layout.xml @@ -21,14 +21,81 @@ tools:backgroundTint="@color/red_400" tools:ignore="ContentDescription" /> - + android:gravity="center|start" + android:orientation="vertical"> + + + + + + + + + + + + + + + + diff --git a/features/feature_notification/src/main/res/layout/notification_switch_controls_layout.xml b/features/feature_notification/src/main/res/layout/notification_switch_controls_layout.xml index cabf8ff04..99da69fda 100644 --- a/features/feature_notification/src/main/res/layout/notification_switch_controls_layout.xml +++ b/features/feature_notification/src/main/res/layout/notification_switch_controls_layout.xml @@ -11,9 +11,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" - android:text="@string/running_records_empty" android:textSize="11sp" - android:visibility="visible" /> + android:visibility="gone" + tools:text="Controls hint" + tools:visibility="visible" /> , ): List { diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordsViewDataMapper.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordsViewDataMapper.kt index 854b012a7..fe99013f7 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordsViewDataMapper.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordsViewDataMapper.kt @@ -52,12 +52,8 @@ class RunningRecordsViewDataMapper @Inject constructor( ) } - // TODO RETRO move strings to res. - // TODO RETRO check repeat. - // TODO RETRO check instant activities. - // TODO RETRO check record actions. - // TODO RETRO add scroll to top on first click when there were no records, otherwise hint is not visible. - // TODO RETRO check pomodoro start on activity click. + // TODO RETRO check first enter, main, widgets, notifs, wear. + // TODO RETRO add hint about how it works and limitations. fun mapToRetroActiveMode( typesMap: Map, recordTags: List, @@ -71,7 +67,7 @@ class RunningRecordsViewDataMapper @Inject constructor( if (prevRecord == null) { result += EmptyViewData( - message = "Click on card to select what you have been doing", + message = resourceRepo.getString(R.string.retroactive_tracking_mode_hint), hint = R.string.running_records_empty_hint.let(resourceRepo::getString), ) } @@ -117,7 +113,7 @@ class RunningRecordsViewDataMapper @Inject constructor( RecordWithHintViewData(it) } result += HintViewData( - text = "Select what you have been doing", + text = resourceRepo.getString(R.string.retroactive_tracking_mode_hint), paddingTop = 0, paddingBottom = 0, ) diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt index f0311aa32..eda52f431 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/delegate/SettingsAdditionalViewModelDelegate.kt @@ -160,9 +160,8 @@ class SettingsAdditionalViewModelDelegate @Inject constructor( runningRecordInteractor.getAll().forEach { removeRunningRecordMediator.removeWithRecordAdd(it) } - // TODO RETRO update widgets - // TODO RETRO update notifs - // TODO RETRO update wear + // TODO do not update widgets if there was running records? + externalViewsInteractor.onRetroactiveTrackingModeChange() } } diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetInteractorImpl.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetInteractorImpl.kt similarity index 93% rename from features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetInteractorImpl.kt rename to features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetInteractorImpl.kt index 7132c3f76..131278ccf 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetInteractorImpl.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetInteractorImpl.kt @@ -1,4 +1,4 @@ -package com.example.util.simpletimetracker.feature_widget.interactor +package com.example.util.simpletimetracker.feature_widget.common import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor import com.example.util.simpletimetracker.domain.model.WidgetType diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetManager.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetManager.kt similarity index 97% rename from features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetManager.kt rename to features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetManager.kt index c789917e6..8cbdb3fa1 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetManager.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetManager.kt @@ -1,4 +1,4 @@ -package com.example.util.simpletimetracker.feature_widget.interactor +package com.example.util.simpletimetracker.feature_widget.common import android.appwidget.AppWidgetManager import android.content.ComponentName diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetViewsHolder.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetViewsHolder.kt similarity index 96% rename from features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetViewsHolder.kt rename to features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetViewsHolder.kt index a3f966652..2621e2de5 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/interactor/WidgetViewsHolder.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetViewsHolder.kt @@ -1,4 +1,4 @@ -package com.example.util.simpletimetracker.feature_widget.interactor +package com.example.util.simpletimetracker.feature_widget.common import android.content.Context import android.view.ContextThemeWrapper diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/di/WidgetModule.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/di/WidgetModule.kt index 49eb44a8e..9bc917f08 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/di/WidgetModule.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/di/WidgetModule.kt @@ -1,7 +1,7 @@ package com.example.util.simpletimetracker.feature_widget.di import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor -import com.example.util.simpletimetracker.feature_widget.interactor.WidgetInteractorImpl +import com.example.util.simpletimetracker.feature_widget.common.WidgetInteractorImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/single/WidgetSingleProvider.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/single/WidgetSingleProvider.kt index 43c73ef71..26125959a 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/single/WidgetSingleProvider.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/single/WidgetSingleProvider.kt @@ -27,6 +27,7 @@ import com.example.util.simpletimetracker.domain.extension.getDaily import com.example.util.simpletimetracker.domain.extension.orFalse import com.example.util.simpletimetracker.domain.interactor.AddRunningRecordMediator import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RemoveRunningRecordMediator @@ -40,7 +41,7 @@ import com.example.util.simpletimetracker.feature_views.extension.measureExactly import com.example.util.simpletimetracker.feature_views.extension.setAllMargins import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import com.example.util.simpletimetracker.feature_widget.R -import com.example.util.simpletimetracker.feature_widget.interactor.WidgetViewsHolder +import com.example.util.simpletimetracker.feature_widget.common.WidgetViewsHolder import com.example.util.simpletimetracker.navigation.params.screen.RecordTagSelectionParams import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.DelicateCoroutinesApi @@ -62,6 +63,9 @@ class WidgetSingleProvider : AppWidgetProvider() { @Inject lateinit var runningRecordInteractor: RunningRecordInteractor + @Inject + lateinit var recordInteractor: RecordInteractor + @Inject lateinit var recordTypeInteractor: RecordTypeInteractor @@ -142,6 +146,7 @@ class WidgetSingleProvider : AppWidgetProvider() { val view: View val recordTypeId = prefsInteractor.getWidget(appWidgetId) val backgroundTransparency = prefsInteractor.getWidgetBackgroundTransparencyPercent() + val retroactiveTrackingModeEnabled = prefsInteractor.getRetroactiveTrackingMode() val typeIds = typeIdsToUpdate if (typeIds.isNotEmpty() && recordTypeId !in typeIds) return@launch val runningRecord = if (runningRecordInteractor.has(recordTypeId)) { @@ -149,6 +154,12 @@ class WidgetSingleProvider : AppWidgetProvider() { } else { null } + val prevRecord = if (retroactiveTrackingModeEnabled) { + recordInteractor.getPrev(timeStarted = System.currentTimeMillis()).firstOrNull() + } else { + null + } + val isCurrentTypeLast = prevRecord?.typeId == recordTypeId val isDarkTheme: Boolean = prefsInteractor.getDarkMode() if (recordTypeId == REPEAT_BUTTON_ITEM_ID) { @@ -161,7 +172,7 @@ class WidgetSingleProvider : AppWidgetProvider() { recordTypeIcon = viewData.iconId, recordTypeName = viewData.name, recordTypeColor = viewData.color, - isRunning = false, + isColored = false, isChecked = null, isComplete = false, backgroundTransparency = backgroundTransparency, @@ -187,6 +198,11 @@ class WidgetSingleProvider : AppWidgetProvider() { } else { null } + val isColored = when { + runningRecord != null -> recordType != null + prevRecord != null -> isCurrentTypeLast + else -> false + } view = prepareView( context = context, recordTypeIcon = recordType?.icon @@ -194,7 +210,7 @@ class WidgetSingleProvider : AppWidgetProvider() { recordTypeName = recordType?.name, recordTypeColor = recordType?.color ?.let { colorMapper.mapToColorInt(it, isDarkTheme) }, - isRunning = runningRecord != null && recordType != null, + isColored = isColored, isChecked = isChecked, isComplete = recordTypeId in completeTypesStateInteractor.widgetTypeIds, backgroundTransparency = backgroundTransparency, @@ -205,13 +221,23 @@ class WidgetSingleProvider : AppWidgetProvider() { val bitmap = view.getBitmapFromView() val views = RemoteViews(context.packageName, R.layout.widget_layout) - if (runningRecord != null) { - val timeStarted = runningRecord.timeStarted - val base = SystemClock.elapsedRealtime() - (System.currentTimeMillis() - timeStarted) - views.setChronometer(R.id.timerWidget, base, null, true) - views.setViewVisibility(R.id.timerWidget, View.VISIBLE) - } else { - views.setViewVisibility(R.id.timerWidget, View.GONE) + when { + runningRecord != null -> { + val timeStarted = runningRecord.timeStarted + val base = System.currentTimeMillis() - timeStarted + setChronometer(base, R.id.timerWidget, views, true) + views.setViewVisibility(R.id.timerWidget2, View.GONE) + } + prevRecord != null && isCurrentTypeLast -> { + val base1 = System.currentTimeMillis() - prevRecord.timeEnded + val base2 = prevRecord.timeEnded - prevRecord.timeStarted + setChronometer(base1, R.id.timerWidget, views, true) + setChronometer(base2, R.id.timerWidget2, views, false) + } + else -> { + views.setViewVisibility(R.id.timerWidget, View.GONE) + views.setViewVisibility(R.id.timerWidget2, View.GONE) + } } views.setImageViewBitmap(R.id.ivWidgetBackground, bitmap) views.setOnClickPendingIntent(R.id.btnWidget, getPendingSelfIntent(context, appWidgetId)) @@ -227,7 +253,7 @@ class WidgetSingleProvider : AppWidgetProvider() { recordTypeIcon: RecordTypeIcon?, recordTypeName: String?, recordTypeColor: Int?, - isRunning: Boolean, + isColored: Boolean, isChecked: Boolean?, isComplete: Boolean, backgroundTransparency: Long, @@ -238,13 +264,13 @@ class WidgetSingleProvider : AppWidgetProvider() { val name = recordTypeName ?: R.string.widget_load_error.let(resourceRepo::getString) - val textColor = if (isRunning) { + val textColor = if (isColored) { resourceRepo.getColor(R.color.colorIcon) } else { resourceRepo.getColor(R.color.widget_universal_empty_color) } - val color = if (isRunning && recordTypeColor != null) { + val color = if (isColored && recordTypeColor != null) { recordTypeColor } else { ColorUtils.changeAlpha( @@ -283,6 +309,17 @@ class WidgetSingleProvider : AppWidgetProvider() { return view } + private fun setChronometer( + timestamp: Long, + chronometerId: Int, + views: RemoteViews, + started: Boolean, + ) { + val base = SystemClock.elapsedRealtime() - timestamp + views.setChronometer(chronometerId, base, null, started) + views.setViewVisibility(chronometerId, View.VISIBLE) + } + private fun measureView(context: Context, view: View) { var width = context.resources.getDimensionPixelSize(R.dimen.record_type_card_width) var height = context.resources.getDimensionPixelSize(R.dimen.record_type_card_height) 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 3ac58542f..417be11ac 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,7 +31,7 @@ import com.example.util.simpletimetracker.feature_views.extension.measureExactly import com.example.util.simpletimetracker.feature_views.extension.pxToDp import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import com.example.util.simpletimetracker.feature_widget.R -import com.example.util.simpletimetracker.feature_widget.interactor.WidgetViewsHolder +import com.example.util.simpletimetracker.feature_widget.common.WidgetViewsHolder import com.example.util.simpletimetracker.navigation.Router import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.DelicateCoroutinesApi diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/WidgetUniversalProvider.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/WidgetUniversalProvider.kt index 6f154b05e..a23600f76 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/WidgetUniversalProvider.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/WidgetUniversalProvider.kt @@ -13,8 +13,8 @@ import android.widget.RemoteViews import com.example.util.simpletimetracker.core.extension.allowDiskRead import com.example.util.simpletimetracker.core.extension.allowVmViolations import com.example.util.simpletimetracker.core.utils.PendingIntents -import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor import com.example.util.simpletimetracker.domain.model.RunningRecord @@ -38,6 +38,9 @@ class WidgetUniversalProvider : AppWidgetProvider() { @Inject lateinit var runningRecordInteractor: RunningRecordInteractor + @Inject + lateinit var recordInteractor: RecordInteractor + @Inject lateinit var recordTypeInteractor: RecordTypeInteractor @@ -70,32 +73,81 @@ class WidgetUniversalProvider : AppWidgetProvider() { val recordTypes = recordTypeInteractor.getAll().associateBy { it.id } val isDarkTheme = prefsInteractor.getDarkMode() val backgroundTransparency = prefsInteractor.getWidgetBackgroundTransparencyPercent() + val retroactiveTrackingModeEnabled = prefsInteractor.getRetroactiveTrackingMode() + val prevRecord = if (retroactiveTrackingModeEnabled) { + recordInteractor.getPrev(timeStarted = System.currentTimeMillis()).firstOrNull() + } else { + null + } - val data = runningRecords - .let { + val data = when { + runningRecords.isNotEmpty() -> { widgetUniversalViewDataMapper.mapToWidgetViewData( - runningRecords = it, + runningRecords = runningRecords, recordTypes = recordTypes, isDarkTheme = isDarkTheme, backgroundTransparency = backgroundTransparency, ) } - .takeUnless { it.data.isEmpty() } - ?: widgetUniversalViewDataMapper.mapToEmptyWidgetViewData( - backgroundTransparency = backgroundTransparency, - ) + prevRecord != null -> { + widgetUniversalViewDataMapper.mapToRetroactiveWidgetViewData( + prevRecord = prevRecord, + recordTypes = recordTypes, + isDarkTheme = isDarkTheme, + backgroundTransparency = backgroundTransparency, + ) + } + else -> { + widgetUniversalViewDataMapper.mapToEmptyWidgetViewData( + backgroundTransparency = backgroundTransparency, + ) + } + } val view = prepareView(context, data) measureView(context, view) val bitmap = view.getBitmapFromView() val views = RemoteViews(context.packageName, R.layout.widget_layout) - if (data.data.size > 2) { - views.setViewVisibility(R.id.timerWidget, View.GONE) - views.setViewVisibility(R.id.timerWidget2, View.GONE) - } else { - setChronometer(runningRecords.getOrNull(0), R.id.timerWidget, views) - setChronometer(runningRecords.getOrNull(1), R.id.timerWidget2, views) + when { + data.data.size > 2 -> { + views.setViewVisibility(R.id.timerWidget, View.GONE) + views.setViewVisibility(R.id.timerWidget2, View.GONE) + } + runningRecords.isNotEmpty() -> { + setChronometer( + timestamp = runningRecords.getOrNull(0)?.timeStarted + ?.let { System.currentTimeMillis() - it }, + chronometerId = R.id.timerWidget, + views = views, + started = true, + ) + setChronometer( + timestamp = runningRecords.getOrNull(1)?.timeStarted + ?.let { System.currentTimeMillis() - it }, + chronometerId = R.id.timerWidget2, + views = views, + started = true, + ) + } + prevRecord != null -> { + setChronometer( + timestamp = System.currentTimeMillis() - prevRecord.timeEnded, + chronometerId = R.id.timerWidget, + views = views, + started = true, + ) + setChronometer( + timestamp = prevRecord.timeEnded - prevRecord.timeStarted, + chronometerId = R.id.timerWidget2, + views = views, + started = false, + ) + } + else -> { + views.setViewVisibility(R.id.timerWidget, View.GONE) + views.setViewVisibility(R.id.timerWidget2, View.GONE) + } } views.setImageViewBitmap(R.id.ivWidgetBackground, bitmap) views.setOnClickPendingIntent(R.id.btnWidget, getPendingIntent(context)) @@ -118,14 +170,14 @@ class WidgetUniversalProvider : AppWidgetProvider() { } private fun setChronometer( - runningRecord: RunningRecord?, + timestamp: Long?, chronometerId: Int, views: RemoteViews, + started: Boolean, ) { - if (runningRecord != null) { - val timeStarted = runningRecord.timeStarted.orZero() - val base = SystemClock.elapsedRealtime() - (System.currentTimeMillis() - timeStarted) - views.setChronometer(chronometerId, base, null, true) + if (timestamp != null) { + val base = SystemClock.elapsedRealtime() - timestamp + views.setChronometer(chronometerId, base, null, started) views.setViewVisibility(chronometerId, View.VISIBLE) } else { views.setViewVisibility(chronometerId, View.GONE) diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt index 54823925d..0ed1b9f29 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/activity/viewModel/WidgetUniversalViewModel.kt @@ -11,6 +11,7 @@ import com.example.util.simpletimetracker.core.interactor.FilterGoalsByDayOfWeek import com.example.util.simpletimetracker.core.interactor.GetCurrentRecordsDurationInteractor import com.example.util.simpletimetracker.core.interactor.RecordRepeatInteractor import com.example.util.simpletimetracker.core.mapper.RecordTypeViewDataMapper +import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.interactor.AddRunningRecordMediator import com.example.util.simpletimetracker.domain.interactor.ChangeSelectedActivityFilterMediator @@ -28,6 +29,7 @@ import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerVi import com.example.util.simpletimetracker.feature_base_adapter.loader.LoaderViewData import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData import com.example.util.simpletimetracker.feature_base_adapter.recordTypeSpecial.RunningRecordTypeSpecialViewData +import com.example.util.simpletimetracker.feature_widget.R import com.example.util.simpletimetracker.feature_widget.universal.mapper.WidgetUniversalViewDataMapper import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.navigation.params.screen.RecordTagSelectionParams @@ -40,6 +42,7 @@ import javax.inject.Inject @HiltViewModel class WidgetUniversalViewModel @Inject constructor( private val router: Router, + private val resourceRepo: ResourceRepo, private val addRunningRecordMediator: AddRunningRecordMediator, private val removeRunningRecordMediator: RemoveRunningRecordMediator, private val recordTypeInteractor: RecordTypeInteractor, @@ -154,6 +157,7 @@ class WidgetUniversalViewModel @Inject constructor( val recordTypesRunning = runningRecords.map(RunningRecord::id) val numberOfCards = prefsInteractor.getNumberOfCards() val isDarkTheme = prefsInteractor.getDarkMode() + val retroactiveTrackingMode = prefsInteractor.getRetroactiveTrackingMode() val goals = filterGoalsByDayOfWeekInteractor .execute(recordTypeGoalInteractor.getAllTypeGoals()) .groupBy { it.idData.value } @@ -209,7 +213,7 @@ class WidgetUniversalViewModel @Inject constructor( } else { recordTypesViewData.let(::addAll) repeatViewData.let(::add) - widgetUniversalViewDataMapper.mapToHint().let(::add) + widgetUniversalViewDataMapper.mapToHint(retroactiveTrackingMode).let(::add) } } } diff --git a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/mapper/WidgetUniversalViewDataMapper.kt b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/mapper/WidgetUniversalViewDataMapper.kt index 42093b5a5..b53f11437 100644 --- a/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/mapper/WidgetUniversalViewDataMapper.kt +++ b/features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/universal/mapper/WidgetUniversalViewDataMapper.kt @@ -4,6 +4,7 @@ import android.graphics.Color import com.example.util.simpletimetracker.core.mapper.ColorMapper import com.example.util.simpletimetracker.core.mapper.IconMapper import com.example.util.simpletimetracker.core.repo.ResourceRepo +import com.example.util.simpletimetracker.domain.model.Record import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RunningRecord import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType @@ -46,6 +47,35 @@ class WidgetUniversalViewDataMapper @Inject constructor( ) } + fun mapToRetroactiveWidgetViewData( + prevRecord: Record, + recordTypes: Map, + isDarkTheme: Boolean, + backgroundTransparency: Long, + ): WidgetUniversalViewData { + val recordType = recordTypes[prevRecord.typeId] + val data = listOf( + IconStackData( + icon = RecordTypeIcon.Image(R.drawable.unknown), + iconBackgroundColor = colorMapper.toUntrackedColor(isDarkTheme), + ), + IconStackData( + icon = recordType?.icon + ?.let(iconMapper::mapIcon) + ?: RecordTypeIcon.Image(R.drawable.unknown), + iconBackgroundColor = recordType?.color + ?.let { colorMapper.mapToColorInt(it, isDarkTheme) } + ?: Color.BLACK, + ), + ) + + return WidgetUniversalViewData( + data = data, + iconColor = R.color.white.let(resourceRepo::getColor), + backgroundAlpha = 1f - backgroundTransparency / 100f, + ) + } + fun mapToEmptyWidgetViewData( backgroundTransparency: Long, ): WidgetUniversalViewData { @@ -60,9 +90,14 @@ class WidgetUniversalViewDataMapper @Inject constructor( ) } - fun mapToHint(): ViewHolderType { - return HintViewData( - text = R.string.running_records_empty.let(resourceRepo::getString), - ) + fun mapToHint( + retroactiveTrackingMode: Boolean, + ): ViewHolderType { + val text = if (retroactiveTrackingMode) { + R.string.retroactive_tracking_mode_hint + } else { + R.string.running_records_empty + }.let(resourceRepo::getString) + return HintViewData(text = text) } } \ No newline at end of file diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index 395c74fd1..f469bc2ea 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -506,6 +506,7 @@ Example:
Are you still doing: %s? goal reached Started at %s + Finished at %s Stop Starting: %s (%s) @@ -555,6 +556,9 @@ Example:
Long break Periods until long break + + Select what you have been doing + Smileys & Emotion People & Body