Skip to content

Commit 9f64622

Browse files
committed
refactor start tag rule logic
1 parent ec00c51 commit 9f64622

File tree

29 files changed

+317
-251
lines changed

29 files changed

+317
-251
lines changed

buildSrc/src/main/kotlin/com/example/util/simpletimetracker/Base.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ object Base {
1717
const val targetSDKWear = 34
1818

1919
// Raise after wear api changes.
20-
const val wearApiVersion = 4
20+
const val wearApiVersion = 5
2121
}

core/src/main/java/com/example/util/simpletimetracker/core/dialog/TypesSelectionDialogListener.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ interface TypesSelectionDialogListener {
88
tag: String?,
99
dataIds: List<Long>,
1010
tagValues: List<RecordBase.Tag>,
11-
selectValueOnStartTagIds: List<Long> = emptyList(),
11+
selectValueOnStartTagIds: List<Long>,
1212
)
1313
}

core/src/main/java/com/example/util/simpletimetracker/core/extension/ViewDataExensions.kt

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ package com.example.util.simpletimetracker.core.extension
33
import com.example.util.simpletimetracker.core.mapper.TimeMapper
44
import com.example.util.simpletimetracker.core.viewData.ChangeRecordDateTimeState
55
import com.example.util.simpletimetracker.domain.record.model.Range
6-
import com.example.util.simpletimetracker.domain.statistics.model.RangeLength
6+
import com.example.util.simpletimetracker.domain.record.model.RecordBase
77
import com.example.util.simpletimetracker.domain.record.model.RecordDataSelectionDialogResult
88
import com.example.util.simpletimetracker.domain.record.model.RecordsFilter
9+
import com.example.util.simpletimetracker.domain.statistics.model.RangeLength
910
import com.example.util.simpletimetracker.feature_base_adapter.runningRecord.GoalTimeViewData
1011
import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon
1112
import com.example.util.simpletimetracker.navigation.params.screen.ChangeRecordDateTimeStateParams
1213
import com.example.util.simpletimetracker.navigation.params.screen.ChangeRunningRecordParams
1314
import com.example.util.simpletimetracker.navigation.params.screen.RangeLengthParams
1415
import com.example.util.simpletimetracker.navigation.params.screen.RangeParams
16+
import com.example.util.simpletimetracker.navigation.params.screen.RecordTagParam
1517
import com.example.util.simpletimetracker.navigation.params.screen.RecordTagSelectionParams
1618
import com.example.util.simpletimetracker.navigation.params.screen.RecordTypeIconParams
1719
import com.example.util.simpletimetracker.navigation.params.screen.RecordsFilterParam
@@ -280,19 +282,25 @@ fun RangeLength.toParams(): RangeLengthParams {
280282
}
281283
}
282284

283-
fun RecordDataSelectionDialogResult.toParams(): RecordTagSelectionFields {
284-
return RecordTagSelectionFields(
285-
fields = fields.map {
286-
when (it) {
287-
is RecordDataSelectionDialogResult.Field.Tags -> RecordTagSelectionParams.Field.Tags
288-
is RecordDataSelectionDialogResult.Field.Comment -> RecordTagSelectionParams.Field.Comment
289-
}
290-
},
291-
requiredTagValueSelectionTagIds = requiredTagValueSelectionTagIds,
285+
fun List<RecordDataSelectionDialogResult.Field>.toParams(): List<RecordTagSelectionParams.FieldParam> {
286+
return this.map {
287+
when (it) {
288+
is RecordDataSelectionDialogResult.Field.Tags -> RecordTagSelectionParams.FieldParam.Tags
289+
is RecordDataSelectionDialogResult.Field.Comment -> RecordTagSelectionParams.FieldParam.Comment
290+
}
291+
}
292+
}
293+
294+
fun RecordTagParam.toModel(): RecordBase.Tag {
295+
return RecordBase.Tag(
296+
tagId = tagId,
297+
numericValue = numericValue,
292298
)
293299
}
294300

295-
data class RecordTagSelectionFields(
296-
val fields: List<RecordTagSelectionParams.Field>,
297-
val requiredTagValueSelectionTagIds: List<Long>,
298-
)
301+
fun RecordBase.Tag.toParams(): RecordTagParam {
302+
return RecordTagParam(
303+
tagId = tagId,
304+
numericValue = numericValue,
305+
)
306+
}

core/src/main/java/com/example/util/simpletimetracker/core/interactor/LoadPreselectedTagsInteractor.kt

Lines changed: 0 additions & 28 deletions
This file was deleted.

domain/src/main/java/com/example/util/simpletimetracker/domain/base/LazySuspend.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package com.example.util.simpletimetracker.domain.base
22

3-
import kotlinx.coroutines.CoroutineScope
43
import kotlinx.coroutines.sync.Mutex
54
import kotlinx.coroutines.sync.withLock
65

7-
fun <T> CoroutineScope.suspendLazy(
8-
initializer: suspend CoroutineScope.() -> T,
6+
fun <T> suspendLazy(
7+
initializer: suspend () -> T,
98
) = object : SuspendLazy<T> {
109
private val mutex = Mutex()
1110
private var value: T? = null

domain/src/main/java/com/example/util/simpletimetracker/domain/record/interactor/AddRunningRecordMediator.kt

Lines changed: 16 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
package com.example.util.simpletimetracker.domain.record.interactor
22

3+
import com.example.util.simpletimetracker.domain.base.CurrentTimestampProvider
4+
import com.example.util.simpletimetracker.domain.base.SuspendLazy
5+
import com.example.util.simpletimetracker.domain.base.suspendLazy
36
import com.example.util.simpletimetracker.domain.notifications.interactor.ActivityStartedStoppedBroadcastInteractor
4-
import com.example.util.simpletimetracker.domain.complexRule.interactor.ComplexRuleProcessActionInteractor
57
import com.example.util.simpletimetracker.domain.notifications.interactor.NotificationGoalCountInteractor
8+
import com.example.util.simpletimetracker.domain.notifications.interactor.UpdateExternalViewsInteractor
69
import com.example.util.simpletimetracker.domain.pomodoro.interactor.PomodoroStartInteractor
710
import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor
8-
import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTagInteractor
9-
import com.example.util.simpletimetracker.domain.recordTag.model.RecordTagValueType
10-
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
11-
import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTypeToDefaultTagInteractor
12-
import com.example.util.simpletimetracker.domain.notifications.interactor.UpdateExternalViewsInteractor
13-
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
14-
import com.example.util.simpletimetracker.domain.base.ResultContainer
15-
import com.example.util.simpletimetracker.domain.base.CurrentTimestampProvider
16-
import com.example.util.simpletimetracker.domain.base.SuspendLazy
17-
import com.example.util.simpletimetracker.domain.base.suspendLazy
1811
import com.example.util.simpletimetracker.domain.record.model.Record
1912
import com.example.util.simpletimetracker.domain.record.model.RecordBase
2013
import com.example.util.simpletimetracker.domain.record.model.RecordDataSelectionDialogResult
2114
import com.example.util.simpletimetracker.domain.record.model.RunningRecord
22-
import com.example.util.simpletimetracker.domain.recordTag.model.RecordTag
23-
import kotlinx.coroutines.coroutineScope
15+
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
16+
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
2417
import java.util.concurrent.TimeUnit
2518
import javax.inject.Inject
2619

@@ -29,15 +22,13 @@ class AddRunningRecordMediator @Inject constructor(
2922
private val removeRunningRecordMediator: RemoveRunningRecordMediator,
3023
private val recordInteractor: RecordInteractor,
3124
private val runningRecordInteractor: RunningRecordInteractor,
32-
private val recordTagInteractor: RecordTagInteractor,
3325
private val recordTypeInteractor: RecordTypeInteractor,
3426
private val addRecordMediator: AddRecordMediator,
35-
private val recordTypeToDefaultTagInteractor: RecordTypeToDefaultTagInteractor,
3627
private val notificationGoalCountInteractor: NotificationGoalCountInteractor,
3728
private val activityStartedStoppedBroadcastInteractor: ActivityStartedStoppedBroadcastInteractor,
3829
private val shouldShowRecordDataSelectionInteractor: ShouldShowRecordDataSelectionInteractor,
3930
private val pomodoroStartInteractor: PomodoroStartInteractor,
40-
private val complexRuleProcessActionInteractor: ComplexRuleProcessActionInteractor,
31+
private val processRulesInteractor: ProcessRulesInteractor,
4132
private val updateExternalViewsInteractor: UpdateExternalViewsInteractor,
4233
private val currentTimestampProvider: CurrentTimestampProvider,
4334
) {
@@ -50,42 +41,17 @@ class AddRunningRecordMediator @Inject constructor(
5041
updateNotificationSwitch: Boolean = true,
5142
commentInputAvailable: Boolean = true,
5243
onNeedToShowTagSelection: suspend (RecordDataSelectionDialogResult) -> Unit,
53-
): Boolean = coroutineScope {
44+
): Boolean {
5445
// Already running
55-
if (runningRecordInteractor.get(typeId) != null) return@coroutineScope false
46+
if (runningRecordInteractor.get(typeId) != null) return false
5647

5748
val shouldShowTagSelectionResult = shouldShowRecordDataSelectionInteractor.execute(
5849
typeId = typeId,
5950
commentInputAvailable = commentInputAvailable,
6051
)
61-
val currentTime = currentTimestampProvider.get()
62-
val prevRecords = suspendLazy { recordInteractor.getAllPrev(currentTime) }
63-
val rulesResult = getRulesResultForStart(
64-
typeId = typeId,
65-
timeStarted = currentTime,
66-
prevRecords = prevRecords,
67-
retroactiveTrackingMode = prefsInteractor.getRetroactiveTrackingMode(),
68-
)
69-
val requiredTagValueSelectionTagIds = filterNumericTagValueSelectionTagIds(
70-
tagIds = rulesResult.tagIdsToSelectValueOnStart,
71-
)
72-
val dialogFields = shouldShowTagSelectionResult.fields.toMutableList()
73-
if (requiredTagValueSelectionTagIds.isNotEmpty() &&
74-
dialogFields.none { it is RecordDataSelectionDialogResult.Field.Tags }
75-
) {
76-
// Force tag selection dialog if any tags need value.
77-
dialogFields += RecordDataSelectionDialogResult.Field.Tags
78-
}
79-
val needsSelection = dialogFields.isNotEmpty() ||
80-
requiredTagValueSelectionTagIds.isNotEmpty()
8152

82-
return@coroutineScope if (needsSelection) {
83-
onNeedToShowTagSelection(
84-
RecordDataSelectionDialogResult(
85-
fields = dialogFields,
86-
requiredTagValueSelectionTagIds = requiredTagValueSelectionTagIds,
87-
),
88-
)
53+
return if (shouldShowTagSelectionResult.fields.isNotEmpty()) {
54+
onNeedToShowTagSelection(shouldShowTagSelectionResult)
8955
false
9056
} else {
9157
startTimer(
@@ -106,7 +72,7 @@ class AddRunningRecordMediator @Inject constructor(
10672
updateNotificationSwitch: Boolean = true,
10773
checkDefaultDuration: Boolean = true,
10874
useSelectedTags: Boolean = false,
109-
) = coroutineScope {
75+
) {
11076
val currentTime = currentTimestampProvider.get()
11177
val actualTimeStarted = when (timeStarted) {
11278
is StartTime.Current -> timeStarted.currentTimeStampMs
@@ -117,7 +83,7 @@ class AddRunningRecordMediator @Inject constructor(
11783
val actualPrevRecords = suspendLazy {
11884
recordInteractor.getAllPrev(actualTimeStarted)
11985
}
120-
val rulesResult = getRulesResultForStart(
86+
val rulesResult = processRulesInteractor.getRulesResultForStart(
12187
typeId = typeId,
12288
timeStarted = actualTimeStarted,
12389
prevRecords = actualPrevRecords,
@@ -139,7 +105,7 @@ class AddRunningRecordMediator @Inject constructor(
139105
val actualTags = if (useSelectedTags) {
140106
tags
141107
} else {
142-
getAllTags(
108+
processRulesInteractor.getAllTags(
143109
typeId = typeId,
144110
currentTags = tags,
145111
tagValuesFromRules = rulesResult.tags,
@@ -271,7 +237,7 @@ class AddRunningRecordMediator @Inject constructor(
271237
params: StartParams,
272238
prevRecords: SuspendLazy<List<Record>>,
273239
) {
274-
val prevRecord = getPrevRecordToMergeWith(params.typeId, prevRecords)
240+
val prevRecord = processRulesInteractor.getPrevRecordToMergeWith(params.typeId, prevRecords)
275241
val sameTags = prevRecord?.tags.orEmpty().sortedBy { it.tagId } == params.tags.sortedBy { it.tagId }
276242
val shouldMerge = sameTags || params.tags.isEmpty()
277243

@@ -304,68 +270,6 @@ class AddRunningRecordMediator @Inject constructor(
304270
)
305271
}
306272

307-
suspend fun processRules(
308-
typeId: Long,
309-
timeStarted: Long,
310-
prevRecords: SuspendLazy<List<Record>>,
311-
): ComplexRuleProcessActionInteractor.Result {
312-
// If no rules - no need to check them.
313-
return if (complexRuleProcessActionInteractor.hasRules()) {
314-
val currentRecords = runningRecordInteractor.getAll()
315-
val hasAnyRunningTimersOnTimeStarted = currentRecords.any {
316-
it.timeStarted <= timeStarted
317-
}
318-
val takeCurrentRecords = currentRecords.isNotEmpty() &&
319-
hasAnyRunningTimersOnTimeStarted
320-
321-
// If no current records - check closest previous.
322-
val records = if (takeCurrentRecords) currentRecords else prevRecords()
323-
324-
val currentTypeIds = records
325-
.map { it.typeIds }
326-
.flatten()
327-
.toSet()
328-
329-
complexRuleProcessActionInteractor.processRules(
330-
timeStarted = timeStarted,
331-
startingTypeId = typeId,
332-
currentTypeIds = currentTypeIds,
333-
)
334-
} else {
335-
ComplexRuleProcessActionInteractor.Result(
336-
isMultitaskingAllowed = ResultContainer.Undefined,
337-
disallowOnlyPreviousTypeIds = emptySet(),
338-
tags = emptyList(),
339-
tagIdsToSelectValueOnStart = emptySet(),
340-
)
341-
}
342-
}
343-
344-
private suspend fun getRulesResultForStart(
345-
typeId: Long,
346-
timeStarted: Long,
347-
prevRecords: SuspendLazy<List<Record>>,
348-
retroactiveTrackingMode: Boolean,
349-
): ComplexRuleProcessActionInteractor.Result {
350-
return if (
351-
retroactiveTrackingMode &&
352-
getPrevRecordToMergeWith(typeId, prevRecords) != null
353-
) {
354-
ComplexRuleProcessActionInteractor.Result(
355-
isMultitaskingAllowed = ResultContainer.Undefined,
356-
disallowOnlyPreviousTypeIds = emptySet(),
357-
tags = emptyList(),
358-
tagIdsToSelectValueOnStart = emptySet(),
359-
)
360-
} else {
361-
processRules(
362-
typeId = typeId,
363-
timeStarted = timeStarted,
364-
prevRecords = prevRecords,
365-
)
366-
}
367-
}
368-
369273
private suspend fun processMultitasking(
370274
typeId: Long,
371275
isMultitaskingAllowed: Boolean,
@@ -403,7 +307,7 @@ class AddRunningRecordMediator @Inject constructor(
403307
if (!params.isMultitaskingAllowed) return
404308

405309
val recordTypesMap = recordTypeInteractor.getAll().associateBy(RecordType::id)
406-
val mergedRecord = getPrevRecordToMergeWith(params.typeId, prevRecords)
310+
val mergedRecord = processRulesInteractor.getPrevRecordToMergeWith(params.typeId, prevRecords)
407311

408312
// Extend prev records to current time.
409313
prevRecords().filter {
@@ -427,42 +331,6 @@ class AddRunningRecordMediator @Inject constructor(
427331
}
428332
}
429333

430-
suspend fun getAllTags(
431-
typeId: Long,
432-
currentTags: List<RecordBase.Tag>,
433-
tagValuesFromRules: List<RecordBase.Tag>,
434-
): List<RecordBase.Tag> {
435-
val defaultTags = recordTypeToDefaultTagInteractor.getTags(typeId)
436-
val merged = linkedMapOf<Long, RecordBase.Tag>()
437-
438-
fun merge(tag: RecordBase.Tag) {
439-
val existing = merged[tag.tagId]
440-
// Tags with values takes precedence.
441-
if (existing == null || (existing.numericValue == null && tag.numericValue != null)) {
442-
merged[tag.tagId] = tag
443-
}
444-
}
445-
446-
currentTags.forEach(::merge)
447-
defaultTags.map { RecordBase.Tag(tagId = it, numericValue = null) }.forEach(::merge)
448-
tagValuesFromRules.forEach(::merge)
449-
450-
return merged.values.toList()
451-
}
452-
453-
private suspend fun filterNumericTagValueSelectionTagIds(tagIds: Set<Long>): List<Long> {
454-
if (tagIds.isEmpty()) return emptyList()
455-
val tags = recordTagInteractor.getAll().associateBy(RecordTag::id)
456-
return tagIds.filter { tags[it]?.valueType == RecordTagValueType.NUMERIC }
457-
}
458-
459-
private suspend fun getPrevRecordToMergeWith(
460-
typeId: Long,
461-
prevRecords: SuspendLazy<List<Record>>,
462-
): Record? {
463-
return prevRecords().firstOrNull { it.typeId == typeId }
464-
}
465-
466334
private data class StartParams(
467335
val typeId: Long,
468336
val timeStarted: Long,

0 commit comments

Comments
 (0)