Skip to content

Commit b2219d6

Browse files
committed
add external event for goal completed
1 parent e112c9f commit b2219d6

File tree

35 files changed

+219
-6
lines changed

35 files changed

+219
-6
lines changed

core/src/main/java/com/example/util/simpletimetracker/core/utils/CoreConsts.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ const val ACTION_EXTERNAL_CHANGE_RECORD = "com.razeeman.util.simpletimetracker.A
1818

1919
const val EVENT_STARTED_ACTIVITY = "com.razeeman.util.simpletimetracker.EVENT_STARTED_ACTIVITY"
2020
const val EVENT_STOPPED_ACTIVITY = "com.razeeman.util.simpletimetracker.EVENT_STOPPED_ACTIVITY"
21+
const val EVENT_COMPLETED_GOAL = "com.razeeman.util.simpletimetracker.EVENT_COMPLETED_GOAL"
2122

2223
const val EXTRA_ACTIVITY_NAME = "extra_activity_name"
24+
const val EXTRA_CATEGORY_NAME = "extra_category_name"
2325
const val EXTRA_RECORD_COMMENT = "extra_record_comment"
2426
const val EXTRA_RECORD_TAG_NAME = "extra_record_tag"
2527
const val EXTRA_RECORD_TYPE_NOTE = "extra_record_type_note"
@@ -28,4 +30,6 @@ const val EXTRA_RECORD_TIME_STARTED = "extra_record_time_started"
2830
const val EXTRA_RECORD_TIME_ENDED = "extra_record_time_ended"
2931
const val EXTRA_RECORD_COMMENT_MODE = "extra_record_comment_mode" // set, append, prefix
3032
const val EXTRA_FIND_RECORD_MODE = "extra_find_record_mode" // current_or_last, current, last
31-
const val EXTRA_FIND_RECORD_WITH_ACTIVITY_NAME = "extra_find_record_with_activity_name"
33+
const val EXTRA_FIND_RECORD_WITH_ACTIVITY_NAME = "extra_find_record_with_activity_name"
34+
const val EXTRA_GOAL_TYPE = "extra_goal_type" // duration, count
35+
const val EXTRA_GOAL_VALUE = "extra_goal_value"

domain/src/main/java/com/example/util/simpletimetracker/domain/notifications/interactor/ActivityStartedStoppedBroadcastInteractor.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.example.util.simpletimetracker.domain.notifications.interactor
22

3+
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal
4+
35
interface ActivityStartedStoppedBroadcastInteractor {
46

57
suspend fun onActionActivityStarted(
@@ -13,4 +15,9 @@ interface ActivityStartedStoppedBroadcastInteractor {
1315
tagIds: List<Long>,
1416
comment: String,
1517
)
18+
19+
suspend fun onGoalReached(
20+
idData: RecordTypeGoal.IdData,
21+
goalType: RecordTypeGoal.Type?,
22+
)
1623
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.example.util.simpletimetracker.domain.notifications.model
2+
3+
enum class ExternalEventGoalType(val dataValue: String) {
4+
DURATION("duration"),
5+
COUNT("count"),
6+
}

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/external/ActivityStartedStoppedBroadcastInteractorImpl.kt

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,35 @@ package com.example.util.simpletimetracker.feature_notification.external
22

33
import android.content.Context
44
import android.content.Intent
5+
import com.example.util.simpletimetracker.core.utils.EVENT_COMPLETED_GOAL
56
import com.example.util.simpletimetracker.core.utils.EVENT_STARTED_ACTIVITY
67
import com.example.util.simpletimetracker.core.utils.EVENT_STOPPED_ACTIVITY
78
import com.example.util.simpletimetracker.core.utils.EXTRA_ACTIVITY_NAME
9+
import com.example.util.simpletimetracker.core.utils.EXTRA_CATEGORY_NAME
10+
import com.example.util.simpletimetracker.core.utils.EXTRA_GOAL_TYPE
11+
import com.example.util.simpletimetracker.core.utils.EXTRA_GOAL_VALUE
812
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_COMMENT
913
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TAG_NAME
1014
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TYPE_ICON
1115
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TYPE_NOTE
16+
import com.example.util.simpletimetracker.domain.category.interactor.CategoryInteractor
17+
import com.example.util.simpletimetracker.domain.category.model.Category
18+
import com.example.util.simpletimetracker.domain.extension.orZero
1219
import com.example.util.simpletimetracker.domain.notifications.interactor.ActivityStartedStoppedBroadcastInteractor
20+
import com.example.util.simpletimetracker.domain.notifications.model.ExternalEventGoalType
1321
import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor
1422
import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTagInteractor
15-
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
1623
import com.example.util.simpletimetracker.domain.recordTag.model.RecordTag
24+
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
1725
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
26+
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal
1827
import dagger.hilt.android.qualifiers.ApplicationContext
1928
import javax.inject.Inject
2029

2130
class ActivityStartedStoppedBroadcastInteractorImpl @Inject constructor(
2231
@ApplicationContext private val context: Context,
2332
private val recordTypeInteractor: RecordTypeInteractor,
33+
private val categoryInteractor: CategoryInteractor,
2434
private val recordTagInteractor: RecordTagInteractor,
2535
private val prefsInteractor: PrefsInteractor,
2636
) : ActivityStartedStoppedBroadcastInteractor {
@@ -61,6 +71,27 @@ class ActivityStartedStoppedBroadcastInteractorImpl @Inject constructor(
6171
)
6272
}
6373

74+
override suspend fun onGoalReached(
75+
idData: RecordTypeGoal.IdData,
76+
goalType: RecordTypeGoal.Type?,
77+
) {
78+
if (!prefsInteractor.getAutomatedTrackingSendEvents()) return
79+
80+
val type = (idData as? RecordTypeGoal.IdData.Type)?.value
81+
?.let { getActivity(it) }
82+
val category = (idData as? RecordTypeGoal.IdData.Category)?.value
83+
?.let { getCategory(it) }
84+
if (type == null && category == null) return
85+
86+
sendGoalBroadcast(
87+
activityName = type?.name.orEmpty(),
88+
categoryName = category?.name.orEmpty(),
89+
goalType = goalType,
90+
note = type?.note.orEmpty(),
91+
icon = type?.icon.orEmpty(),
92+
)
93+
}
94+
6495
private fun sendBroadcast(
6596
actionString: String,
6697
activityName: String,
@@ -80,12 +111,43 @@ class ActivityStartedStoppedBroadcastInteractorImpl @Inject constructor(
80111
}.let(context::sendBroadcast)
81112
}
82113

114+
private fun sendGoalBroadcast(
115+
activityName: String,
116+
categoryName: String,
117+
goalType: RecordTypeGoal.Type?,
118+
note: String,
119+
icon: String,
120+
) {
121+
val extraGoalType = when (goalType) {
122+
is RecordTypeGoal.Type.Duration -> ExternalEventGoalType.DURATION.dataValue
123+
is RecordTypeGoal.Type.Count -> ExternalEventGoalType.COUNT.dataValue
124+
null -> ""
125+
}
126+
val goalValue = goalType?.value.orZero()
127+
128+
Intent().apply {
129+
action = EVENT_COMPLETED_GOAL
130+
if (activityName.isNotEmpty()) putExtra(EXTRA_ACTIVITY_NAME, activityName)
131+
if (categoryName.isNotEmpty()) putExtra(EXTRA_CATEGORY_NAME, categoryName)
132+
if (extraGoalType.isNotEmpty()) putExtra(EXTRA_GOAL_TYPE, extraGoalType)
133+
if (goalValue != 0L) putExtra(EXTRA_GOAL_VALUE, goalValue)
134+
if (note.isNotEmpty()) putExtra(EXTRA_RECORD_TYPE_NOTE, note)
135+
if (icon.isNotEmpty()) putExtra(EXTRA_RECORD_TYPE_ICON, icon)
136+
}.let(context::sendBroadcast)
137+
}
138+
83139
private suspend fun getActivity(
84140
typeId: Long,
85141
): RecordType? {
86142
return recordTypeInteractor.get(typeId)
87143
}
88144

145+
private suspend fun getCategory(
146+
categoryId: Long,
147+
): Category? {
148+
return categoryInteractor.get(categoryId)
149+
}
150+
89151
private suspend fun getTagNames(
90152
tagIds: List<Long>,
91153
): List<String> {

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalCountInteractorImpl.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTyp
1010
import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor
1111
import com.example.util.simpletimetracker.domain.statistics.model.RangeLength
1212
import com.example.util.simpletimetracker.domain.category.model.RecordTypeCategory
13+
import com.example.util.simpletimetracker.domain.notifications.interactor.ActivityStartedStoppedBroadcastInteractor
1314
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal
1415
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal.Range
1516
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal.Type
@@ -25,6 +26,7 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
2526
private val notificationGoalParamsInteractor: NotificationGoalParamsInteractor,
2627
private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor,
2728
private val filterGoalsByDayOfWeekInteractor: FilterGoalsByDayOfWeekInteractor,
29+
private val activityStartedStoppedBroadcastInteractor: ActivityStartedStoppedBroadcastInteractor,
2830
) : NotificationGoalCountInteractor {
2931

3032
override suspend fun checkAndShow(typeId: Long) {
@@ -211,10 +213,17 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
211213
}
212214

213215
private suspend fun show(idData: RecordTypeGoal.IdData, goalRange: Range) {
214-
notificationGoalParamsInteractor.execute(
216+
val params = notificationGoalParamsInteractor.execute(
215217
idData = idData,
216218
range = goalRange,
217219
type = NotificationGoalParamsInteractor.Type.Count,
218-
)?.let(manager::show)
220+
)
221+
params?.let(manager::show)
222+
params?.let {
223+
activityStartedStoppedBroadcastInteractor.onGoalReached(
224+
idData = idData,
225+
goalType = it.goalType,
226+
)
227+
}
219228
}
220229
}

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalParamsInteractor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class NotificationGoalParamsInteractor @Inject constructor(
120120
return NotificationGoalTimeParams(
121121
idData = idData,
122122
goalRange = range,
123+
goalType = goal?.type,
123124
icon = recordType?.icon
124125
?.let(iconMapper::mapIcon)
125126
?: RecordTypeIcon.Text(""),

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/interactor/NotificationGoalTimeInteractorImpl.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTyp
1111
import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor
1212
import com.example.util.simpletimetracker.domain.statistics.model.RangeLength
1313
import com.example.util.simpletimetracker.domain.category.model.RecordTypeCategory
14+
import com.example.util.simpletimetracker.domain.notifications.interactor.ActivityStartedStoppedBroadcastInteractor
1415
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal
1516
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal.Range
1617
import com.example.util.simpletimetracker.domain.record.model.RunningRecord
@@ -43,6 +44,7 @@ class NotificationGoalTimeInteractorImpl @Inject constructor(
4344
private val notificationGoalParamsInteractor: NotificationGoalParamsInteractor,
4445
private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor,
4546
private val filterGoalsByDayOfWeekInteractor: FilterGoalsByDayOfWeekInteractor,
47+
private val activityStartedStoppedBroadcastInteractor: ActivityStartedStoppedBroadcastInteractor,
4648
) : NotificationGoalTimeInteractor {
4749

4850
override suspend fun checkAndReschedule(typeIds: List<Long>) {
@@ -71,11 +73,18 @@ class NotificationGoalTimeInteractorImpl @Inject constructor(
7173
idData: RecordTypeGoal.IdData,
7274
goalRange: Range,
7375
) {
74-
notificationGoalParamsInteractor.execute(
76+
val params = notificationGoalParamsInteractor.execute(
7577
idData = idData,
7678
range = goalRange,
7779
type = NotificationGoalParamsInteractor.Type.Duration,
78-
)?.let(manager::show)
80+
)
81+
params?.let(manager::show)
82+
params?.let {
83+
activityStartedStoppedBroadcastInteractor.onGoalReached(
84+
idData = idData,
85+
goalType = it.goalType,
86+
)
87+
}
7988
}
8089

8190
private suspend fun checkAndRescheduleType(typeId: Long) {

features/feature_notification/src/main/java/com/example/util/simpletimetracker/feature_notification/goalTime/manager/NotificationGoalTimeParams.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon
77
data class NotificationGoalTimeParams(
88
val idData: RecordTypeGoal.IdData,
99
val goalRange: RecordTypeGoal.Range,
10+
val goalType: RecordTypeGoal.Type?,
1011
val icon: RecordTypeIcon,
1112
val color: Int,
1213
val text: String,

features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/mapper/SettingsAutomatedTrackingMapper.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package com.example.util.simpletimetracker.feature_settings.mapper
22

3+
import android.graphics.Typeface
34
import android.text.SpannableString
45
import android.text.SpannableStringBuilder
6+
import android.text.Spanned
7+
import android.text.style.ForegroundColorSpan
8+
import android.text.style.StyleSpan
59
import com.example.util.simpletimetracker.core.extension.fromHtml
610
import com.example.util.simpletimetracker.core.manager.ClipboardManager
11+
import com.example.util.simpletimetracker.core.mapper.ColorMapper
712
import com.example.util.simpletimetracker.core.provider.ApplicationDataProvider
813
import com.example.util.simpletimetracker.core.repo.ResourceRepo
914
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_ADD_RECORD
@@ -14,22 +19,29 @@ import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_STOP_ACTIVI
1419
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_STOP_ALL_ACTIVITIES
1520
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_STOP_LONGEST_ACTIVITY
1621
import com.example.util.simpletimetracker.core.utils.ACTION_EXTERNAL_STOP_SHORTEST_ACTIVITY
22+
import com.example.util.simpletimetracker.core.utils.EVENT_COMPLETED_GOAL
1723
import com.example.util.simpletimetracker.core.utils.EVENT_STARTED_ACTIVITY
1824
import com.example.util.simpletimetracker.core.utils.EVENT_STOPPED_ACTIVITY
1925
import com.example.util.simpletimetracker.core.utils.EXTRA_ACTIVITY_NAME
26+
import com.example.util.simpletimetracker.core.utils.EXTRA_CATEGORY_NAME
2027
import com.example.util.simpletimetracker.core.utils.EXTRA_FIND_RECORD_MODE
2128
import com.example.util.simpletimetracker.core.utils.EXTRA_FIND_RECORD_WITH_ACTIVITY_NAME
29+
import com.example.util.simpletimetracker.core.utils.EXTRA_GOAL_TYPE
30+
import com.example.util.simpletimetracker.core.utils.EXTRA_GOAL_VALUE
2231
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_COMMENT
2332
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_COMMENT_MODE
2433
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TAG_NAME
2534
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TIME_ENDED
2635
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TIME_STARTED
2736
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TYPE_ICON
2837
import com.example.util.simpletimetracker.core.utils.EXTRA_RECORD_TYPE_NOTE
38+
import com.example.util.simpletimetracker.domain.color.model.AppColor
2939
import com.example.util.simpletimetracker.domain.extension.indexesOf
3040
import com.example.util.simpletimetracker.domain.notifications.model.ExternalActionCommentMode
3141
import com.example.util.simpletimetracker.domain.notifications.model.ExternalActionFindRecordMode
42+
import com.example.util.simpletimetracker.domain.notifications.model.ExternalEventGoalType
3243
import com.example.util.simpletimetracker.feature_settings.R
44+
import com.example.util.simpletimetracker.feature_views.TextViewRoundedSpans
3345
import com.example.util.simpletimetracker.feature_views.extension.setClickableSpan
3446
import com.example.util.simpletimetracker.feature_views.extension.setImageSpan
3547
import com.example.util.simpletimetracker.feature_views.extension.toSpannableString
@@ -41,6 +53,7 @@ import javax.inject.Inject
4153
class SettingsAutomatedTrackingMapper @Inject constructor(
4254
private val resourceRepo: ResourceRepo,
4355
private val router: Router,
56+
private val colorMapper: ColorMapper,
4457
private val clipboardManager: ClipboardManager,
4558
private val applicationDataProvider: ApplicationDataProvider,
4659
) {
@@ -154,6 +167,18 @@ class SettingsAutomatedTrackingMapper @Inject constructor(
154167
),
155168
optional = emptyList(),
156169
),
170+
AvailableAction(
171+
action = EVENT_COMPLETED_GOAL,
172+
extras = listOf(
173+
EXTRA_ACTIVITY_NAME,
174+
EXTRA_CATEGORY_NAME,
175+
EXTRA_GOAL_TYPE,
176+
EXTRA_GOAL_VALUE,
177+
EXTRA_RECORD_TYPE_NOTE,
178+
EXTRA_RECORD_TYPE_ICON,
179+
),
180+
optional = emptyList(),
181+
),
157182
),
158183
isDarkTheme = isDarkTheme,
159184
)
@@ -195,18 +220,31 @@ class SettingsAutomatedTrackingMapper @Inject constructor(
195220
description = resourceRepo.getString(R.string.settings_automated_tracking_find_with_activity),
196221
values = emptyList(),
197222
),
223+
ExtraDescription(
224+
extra = EXTRA_GOAL_TYPE,
225+
description = resourceRepo.getString(R.string.settings_automated_tracking_goal_type),
226+
values = ExternalEventGoalType.entries.map { it.dataValue },
227+
),
228+
ExtraDescription(
229+
extra = EXTRA_GOAL_VALUE,
230+
description = resourceRepo.getString(R.string.settings_automated_tracking_goal_value),
231+
values = emptyList(),
232+
)
198233
),
199234
)
200235

201236
val availableActionsHint = resourceRepo.getString(
202237
R.string.settings_automated_tracking_available_actions,
203238
).uppercase()
239+
.let { setHintSpans(it, isDarkTheme) }
204240
val availableEventsHint = resourceRepo.getString(
205241
R.string.settings_automated_tracking_available_events,
206242
).uppercase()
243+
.let { setHintSpans(it, isDarkTheme) }
207244
val extrasDescriptionsHint = resourceRepo.getString(
208245
R.string.settings_automated_tracking_extras_description,
209246
).uppercase()
247+
.let { setHintSpans(it, isDarkTheme) }
210248

211249
val finalText = SpannableStringBuilder()
212250
.append(mainText).append("\n")
@@ -375,6 +413,30 @@ class SettingsAutomatedTrackingMapper @Inject constructor(
375413
.toSpannableString()
376414
}.getOrNull() ?: SpannableString("")
377415

416+
private fun setHintSpans(
417+
text: String,
418+
isDarkTheme: Boolean,
419+
): CharSequence {
420+
val textColor = resourceRepo.getThemedAttr(
421+
attrId = R.attr.appLightTextColor,
422+
isDarkTheme = isDarkTheme,
423+
)
424+
val backgroundColor = resourceRepo.getThemedAttr(
425+
attrId = R.attr.appInactiveColor,
426+
isDarkTheme = isDarkTheme,
427+
)
428+
val spans = listOf(
429+
ForegroundColorSpan(textColor),
430+
StyleSpan(Typeface.BOLD),
431+
TextViewRoundedSpans.MarkerSpan(backgroundColor),
432+
)
433+
return text.toSpannableString().apply {
434+
for (span in spans) {
435+
setSpan(span, 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
436+
}
437+
}
438+
}
439+
378440
private fun copyToClipboard(text: String) {
379441
clipboardManager.send(text)
380442
val message = resourceRepo.getString(R.string.copied_to_clipboard)

resources/src/main/res/values-ar/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@
405405
<string name="settings_automated_tracking_find_record_mode">يختار السجل الذي تريد تغييره.</string>
406406
<string name="settings_automated_tracking_comment_mode">يختار كيفية تغيير تعليق السجل المحدد.</string>
407407
<string name="settings_automated_tracking_find_with_activity">بالإضافة إلى ذلك، يقوم بتحديد السجل حسب اسم النشاط، إذا كان هناك العديد من السجلات الحالية أو الأخيرة.</string>
408+
<string name="settings_automated_tracking_goal_type">نوع الهدف.</string>
409+
<string name="settings_automated_tracking_goal_value">قيمة الهدف، المدة بالثواني أو عدد السجلات.</string>
408410

409411
<string name="settings_save_backup">حفظ نسخة احتياطية</string>
410412
<string name="settings_save_description">قبل الحفظ، تأكد من تثبيت أحدث إصدار من التطبيق لتجنب التعارضات.</string>

0 commit comments

Comments
 (0)