Skip to content

Commit d710325

Browse files
committed
add multitasking to retroactive mode
1 parent 1acf082 commit d710325

File tree

33 files changed

+338
-149
lines changed

33 files changed

+338
-149
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
@@ -5,7 +5,7 @@ object Base {
55
const val namespace = "com.example.util.simpletimetracker"
66

77
// Raise by 2 to account for wear version code.
8-
const val versionCode = 77
8+
const val versionCode = 79
99
const val versionName = "1.46"
1010
const val minSDK = 21
1111
const val currentSDK = 34

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@ class RecordRepeatInteractor @Inject constructor(
4949
): ActionResult {
5050
val type = prefsInteractor.getRepeatButtonType()
5151

52+
// TODO repeat several records?
5253
val prevRecord = recordInteractor.getPrev(
5354
timeStarted = System.currentTimeMillis(),
54-
limit = 2,
5555
).let {
5656
when (type) {
57-
is RepeatButtonType.RepeatLast -> it.getOrNull(0)
58-
is RepeatButtonType.RepeatBeforeLast -> it.getOrNull(1)
57+
is RepeatButtonType.RepeatLast -> it
58+
is RepeatButtonType.RepeatBeforeLast -> if (it != null) {
59+
recordInteractor.getPrev(timeStarted = it.timeEnded - 1)
60+
} else {
61+
null
62+
}
5963
}
6064
} ?: run {
6165
messageShower(R.string.running_records_repeat_no_prev_record)

data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordDao.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ interface RecordDao {
5252
suspend fun getFromRangeByType(typesIds: List<Long>, start: Long, end: Long): List<RecordWithRecordTagsDBO>
5353

5454
@Transaction
55-
@Query("SELECT * FROM records WHERE time_ended <= :timeStarted ORDER BY time_ended DESC LIMIT :limit")
56-
suspend fun getPrev(timeStarted: Long, limit: Long): List<RecordWithRecordTagsDBO>
55+
@Query("SELECT * FROM records WHERE time_ended <= :timeStarted ORDER BY time_ended DESC LIMIT 1")
56+
suspend fun getPrev(timeStarted: Long): RecordWithRecordTagsDBO?
5757

5858
@Transaction
5959
@Query("SELECT * FROM records WHERE time_started >= :timeEnded ORDER BY time_started ASC LIMIT 1")
@@ -75,6 +75,14 @@ interface RecordDao {
7575
@Query("SELECT time_ended FROM records WHERE time_ended > :fromTimestamp ORDER BY time_ended ASC LIMIT 1")
7676
suspend fun getNextTimeEnded(fromTimestamp: Long): Long?
7777

78+
@Transaction
79+
@Query("SELECT * FROM records WHERE time_started = :timeStarted")
80+
suspend fun getByTimeStarted(timeStarted: Long): List<RecordWithRecordTagsDBO>
81+
82+
@Transaction
83+
@Query("SELECT * FROM records WHERE time_ended = :timeEnded")
84+
suspend fun getByTimeEnded(timeEnded: Long): List<RecordWithRecordTagsDBO>
85+
7886
@Insert(onConflict = OnConflictStrategy.REPLACE)
7987
suspend fun insert(record: RecordDBO): Long
8088

data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordRepoImpl.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ class RecordRepoImpl @Inject constructor(
107107
)
108108
}
109109

110-
override suspend fun getPrev(timeStarted: Long, limit: Long): List<Record> = withContext(Dispatchers.IO) {
110+
override suspend fun getPrev(timeStarted: Long): Record? = withContext(Dispatchers.IO) {
111111
logDataAccess("getPrev")
112-
recordDao.getPrev(timeStarted, limit).map(::mapItem)
112+
recordDao.getPrev(timeStarted)?.let(::mapItem)
113113
}
114114

115115
override suspend fun getNext(timeEnded: Long): Record? = withContext(Dispatchers.IO) {
@@ -137,6 +137,16 @@ class RecordRepoImpl @Inject constructor(
137137
recordDao.getNextTimeEnded(fromTimestamp)
138138
}
139139

140+
override suspend fun getByTimeStarted(timeStarted: Long): List<Record> = withContext(Dispatchers.IO) {
141+
logDataAccess("getByTimeStarted")
142+
recordDao.getByTimeStarted(timeStarted).map(::mapItem)
143+
}
144+
145+
override suspend fun getByTimeEnded(timeEnded: Long): List<Record> = withContext(Dispatchers.IO) {
146+
logDataAccess("getByTimeEnded")
147+
recordDao.getByTimeEnded(timeEnded).map(::mapItem)
148+
}
149+
140150
override suspend fun add(record: Record): Long = mutex.withLockedCache(
141151
logMessage = "add",
142152
accessSource = {

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

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

3-
import com.example.util.simpletimetracker.domain.model.Range
43
import com.example.util.simpletimetracker.domain.model.Record
54
import com.example.util.simpletimetracker.domain.model.RecordDataSelectionDialogResult
65
import com.example.util.simpletimetracker.domain.model.RecordType
@@ -55,11 +54,31 @@ class AddRunningRecordMediator @Inject constructor(
5554
}
5655
}
5756

57+
suspend fun startTimers(
58+
typeIds: Set<Long>,
59+
) {
60+
val current = System.currentTimeMillis()
61+
val timeStarted = StartTime.Current(current)
62+
val prevRecords = recordInteractor.getAllPrev(current)
63+
typeIds.forEachIndexed { index, id ->
64+
startTimer(
65+
typeId = id,
66+
tagIds = emptyList(),
67+
comment = "",
68+
timeStarted = timeStarted,
69+
prevRecords = PrevRecords.Records(prevRecords),
70+
// Update only on last.
71+
updateNotificationSwitch = index == typeIds.size - 1,
72+
)
73+
}
74+
}
75+
5876
suspend fun startTimer(
5977
typeId: Long,
6078
tagIds: List<Long>,
6179
comment: String,
6280
timeStarted: StartTime = StartTime.TakeCurrent,
81+
prevRecords: PrevRecords = PrevRecords.Load,
6382
updateNotificationSwitch: Boolean = true,
6483
checkDefaultDuration: Boolean = true,
6584
) {
@@ -70,25 +89,30 @@ class AddRunningRecordMediator @Inject constructor(
7089
is StartTime.Timestamp -> timeStarted.timestampMs
7190
}
7291
val retroactiveTrackingMode = prefsInteractor.getRetroactiveTrackingMode()
73-
val prevRecord = if (retroactiveTrackingMode) {
74-
recordInteractor.getPrev(actualTimeStarted).firstOrNull()
92+
val actualPrevRecords = if (retroactiveTrackingMode) {
93+
when (prevRecords) {
94+
is PrevRecords.Load -> recordInteractor.getAllPrev(actualTimeStarted)
95+
is PrevRecords.Records -> prevRecords.records
96+
}
7597
} else {
76-
null
98+
emptyList()
99+
}
100+
val rulesResult = if (
101+
retroactiveTrackingMode &&
102+
getPrevRecordToMergeWith(typeId, actualPrevRecords) != null
103+
) {
104+
// No need to check rules on merge.
105+
ComplexRuleProcessActionInteractor.Result(
106+
isMultitaskingAllowed = ResultContainer.Undefined,
107+
tagsIds = emptySet(),
108+
)
109+
} else {
110+
processRules(
111+
typeId = typeId,
112+
timeStarted = actualTimeStarted,
113+
prevRecords = actualPrevRecords,
114+
)
77115
}
78-
val rulesResult = processRules(
79-
typeId = typeId,
80-
timeStarted = if (
81-
retroactiveTrackingMode &&
82-
prevRecord != null &&
83-
shouldMergeWithPrevRecord(typeId, prevRecord)
84-
) {
85-
// If will merge - it will be one record,
86-
// so need to check rules from original start.
87-
prevRecord.timeStarted
88-
} else {
89-
actualTimeStarted
90-
},
91-
)
92116
processMultitasking(
93117
typeId = typeId,
94118
isMultitaskingAllowedByRules = rulesResult.isMultitaskingAllowed,
@@ -116,7 +140,7 @@ class AddRunningRecordMediator @Inject constructor(
116140
updateNotificationSwitch = updateNotificationSwitch,
117141
)
118142
if (retroactiveTrackingMode) {
119-
addRetroactiveModeInternal(startParams, prevRecord)
143+
addRetroactiveModeInternal(startParams, actualPrevRecords)
120144
} else {
121145
addInternal(startParams, checkDefaultDuration)
122146
}
@@ -160,12 +184,12 @@ class AddRunningRecordMediator @Inject constructor(
160184

161185
private suspend fun addRetroactiveModeInternal(
162186
params: StartParams,
163-
prevRecord: Record?,
187+
prevRecords: List<Record>,
164188
) {
165189
val type = recordTypeInteractor.get(params.typeId) ?: return
166190

167191
if (type.defaultDuration > 0L) {
168-
val newTimeStarted = prevRecord?.timeEnded
192+
val newTimeStarted = prevRecords.firstOrNull()?.timeEnded
169193
?: (params.timeStarted - type.defaultDuration * 1000)
170194
addInstantRecord(
171195
params = params.copy(timeStarted = newTimeStarted),
@@ -174,7 +198,7 @@ class AddRunningRecordMediator @Inject constructor(
174198
} else {
175199
addRecordRetroactively(
176200
params = params,
177-
prevRecord = prevRecord,
201+
prevRecords = prevRecords,
178202
)
179203
}
180204
}
@@ -218,10 +242,10 @@ class AddRunningRecordMediator @Inject constructor(
218242

219243
private suspend fun addRecordRetroactively(
220244
params: StartParams,
221-
prevRecord: Record?,
245+
prevRecords: List<Record>,
222246
) {
223-
val shouldMerge = shouldMergeWithPrevRecord(params.typeId, prevRecord)
224-
val record = if (prevRecord != null && shouldMerge) {
247+
val prevRecord = getPrevRecordToMergeWith(params.typeId, prevRecords)
248+
val record = if (prevRecord != null) {
225249
Record(
226250
id = prevRecord.id, // Updates existing record.
227251
typeId = params.typeId,
@@ -233,7 +257,7 @@ class AddRunningRecordMediator @Inject constructor(
233257
?: prevRecord.tagIds,
234258
)
235259
} else {
236-
val newTimeStarted = prevRecord?.timeEnded
260+
val newTimeStarted = prevRecords.firstOrNull()?.timeEnded
237261
?: (params.timeStarted - TimeUnit.MINUTES.toMillis(5))
238262
Record(
239263
id = 0L, // Creates new record.
@@ -253,21 +277,17 @@ class AddRunningRecordMediator @Inject constructor(
253277
private suspend fun processRules(
254278
typeId: Long,
255279
timeStarted: Long,
280+
prevRecords: List<Record>,
256281
): ComplexRuleProcessActionInteractor.Result {
257282
// If no rules - no need to check them.
258283
return if (complexRuleProcessActionInteractor.hasRules()) {
259-
// Check running records but also record that are recorded for this time.
260-
val currentRecords = runningRecordInteractor.getAll() +
261-
recordInteractor.getFromRange(Range(timeStarted, timeStarted))
284+
// TODO do not check current records for Continue action?
285+
val currentRecords = runningRecordInteractor.getAll()
262286

263287
// If no current records - check closest previous.
264-
val prevRecord = if (currentRecords.isEmpty()) {
265-
recordInteractor.getPrev(timeStarted = timeStarted, limit = 1)
266-
} else {
267-
emptySet()
268-
}
288+
val records = currentRecords.ifEmpty { prevRecords }
269289

270-
val currentTypeIds = (currentRecords + prevRecord)
290+
val currentTypeIds = records
271291
.map { it.typeIds }
272292
.flatten()
273293
.toSet()
@@ -319,11 +339,11 @@ class AddRunningRecordMediator @Inject constructor(
319339
return (tagIds + defaultTags + tagIdsFromRules).toSet().toList()
320340
}
321341

322-
private fun shouldMergeWithPrevRecord(
342+
private fun getPrevRecordToMergeWith(
323343
typeId: Long,
324-
prevRecord: Record?,
325-
): Boolean {
326-
return prevRecord != null && prevRecord.typeId == typeId
344+
prevRecords: List<Record>,
345+
): Record? {
346+
return prevRecords.firstOrNull { it.typeId == typeId }
327347
}
328348

329349
private data class StartParams(
@@ -339,4 +359,9 @@ class AddRunningRecordMediator @Inject constructor(
339359
data class Timestamp(val timestampMs: Long) : StartTime
340360
object TakeCurrent : StartTime
341361
}
362+
363+
sealed interface PrevRecords {
364+
data class Records(val records: List<Record>) : PrevRecords
365+
object Load : PrevRecords
366+
}
342367
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,26 @@ class RecordInteractor @Inject constructor(
4444
return recordRepo.get(id)
4545
}
4646

47-
suspend fun getPrev(timeStarted: Long, limit: Long = 1): List<Record> {
48-
return recordRepo.getPrev(
49-
timeStarted = timeStarted,
50-
limit = limit,
51-
)
47+
suspend fun getPrev(timeStarted: Long): Record? {
48+
return recordRepo.getPrev(timeStarted)
49+
}
50+
51+
// Can return several records ended at the same time.
52+
suspend fun getAllPrev(timeStarted: Long): List<Record> {
53+
val prev = recordRepo.getPrev(timeStarted) ?: return emptyList()
54+
return recordRepo.getByTimeEnded(prev.timeEnded)
5255
}
5356

5457
suspend fun getNext(timeEnded: Long): Record? {
5558
return recordRepo.getNext(timeEnded)
5659
}
5760

61+
// Can return several records ended at the same time.
62+
suspend fun getAllNext(timeStarted: Long): List<Record> {
63+
val prev = recordRepo.getNext(timeStarted) ?: return emptyList()
64+
return recordRepo.getByTimeStarted(prev.timeStarted)
65+
}
66+
5867
suspend fun getPrevTimeStarted(fromTimestamp: Long): Long? {
5968
return recordRepo.getPrevTimeStarted(fromTimestamp)
6069
}

domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordRepo.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ interface RecordRepo {
2525

2626
suspend fun getFromRangeByType(typeIds: List<Long>, range: Range): List<Record>
2727

28-
suspend fun getPrev(timeStarted: Long, limit: Long): List<Record>
28+
suspend fun getPrev(timeStarted: Long): Record?
2929

3030
suspend fun getNext(timeEnded: Long): Record?
3131

32+
suspend fun getByTimeStarted(timeStarted: Long): List<Record>
33+
34+
suspend fun getByTimeEnded(timeEnded: Long): List<Record>
35+
3236
suspend fun getPrevTimeStarted(fromTimestamp: Long): Long?
3337

3438
suspend fun getNextTimeStarted(fromTimestamp: Long): Long?

features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/recordType/RecordTypeAdapterDelegate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ fun createRecordTypeAdapterDelegate(
3737
itemIsChecked = item.isChecked.orFalse()
3838
itemCompleteIsAnimated = true
3939
itemIsComplete = item.isComplete
40+
val newItemScale = if (item.isSelected) 1.1f else 1.0f
41+
scaleX = newItemScale
42+
scaleY = newItemScale
4043
getCheckmarkOutline().itemIsFiltered = item.itemIsFiltered
4144
onItemClick?.let { setOnClickWith(item, it) }
4245
onItemLongClick?.let { setOnLongClick { it(item, mapOf(this to transitionName)) } }

features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/recordType/RecordTypeViewData.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ data class RecordTypeViewData(
1717
val isChecked: Boolean? = null,
1818
val itemIsFiltered: Boolean = false,
1919
val isComplete: Boolean = false,
20+
val isSelected: Boolean = false,
2021
) : ViewHolderType {
2122

2223
override fun getUniqueId(): Long = id

features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordActionsAdjustDelegate.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ class ChangeRecordActionsAdjustDelegate @Inject constructor(
318318
newTimeEnded: Long,
319319
adjustNextRecordAvailable: Boolean,
320320
): AdjacentRecords {
321-
suspend fun getNext(): Record? {
322-
return recordInteractor.getNext(newTimeEnded)
321+
suspend fun getNext(): List<Record> {
322+
return recordInteractor.getAllNext(newTimeEnded)
323323
}
324324

325325
val recordRange = Range(timeStarted = newTimeStarted, timeEnded = newTimeEnded)
@@ -328,14 +328,14 @@ class ChangeRecordActionsAdjustDelegate @Inject constructor(
328328

329329
val previousRecords = adjacentRecords
330330
.filter { it.timeStarted < newTimeStarted && it.timeEnded <= newTimeEnded }
331-
.ifEmpty { recordInteractor.getPrev(newTimeStarted) }
331+
.ifEmpty { recordInteractor.getAllPrev(newTimeStarted) }
332332
.filter { it.id != recordId }
333333
val overlappedRecords = adjacentRecords
334334
.filter { it.timeStarted >= newTimeStarted && it.timeEnded <= newTimeEnded }
335335
.filter { it.id != recordId }
336336
val nextRecords = adjacentRecords
337337
.filter { it.timeStarted >= newTimeStarted && it.timeEnded > newTimeEnded }
338-
.ifEmpty { listOfNotNull(if (adjustNextRecordAvailable) getNext() else null) }
338+
.ifEmpty { if (adjustNextRecordAvailable) getNext() else emptyList() }
339339
.takeIf { adjustNextRecordAvailable }
340340
.orEmpty()
341341
.filter { it.id != recordId }

0 commit comments

Comments
 (0)