A fork of Simple Time Tracker with WearOS support. (Now integrated upstream)
0

Configure Feed

Select the types of activity you want to include in your feed.

adding daily weekly goal notifications

razeeman (Dec 24, 2022, 12:59 PM +0300) 973b3f5d c56f7d0f

+251 -60
+21
core/src/main/java/com/example/util/simpletimetracker/core/interactor/AddRecordMediator.kt
··· 1 + package com.example.util.simpletimetracker.core.interactor 2 + 3 + import com.example.util.simpletimetracker.domain.interactor.RecordInteractor 4 + import com.example.util.simpletimetracker.domain.model.Record 5 + import com.example.util.simpletimetracker.domain.model.WidgetType 6 + import javax.inject.Inject 7 + 8 + class AddRecordMediator @Inject constructor( 9 + private val recordInteractor: RecordInteractor, 10 + private val widgetInteractor: WidgetInteractor, 11 + private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor, 12 + ) { 13 + 14 + suspend fun add( 15 + record: Record, 16 + ) { 17 + recordInteractor.add(record) 18 + notificationGoalTimeInteractor.checkAndReschedule(record.typeId) 19 + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 20 + } 21 + }
+5 -1
core/src/main/java/com/example/util/simpletimetracker/core/interactor/NotificationGoalTimeInteractor.kt
··· 1 1 package com.example.util.simpletimetracker.core.interactor 2 2 3 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 4 + 3 5 interface NotificationGoalTimeInteractor { 4 6 5 7 suspend fun checkAndReschedule(typeId: Long) 6 8 7 9 fun cancel(typeId: Long) 8 10 9 - fun show(typeId: Long) 11 + fun cancel(typeId: Long, goalTimeType: GoalTimeType) 12 + 13 + fun show(typeId: Long, goalTimeType: GoalTimeType) 10 14 }
+18
core/src/main/java/com/example/util/simpletimetracker/core/interactor/RemoveRecordMediator.kt
··· 1 + package com.example.util.simpletimetracker.core.interactor 2 + 3 + import com.example.util.simpletimetracker.domain.interactor.RecordInteractor 4 + import com.example.util.simpletimetracker.domain.model.WidgetType 5 + import javax.inject.Inject 6 + 7 + class RemoveRecordMediator @Inject constructor( 8 + private val recordInteractor: RecordInteractor, 9 + private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor, 10 + private val widgetInteractor: WidgetInteractor, 11 + ) { 12 + 13 + suspend fun remove(recordId: Long, typeId: Long) { 14 + recordInteractor.remove(recordId) 15 + notificationGoalTimeInteractor.checkAndReschedule(typeId) 16 + widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 17 + } 18 + }
+1
core/src/main/java/com/example/util/simpletimetracker/core/interactor/RemoveRunningRecordMediator.kt
··· 23 23 .toSeconds(System.currentTimeMillis() - runningRecord.timeStarted) 24 24 25 25 if (duration > durationToIgnore || durationToIgnore == 0L) { 26 + // No need to update widgets and notification because it will be done in running record remove. 26 27 recordInteractor.add( 27 28 typeId = runningRecord.id, 28 29 timeStarted = runningRecord.timeStarted,
+9 -7
core/src/main/java/com/example/util/simpletimetracker/core/sharedViewModel/RemoveRecordViewModel.kt
··· 5 5 import androidx.lifecycle.ViewModel 6 6 import androidx.lifecycle.viewModelScope 7 7 import com.example.util.simpletimetracker.core.R 8 - import com.example.util.simpletimetracker.core.interactor.WidgetInteractor 8 + import com.example.util.simpletimetracker.core.interactor.AddRecordMediator 9 + import com.example.util.simpletimetracker.core.interactor.RemoveRecordMediator 9 10 import com.example.util.simpletimetracker.core.repo.ResourceRepo 11 + import com.example.util.simpletimetracker.domain.extension.orZero 10 12 import com.example.util.simpletimetracker.domain.interactor.RecordInteractor 11 13 import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 12 14 import com.example.util.simpletimetracker.domain.model.Record 13 - import com.example.util.simpletimetracker.domain.model.WidgetType 14 15 import com.example.util.simpletimetracker.navigation.params.notification.SnackBarParams 15 16 import com.example.util.simpletimetracker.navigation.params.screen.ChangeRecordParams 16 17 import kotlinx.coroutines.launch ··· 19 20 class RemoveRecordViewModel @Inject constructor( 20 21 private val resourceRepo: ResourceRepo, 21 22 private val recordInteractor: RecordInteractor, 23 + private val addRecordMediator: AddRecordMediator, 24 + private val removeRecordMediator: RemoveRecordMediator, 22 25 private val recordTypeInteractor: RecordTypeInteractor, 23 - private val widgetInteractor: WidgetInteractor, 24 26 ) : ViewModel() { 25 27 26 28 val deleteButtonEnabled: LiveData<Boolean> = MutableLiveData() ··· 41 43 viewModelScope.launch { 42 44 if (recordId != 0L) { 43 45 val removedRecord = recordInteractor.get(recordId) 44 - val removedName = removedRecord?.typeId 46 + val typeId = removedRecord?.typeId 47 + val removedName = typeId 45 48 ?.let { recordTypeInteractor.get(it) } 46 49 ?.name 47 50 .orEmpty() ··· 53 56 else -> null 54 57 } 55 58 56 - recordInteractor.remove(recordId) 57 - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 59 + removeRecordMediator.remove(recordId, typeId.orZero()) 58 60 59 61 (needUpdate as MutableLiveData).value = true 60 62 ··· 78 80 79 81 private fun onAction(removedRecord: Record) { 80 82 viewModelScope.launch { 81 - recordInteractor.add(removedRecord) 83 + addRecordMediator.add(removedRecord) 82 84 (needUpdate as MutableLiveData).value = true 83 85 } 84 86 }
+3
core/src/main/res/values-de/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Deaktiviert</string> 122 122 <string name="change_record_type_goal_time_hint">Zielzeit</string> 123 123 <string name="change_record_type_goal_time_description">Benachrichtigung nach der angegebenen Zeit der Erfassung dieser Aktivität anzeigen.</string> 124 + <string name="change_record_type_session_goal_time">Session</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filter entfernt</string>
+3
core/src/main/res/values-es/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Deshabilitado</string> 122 122 <string name="change_record_type_goal_time_hint">Objetivo</string> 123 123 <string name="change_record_type_goal_time_description">Mostrar notificación después del tiempo de segimiento especificado de esta actividad.</string> 124 + <string name="change_record_type_session_goal_time">Sesión</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filtro eliminado</string>
+3
core/src/main/res/values-fr/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Désactivé</string> 122 122 <string name="change_record_type_goal_time_hint">Objectif de durée</string> 123 123 <string name="change_record_type_goal_time_description">Afficher une notification dès que la durée spécifiée de suivi de cette activité est dépassée.</string> 124 + <string name="change_record_type_session_goal_time">Session</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filtre supprimé</string>
+3
core/src/main/res/values-hi/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">अक्षम</string> 122 122 <string name="change_record_type_goal_time_hint">लक्ष्य काल</string> 123 123 <string name="change_record_type_goal_time_description">इस गतिविधि को ट्रैक करने के निर्सधारित समय के बाद सूचना दिखाएं।</string> 124 + <string name="change_record_type_session_goal_time">सत्र</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">फ़िल्टर हटा दिया गया</string>
+3
core/src/main/res/values-in/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Dinonaktifkan</string> 122 122 <string name="change_record_type_goal_time_hint">Waktu tujuan</string> 123 123 <string name="change_record_type_goal_time_description">Tampilkan notifikasi setelah waktu yang ditentukan dalam melacak kegiatan ini.</string> 124 + <string name="change_record_type_session_goal_time">Sidang</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filter dihapus</string>
+3
core/src/main/res/values-it/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Disabilitato</string> 122 122 <string name="change_record_type_goal_time_hint">Obiettivo temporale</string> 123 123 <string name="change_record_type_goal_time_description">Mostra notifiche dopo il tempo di tracciamento specificato per questa attività.</string> 124 + <string name="change_record_type_session_goal_time">Sessione</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filtro rimosso</string>
+3
core/src/main/res/values-ja/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">未設定</string> 122 122 <string name="change_record_type_goal_time_hint">目標時間</string> 123 123 <string name="change_record_type_goal_time_description">このアクティビティを追跡して指定時間後に通知を表示します。</string> 124 + <string name="change_record_type_session_goal_time">セッション</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">フィルターを取り外しました</string>
+3
core/src/main/res/values-nl/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Uitgeschakeld</string> 122 122 <string name="change_record_type_goal_time_hint">Doeltijd</string> 123 123 <string name="change_record_type_goal_time_description">Melding weergeven na het opgegeven tijdstip waarop deze activiteit is gevolgd.</string> 124 + <string name="change_record_type_session_goal_time">Sessie</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filter verwijderd</string>
+3
core/src/main/res/values-pt/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Desativado</string> 122 122 <string name="change_record_type_goal_time_hint">Meta</string> 123 123 <string name="change_record_type_goal_time_description">Mostrar notificação após atingir o tempo determinado para monitorar esta atividade.</string> 124 + <string name="change_record_type_session_goal_time">Sessão</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filtro removido</string>
+3
core/src/main/res/values-ru/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Выкл.</string> 122 122 <string name="change_record_type_goal_time_hint">Время цели</string> 123 123 <string name="change_record_type_goal_time_description">Показывать уведомление после указанного времени отслеживания этой активности.</string> 124 + <string name="change_record_type_session_goal_time">Сессия</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Фильтр удален</string>
+3
core/src/main/res/values-sv/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Inaktiverad</string> 122 122 <string name="change_record_type_goal_time_hint">Tidsmål</string> 123 123 <string name="change_record_type_goal_time_description">Visa notis efter att aktivitet uppnått sitt tidsmål.</string> 124 + <string name="change_record_type_session_goal_time">Session</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filter borttaget</string>
+3
core/src/main/res/values-tr/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Devre dışı</string> 122 122 <string name="change_record_type_goal_time_hint">Hedef süre</string> 123 123 <string name="change_record_type_goal_time_description">Bu aktivite belirtilen süre kadar takip edildiğinde bildirim göster.</string> 124 + <string name="change_record_type_session_goal_time">Dönem</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Filtre kaldırıldı</string>
+3
core/src/main/res/values-uk/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">Відключено</string> 122 122 <string name="change_record_type_goal_time_hint">Час цілі</string> 123 123 <string name="change_record_type_goal_time_description">Показувати сповіщення після вказаного часу відстеження цієї дії.</string> 124 + <string name="change_record_type_session_goal_time">Сесія</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">Фільтр видалений</string>
+3
core/src/main/res/values-zh/strings.xml
··· 121 121 <string name="change_record_type_goal_time_disabled">不启用</string> 122 122 <string name="change_record_type_goal_time_hint">目标时间</string> 123 123 <string name="change_record_type_goal_time_description">追踪此活动经过指定时间后显示通知。</string> 124 + <string name="change_record_type_session_goal_time">会议</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 124 127 125 128 <!-- Activity filters --> 126 129 <string name="change_activity_filter_removed">过滤器已移除</string>
+2 -2
core/src/main/res/values/strings.xml
··· 122 122 <string name="change_record_type_goal_time_hint">Goal time</string> 123 123 <string name="change_record_type_goal_time_description">Show notification after specified time of tracking this activity.</string> 124 124 <string name="change_record_type_session_goal_time">Session</string> 125 - <string name="change_record_type_daily_goal_time">Day</string> 126 - <string name="change_record_type_weekly_goal_time">Week</string> 125 + <string name="change_record_type_daily_goal_time">@string/range_day</string> 126 + <string name="change_record_type_weekly_goal_time">@string/range_week</string> 127 127 128 128 <!-- Activity filters --> 129 129 <string name="change_activity_filter_removed">Filter removed</string>
+1 -1
domain/build.gradle.kts
··· 2 2 import com.example.util.simpletimetracker.Deps 3 3 4 4 plugins { 5 - id("com.android.library") 5 + id("com.android.library") // TODO java library 6 6 id("kotlin-android") 7 7 id("kotlin-kapt") 8 8 }
+7
domain/src/main/java/com/example/util/simpletimetracker/domain/model/GoalTimeType.kt
··· 1 + package com.example.util.simpletimetracker.domain.model 2 + 3 + sealed interface GoalTimeType { 4 + object Session : GoalTimeType 5 + object Day : GoalTimeType 6 + object Week : GoalTimeType 7 + }
+11 -12
features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt
··· 3 3 import androidx.lifecycle.LiveData 4 4 import androidx.lifecycle.MutableLiveData 5 5 import androidx.lifecycle.viewModelScope 6 + import com.example.util.simpletimetracker.core.interactor.AddRecordMediator 6 7 import com.example.util.simpletimetracker.core.interactor.AddRunningRecordMediator 7 8 import com.example.util.simpletimetracker.core.interactor.RecordTagViewDataInteractor 8 9 import com.example.util.simpletimetracker.core.interactor.RecordTypesViewDataInteractor 10 + import com.example.util.simpletimetracker.core.interactor.RemoveRecordMediator 9 11 import com.example.util.simpletimetracker.core.interactor.RemoveRunningRecordMediator 10 - import com.example.util.simpletimetracker.core.interactor.WidgetInteractor 11 12 import com.example.util.simpletimetracker.core.mapper.TimeMapper 12 13 import com.example.util.simpletimetracker.core.repo.ResourceRepo 13 14 import com.example.util.simpletimetracker.domain.extension.orFalse ··· 17 18 import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor 18 19 import com.example.util.simpletimetracker.domain.model.RangeLength 19 20 import com.example.util.simpletimetracker.domain.model.Record 20 - import com.example.util.simpletimetracker.domain.model.WidgetType 21 21 import com.example.util.simpletimetracker.feature_change_record.interactor.ChangeRecordViewDataInteractor 22 22 import com.example.util.simpletimetracker.feature_change_record.viewData.ChangeRecordViewData 23 23 import com.example.util.simpletimetracker.navigation.Router ··· 35 35 resourceRepo: ResourceRepo, 36 36 private val router: Router, 37 37 private val recordInteractor: RecordInteractor, 38 + private val addRecordMediator: AddRecordMediator, 39 + private val removeRecordMediator: RemoveRecordMediator, 38 40 private val changeRecordViewDataInteractor: ChangeRecordViewDataInteractor, 39 41 private val runningRecordInteractor: RunningRecordInteractor, 40 42 private val addRunningRecordMediator: AddRunningRecordMediator, 41 43 private val removeRunningRecordMediator: RemoveRunningRecordMediator, 42 44 private val timeMapper: TimeMapper, 43 - private val widgetInteractor: WidgetInteractor, 44 45 ) : ChangeRecordBaseViewModel( 45 46 router, 46 47 resourceRepo, ··· 81 82 comment = newComment, 82 83 tagIds = newCategoryIds 83 84 ).let { 84 - recordInteractor.add(it) 85 - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 85 + addRecordMediator.add(it) 86 86 router.back() 87 87 } 88 88 } ··· 96 96 comment = newComment, 97 97 tagIds = newCategoryIds 98 98 ).let { 99 - recordInteractor.add(it) 99 + addRecordMediator.add(it) 100 100 } 101 101 newTimeStarted = newTimeSplit 102 102 onSaveClick() ··· 105 105 override suspend fun onContinueClickDelegate() { 106 106 // Remove current record if exist. 107 107 (extra as? ChangeRecordParams.Tracked)?.id?.let { 108 - recordInteractor.remove(it) 109 - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 108 + val typeId = recordInteractor.get(it)?.typeId.orZero() 109 + removeRecordMediator.remove(it, typeId) 110 110 } 111 111 // Stop same type running record if exist (only one of the same type can run at once). 112 112 runningRecordInteractor.get(newTypeId) ··· 133 133 previousRecord?.copy( 134 134 timeEnded = newTimeEnded, 135 135 )?.let { 136 - recordInteractor.add(it) 137 - widgetInteractor.updateWidgets(listOf(WidgetType.STATISTICS_CHART)) 136 + addRecordMediator.add(it) 138 137 router.back() 139 138 } 140 139 } ··· 151 150 previousRecord?.copy( 152 151 timeEnded = newTimeStarted, 153 152 )?.let { 154 - recordInteractor.add(it) 153 + addRecordMediator.add(it) 155 154 } 156 155 157 156 // Find next record. ··· 162 161 nextRecord?.copy( 163 162 timeStarted = newTimeEnded, 164 163 )?.let { 165 - recordInteractor.add(it) 164 + addRecordMediator.add(it) 166 165 } 167 166 } 168 167 }
+4 -2
features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt
··· 4 4 import androidx.lifecycle.MutableLiveData 5 5 import androidx.lifecycle.viewModelScope 6 6 import com.example.util.simpletimetracker.core.extension.set 7 + import com.example.util.simpletimetracker.core.interactor.AddRecordMediator 7 8 import com.example.util.simpletimetracker.core.interactor.AddRunningRecordMediator 8 9 import com.example.util.simpletimetracker.core.interactor.RecordTagViewDataInteractor 9 10 import com.example.util.simpletimetracker.core.interactor.RecordTypesViewDataInteractor ··· 44 45 private val removeRunningRecordMediator: RemoveRunningRecordMediator, 45 46 private val runningRecordInteractor: RunningRecordInteractor, 46 47 private val recordInteractor: RecordInteractor, 48 + private val addRecordMediator: AddRecordMediator, 47 49 private val changeRunningRecordViewDataInteractor: ChangeRunningRecordViewDataInteractor, 48 50 private val resourceRepo: ResourceRepo, 49 51 ) : ChangeRecordBaseViewModel( ··· 94 96 comment = newComment, 95 97 tagIds = newCategoryIds 96 98 ).let { 97 - recordInteractor.add(it) 99 + addRecordMediator.add(it) 98 100 } 99 101 newTimeStarted = newTimeSplit 100 102 onSaveClick() ··· 112 114 previousRecord?.copy( 113 115 timeEnded = newTimeStarted, 114 116 )?.let { 115 - recordInteractor.add(it) 117 + addRecordMediator.add(it) 116 118 } 117 119 } 118 120 }
+3 -2
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/controller/NotificationGoalTimeBroadcastController.kt
··· 1 1 package com.example.util.simpletimetracker.feature_notification.goalTime.controller 2 2 3 3 import com.example.util.simpletimetracker.core.interactor.NotificationGoalTimeInteractor 4 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 4 5 import javax.inject.Inject 5 6 6 7 class NotificationGoalTimeBroadcastController @Inject constructor( 7 8 private val notificationGoalTimeInteractor: NotificationGoalTimeInteractor 8 9 ) { 9 10 10 - fun onGoalTimeReminder(typeId: Long) { 11 - notificationGoalTimeInteractor.show(typeId) 11 + fun onGoalTimeReminder(typeId: Long, goalTimeType: GoalTimeType) { 12 + notificationGoalTimeInteractor.show(typeId, goalTimeType) 12 13 } 13 14 14 15 fun onBootCompleted() {
+75 -12
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt
··· 3 3 import com.example.util.simpletimetracker.core.interactor.NotificationGoalTimeInteractor 4 4 import com.example.util.simpletimetracker.core.mapper.ColorMapper 5 5 import com.example.util.simpletimetracker.core.mapper.IconMapper 6 + import com.example.util.simpletimetracker.core.mapper.RangeMapper 6 7 import com.example.util.simpletimetracker.core.mapper.TimeMapper 7 8 import com.example.util.simpletimetracker.core.repo.ResourceRepo 8 9 import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor 10 + import com.example.util.simpletimetracker.domain.interactor.RecordInteractor 9 11 import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 10 12 import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor 13 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 14 + import com.example.util.simpletimetracker.domain.model.RangeLength 11 15 import com.example.util.simpletimetracker.feature_notification.R 12 16 import com.example.util.simpletimetracker.feature_notification.goalTime.manager.NotificationGoalTimeManager 13 17 import com.example.util.simpletimetracker.feature_notification.goalTime.manager.NotificationGoalTimeParams ··· 19 23 class NotificationGoalTimeInteractorImpl @Inject constructor( 20 24 private val resourceRepo: ResourceRepo, 21 25 private val recordTypeInteractor: RecordTypeInteractor, 26 + private val recordInteractor: RecordInteractor, 22 27 private val runningRecordInteractor: RunningRecordInteractor, 23 28 private val prefsInteractor: PrefsInteractor, 24 29 private val manager: NotificationGoalTimeManager, 25 30 private val scheduler: NotificationGoalTimeScheduler, 26 31 private val timeMapper: TimeMapper, 27 32 private val colorMapper: ColorMapper, 28 - private val iconMapper: IconMapper 33 + private val iconMapper: IconMapper, 34 + private val rangeMapper: RangeMapper, 29 35 ) : NotificationGoalTimeInteractor { 30 36 31 37 override suspend fun checkAndReschedule(typeId: Long) { 32 38 val recordType = recordTypeInteractor.get(typeId) 33 39 val runningRecord = runningRecordInteractor.get(typeId) 40 + val startOfDayShift = prefsInteractor.getStartOfDayShift() 41 + val firstDayOfWeek = prefsInteractor.getFirstDayOfWeek() 34 42 35 43 if (recordType == null || runningRecord == null) return 36 44 37 - val goalTime = recordType.goalTime * 1000 38 - val timeStarted = runningRecord.timeStarted 39 - val currentDuration = System.currentTimeMillis() - timeStarted 45 + cancel(typeId) 46 + 47 + // Session 48 + val sessionGoalTime = recordType.goalTime * 1000 49 + val sessionCurrent = System.currentTimeMillis() - runningRecord.timeStarted 50 + 51 + if (sessionGoalTime > 0L && sessionGoalTime > sessionCurrent) { 52 + scheduler.schedule(sessionGoalTime - sessionCurrent, typeId, GoalTimeType.Session) 53 + } 54 + 55 + // Daily 56 + val dailyGoalTime = recordType.dailyGoalTime * 1000 57 + val (todayRangeStart, todayRangeEnd) = timeMapper.getRangeStartAndEnd( 58 + rangeLength = RangeLength.Day, 59 + shift = 0, 60 + firstDayOfWeek = firstDayOfWeek, 61 + startOfDayShift = startOfDayShift, 62 + ) 63 + val dailyCurrent = recordInteractor.getFromRange(todayRangeStart, todayRangeEnd) 64 + .map { rangeMapper.clampToRange(it, todayRangeStart, todayRangeEnd) } 65 + .let(rangeMapper::mapToDuration) + sessionCurrent 66 + 67 + if (dailyGoalTime > 0 && dailyGoalTime > dailyCurrent) { 68 + scheduler.schedule(dailyGoalTime - dailyCurrent, typeId, GoalTimeType.Day) 69 + } 70 + 71 + // Weekly 72 + val weeklyGoalTime = recordType.weeklyGoalTime * 1000 73 + val (weekRangeStart, weekRangeEnd) = timeMapper.getRangeStartAndEnd( 74 + rangeLength = RangeLength.Week, 75 + shift = 0, 76 + firstDayOfWeek = firstDayOfWeek, 77 + startOfDayShift = startOfDayShift, 78 + ) 79 + val weeklyCurrent = recordInteractor.getFromRange(weekRangeStart, weekRangeEnd) 80 + .map { rangeMapper.clampToRange(it, weekRangeStart, weekRangeEnd) } 81 + .let(rangeMapper::mapToDuration) + sessionCurrent 40 82 41 - cancel(typeId) 42 - if (goalTime > 0L && goalTime > currentDuration) { 43 - scheduler.schedule(goalTime - currentDuration, typeId) 83 + if (weeklyGoalTime > 0 && weeklyGoalTime > weeklyCurrent) { 84 + scheduler.schedule(weeklyGoalTime - weeklyCurrent, typeId, GoalTimeType.Week) 44 85 } 45 86 } 46 87 47 88 override fun cancel(typeId: Long) { 48 - scheduler.cancelSchedule(typeId) 49 - manager.hide(typeId) 89 + listOf( 90 + GoalTimeType.Session, 91 + GoalTimeType.Day, 92 + GoalTimeType.Week 93 + ).forEach { 94 + cancel(typeId, it) 95 + } 96 + } 97 + 98 + override fun cancel(typeId: Long, goalTimeType: GoalTimeType) { 99 + scheduler.cancelSchedule(typeId, goalTimeType) 100 + manager.hide(typeId, goalTimeType) 50 101 } 51 102 52 - override fun show(typeId: Long) { 103 + override fun show(typeId: Long, goalTimeType: GoalTimeType) { 53 104 GlobalScope.launch { 54 105 val recordType = recordTypeInteractor.get(typeId) ?: return@launch 55 106 val isDarkTheme = prefsInteractor.getDarkMode() 56 107 108 + val goalTimeString = when (goalTimeType) { 109 + is GoalTimeType.Session -> recordType.goalTime 110 + is GoalTimeType.Day -> recordType.dailyGoalTime 111 + is GoalTimeType.Week -> recordType.weeklyGoalTime 112 + }.let(timeMapper::formatDuration) 113 + val goalTimeTypeString = when (goalTimeType) { 114 + is GoalTimeType.Session -> R.string.change_record_type_session_goal_time 115 + is GoalTimeType.Day -> R.string.change_record_type_daily_goal_time 116 + is GoalTimeType.Week -> R.string.change_record_type_weekly_goal_time 117 + }.let(resourceRepo::getString).let { " ($it)" } 118 + 57 119 NotificationGoalTimeParams( 58 120 typeId = recordType.id, 121 + goalTimeType = goalTimeType, 59 122 icon = recordType.icon 60 123 .let(iconMapper::mapIcon), 61 124 color = recordType.color ··· 63 126 text = recordType.name, 64 127 description = resourceRepo.getString( 65 128 R.string.notification_goal_time_description, 66 - recordType.goalTime.let(timeMapper::formatDuration) 67 - ) 129 + goalTimeString 130 + ) + goalTimeTypeString 68 131 ).let(manager::show) 69 132 } 70 133 }
+15 -6
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/manager/NotificationGoalTimeManager.kt
··· 15 15 import androidx.core.app.NotificationCompat 16 16 import androidx.core.app.NotificationManagerCompat 17 17 import com.example.util.simpletimetracker.core.utils.PendingIntents 18 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 19 + import com.example.util.simpletimetracker.feature_notification.R 20 + import com.example.util.simpletimetracker.feature_notification.recordType.customView.NotificationIconView 18 21 import com.example.util.simpletimetracker.feature_views.extension.getBitmapFromView 19 22 import com.example.util.simpletimetracker.feature_views.extension.measureExactly 20 - import com.example.util.simpletimetracker.feature_notification.R 21 - import com.example.util.simpletimetracker.feature_notification.recordType.customView.NotificationIconView 22 23 import com.example.util.simpletimetracker.navigation.Router 23 24 import dagger.hilt.android.qualifiers.ApplicationContext 24 25 import javax.inject.Inject ··· 27 28 @Singleton 28 29 class NotificationGoalTimeManager @Inject constructor( 29 30 @ApplicationContext private val context: Context, 30 - private val router: Router 31 + private val router: Router, 31 32 ) { 32 33 33 34 private val notificationManager: NotificationManagerCompat = ··· 50 51 fun show(params: NotificationGoalTimeParams) { 51 52 val notification: Notification = buildNotification(params) 52 53 createAndroidNotificationChannel() 53 - notificationManager.notify(NOTIFICATION_TAG, params.typeId.toInt(), notification) 54 + notificationManager.notify(getNotificationTag(params.goalTimeType), params.typeId.toInt(), notification) 55 + } 56 + 57 + fun hide(typeId: Long, goalTimeType: GoalTimeType) { 58 + notificationManager.cancel(getNotificationTag(goalTimeType), typeId.toInt()) 54 59 } 55 60 56 - fun hide(typeId: Long) { 57 - notificationManager.cancel(NOTIFICATION_TAG, typeId.toInt()) 61 + private fun getNotificationTag(goalTimeType: GoalTimeType): String { 62 + return NOTIFICATION_TAG + when (goalTimeType) { 63 + is GoalTimeType.Session -> "" // back support for previous versions, keep same tag 64 + is GoalTimeType.Day -> "day" 65 + is GoalTimeType.Week -> "week" 66 + } 58 67 } 59 68 60 69 private fun buildNotification(params: NotificationGoalTimeParams): Notification {
+2
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/manager/NotificationGoalTimeParams.kt
··· 1 1 package com.example.util.simpletimetracker.feature_notification.goalTime.manager 2 2 3 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 3 4 import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon 4 5 5 6 data class NotificationGoalTimeParams( 6 7 val typeId: Long, 8 + val goalTimeType: GoalTimeType, 7 9 val icon: RecordTypeIcon, 8 10 val color: Int, 9 11 val text: String,
+15 -10
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/scheduler/NotificationGoalTimeScheduler.kt
··· 7 7 import android.content.Intent 8 8 import android.os.Build 9 9 import com.example.util.simpletimetracker.core.utils.PendingIntents 10 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 10 11 import com.example.util.simpletimetracker.feature_notification.recevier.NotificationReceiver 11 12 import dagger.hilt.android.qualifiers.ApplicationContext 12 13 import javax.inject.Inject ··· 18 19 private val alarmManager 19 20 get() = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager 20 21 21 - fun schedule(durationMillis: Long, typeId: Long) { 22 - val timestamp = System.currentTimeMillis() + durationMillis 22 + fun schedule(durationMillisFromNow: Long, typeId: Long, goalTimeType: GoalTimeType) { 23 + val timestamp = System.currentTimeMillis() + durationMillisFromNow 23 24 24 - scheduleAtTime(timestamp, typeId) 25 + scheduleAtTime(timestamp, typeId, goalTimeType) 25 26 } 26 27 27 - fun cancelSchedule(typeId: Long) { 28 - alarmManager?.cancel(getPendingIntent(typeId)) 28 + fun cancelSchedule(typeId: Long, goalTimeType: GoalTimeType) { 29 + alarmManager?.cancel(getPendingIntent(typeId, goalTimeType)) 29 30 } 30 31 31 - private fun scheduleAtTime(timestamp: Long, typeId: Long) { 32 + private fun scheduleAtTime(timestamp: Long, typeId: Long, goalTimeType: GoalTimeType) { 32 33 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 33 - alarmManager?.setAndAllowWhileIdle(RTC_WAKEUP, timestamp, getPendingIntent(typeId)) 34 + alarmManager?.setAndAllowWhileIdle(RTC_WAKEUP, timestamp, getPendingIntent(typeId, goalTimeType)) 34 35 } else { 35 - alarmManager?.set(RTC_WAKEUP, timestamp, getPendingIntent(typeId)) 36 + alarmManager?.set(RTC_WAKEUP, timestamp, getPendingIntent(typeId, goalTimeType)) 36 37 } 37 38 } 38 39 39 - private fun getPendingIntent(typeId: Long): PendingIntent { 40 + private fun getPendingIntent(typeId: Long, goalTimeType: GoalTimeType): PendingIntent { 40 41 val intent = Intent(context, NotificationReceiver::class.java).apply { 41 - action = NotificationReceiver.ACTION_GOAL_TIME_REMINDER 42 + action = when (goalTimeType) { 43 + is GoalTimeType.Session -> NotificationReceiver.ACTION_GOAL_TIME_REMINDER_SESSION 44 + is GoalTimeType.Day -> NotificationReceiver.ACTION_GOAL_TIME_REMINDER_DAILY 45 + is GoalTimeType.Week -> NotificationReceiver.ACTION_GOAL_TIME_REMINDER_WEEKLY 46 + } 42 47 putExtra(NotificationReceiver.EXTRA_GOAL_TIME_TYPE_ID, typeId) 43 48 } 44 49
+20 -5
features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/recevier/NotificationReceiver.kt
··· 8 8 import com.example.util.simpletimetracker.core.utils.EXTRA_ACTIVITY_NAME 9 9 import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_COMMENT 10 10 import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TAG_NAME 11 + import com.example.util.simpletimetracker.domain.model.GoalTimeType 11 12 import com.example.util.simpletimetracker.feature_notification.goalTime.controller.NotificationGoalTimeBroadcastController 12 13 import com.example.util.simpletimetracker.feature_notification.inactivity.controller.NotificationInactivityBroadcastController 13 14 import com.example.util.simpletimetracker.feature_notification.recordType.controller.NotificationTypeBroadcastController ··· 27 28 lateinit var goalTimeController: NotificationGoalTimeBroadcastController 28 29 29 30 override fun onReceive(context: Context?, intent: Intent?) { 30 - if (context == null || intent == null || intent.action == null) return 31 + val action = intent?.action 32 + if (context == null || intent == null || action == null) return 31 33 32 - when (intent.action) { 34 + when (action) { 33 35 ACTION_INACTIVITY_REMINDER -> { 34 36 inactivityController.onInactivityReminder() 35 37 } 36 - ACTION_GOAL_TIME_REMINDER -> { 38 + ACTION_GOAL_TIME_REMINDER_SESSION, 39 + ACTION_GOAL_TIME_REMINDER_DAILY, 40 + ACTION_GOAL_TIME_REMINDER_WEEKLY, 41 + -> { 37 42 val typeId = intent.getLongExtra(EXTRA_GOAL_TIME_TYPE_ID, 0) 38 - goalTimeController.onGoalTimeReminder(typeId) 43 + val goalTimeType = when (action) { 44 + ACTION_GOAL_TIME_REMINDER_SESSION -> GoalTimeType.Session 45 + ACTION_GOAL_TIME_REMINDER_DAILY -> GoalTimeType.Day 46 + ACTION_GOAL_TIME_REMINDER_WEEKLY -> GoalTimeType.Week 47 + else -> GoalTimeType.Session 48 + } 49 + goalTimeController.onGoalTimeReminder(typeId, goalTimeType) 39 50 } 40 51 ACTION_START_ACTIVITY -> { 41 52 val name = intent.getStringExtra(EXTRA_ACTIVITY_NAME) ··· 66 77 companion object { 67 78 const val ACTION_INACTIVITY_REMINDER = 68 79 "com.razeeman.util.simpletimetracker.ACTION_INACTIVITY_REMINDER" 69 - const val ACTION_GOAL_TIME_REMINDER = 80 + const val ACTION_GOAL_TIME_REMINDER_SESSION = 70 81 "com.razeeman.util.simpletimetracker.ACTION_GOAL_TIME_REMINDER" 82 + const val ACTION_GOAL_TIME_REMINDER_DAILY = 83 + "com.razeeman.util.simpletimetracker.ACTION_GOAL_TIME_REMINDER_DAILY" 84 + const val ACTION_GOAL_TIME_REMINDER_WEEKLY = 85 + "com.razeeman.util.simpletimetracker.ACTION_GOAL_TIME_REMINDER_WEEKLY" 71 86 const val EXTRA_GOAL_TIME_TYPE_ID = 72 87 "extra_goal_time_type_id" 73 88 }