Skip to content

Commit

Permalink
finish activity suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Dec 31, 2024
1 parent f9bc72b commit 16eaf44
Show file tree
Hide file tree
Showing 77 changed files with 1,170 additions and 289 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.example.util.simpletimetracker.core.interactor

import com.example.util.simpletimetracker.core.mapper.RecordTypeViewDataMapper
import com.example.util.simpletimetracker.domain.activitySuggestion.interactor.GetCurrentActivitySuggestionsInteractor
import com.example.util.simpletimetracker.domain.record.model.RunningRecord
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
import com.example.util.simpletimetracker.domain.recordType.model.RecordTypeGoal
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
import com.example.util.simpletimetracker.feature_base_adapter.recordTypeSuggestion.RecordTypeSuggestionViewData
import javax.inject.Inject

class ActivitySuggestionViewDataInteractor @Inject constructor(
private val getCurrentActivitySuggestionsInteractor: GetCurrentActivitySuggestionsInteractor,
private val recordTypeViewDataMapper: RecordTypeViewDataMapper,
) {

suspend fun getSuggestionsViewData(
recordTypesMap: Map<Long, RecordType>,
goals: Map<Long, List<RecordTypeGoal>>,
runningRecords: List<RunningRecord>,
allDailyCurrents: Map<Long, GetCurrentRecordsDurationInteractor.Result>,
completeTypeIds: Set<Long>,
numberOfCards: Int,
isDarkTheme: Boolean,
): List<ViewHolderType> {
val suggestionTypes = getCurrentActivitySuggestionsInteractor.execute(
recordTypesMap = recordTypesMap,
runningRecords = runningRecords,
)

return suggestionTypes.map { recordType ->
recordTypeViewDataMapper.map(
recordType = recordType,
numberOfCards = numberOfCards,
isDarkTheme = isDarkTheme,
checkState = recordTypeViewDataMapper.mapGoalCheckmark(
type = recordType,
goals = goals,
allDailyCurrents = allDailyCurrents,
),
isComplete = recordType.id in completeTypeIds,
).let {
RecordTypeSuggestionViewData(it)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.example.util.simpletimetracker.core.mapper

import com.example.util.simpletimetracker.domain.activitySuggestion.model.ActivitySuggestion
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
import com.example.util.simpletimetracker.feature_base_adapter.activitySuggestion.ActivitySuggestionViewData
import com.example.util.simpletimetracker.feature_base_adapter.listElement.ListElementViewData
import javax.inject.Inject

class ActivitySuggestionViewDataMapper @Inject constructor(
private val iconMapper: IconMapper,
private val colorMapper: ColorMapper,
) {

fun mapSuggestionFiltered(
suggestion: ActivitySuggestion,
isDarkTheme: Boolean,
typesMap: Map<Long, RecordType>,
typesOrder: List<Long>,
isFiltered: Boolean,
): ActivitySuggestionViewData {
val activityItems = mapTypes(
typeIds = listOf(suggestion.forTypeId).toSet(),
isDarkTheme = isDarkTheme,
typesMap = typesMap,
typesOrder = typesOrder,
)
val suggestionItems = mapTypes(
typeIds = suggestion.suggestionIds.toSet(),
isDarkTheme = isDarkTheme,
typesMap = typesMap,
typesOrder = typesOrder,
)

return ActivitySuggestionViewData(
id = suggestion.id,
activity = activityItems,
suggestions = suggestionItems,
color = if (isFiltered) {
colorMapper.toInactiveColor(isDarkTheme)
} else {
colorMapper.toActiveColor(isDarkTheme)
},
)
}

private fun mapTypes(
typeIds: Set<Long>,
isDarkTheme: Boolean,
typesMap: Map<Long, RecordType>,
typesOrder: List<Long>,
): List<ViewHolderType> {
val data = typeIds
.sortedBy { typesOrder.indexOf(it) }
.mapNotNull { typesMap[it] }
if (data.isEmpty()) return emptyList()

val result = mutableListOf<ViewHolderType>()
result += data.map {
ListElementViewData(
text = it.name,
icon = iconMapper.mapIcon(it.icon),
color = colorMapper.mapToColorInt(
color = it.color,
isDarkTheme = isDarkTheme,
),
)
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import com.example.util.simpletimetracker.domain.recordTag.model.RecordTag
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
import com.example.util.simpletimetracker.feature_base_adapter.complexRule.ComplexRuleAddViewData
import com.example.util.simpletimetracker.feature_base_adapter.complexRule.ComplexRuleElementContentViewData
import com.example.util.simpletimetracker.feature_base_adapter.complexRule.ComplexRuleElementTitleViewData
import com.example.util.simpletimetracker.feature_base_adapter.complexRule.ComplexRuleViewData
import com.example.util.simpletimetracker.feature_base_adapter.listElement.ListElementViewData
import javax.inject.Inject

class ComplexRulesViewDataMapper @Inject constructor(
Expand Down Expand Up @@ -119,7 +119,7 @@ class ComplexRulesViewDataMapper @Inject constructor(
text = mapActionTitle(action),
)
result += data.map {
ComplexRuleElementContentViewData(
ListElementViewData(
text = it.name,
icon = recordTagViewDataMapper.mapIcon(
tag = it,
Expand Down Expand Up @@ -187,7 +187,7 @@ class ComplexRulesViewDataMapper @Inject constructor(
val result = mutableListOf<ViewHolderType>()
result += ComplexRuleElementTitleViewData(title)
result += data.map {
ComplexRuleElementContentViewData(
ListElementViewData(
text = it.name,
icon = iconMapper.mapIcon(it.icon),
color = colorMapper.mapToColorInt(
Expand All @@ -211,7 +211,7 @@ class ComplexRulesViewDataMapper @Inject constructor(
text = resourceRepo.getString(R.string.range_day),
)
result += data.map {
ComplexRuleElementContentViewData(
ListElementViewData(
text = it,
icon = null,
color = resourceRepo.getColor(R.color.colorSecondary),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class RecordTypeViewDataMapper @Inject constructor(
type: RunningRecordTypeSpecialViewData.Type,
@StringRes name: Int,
icon: RecordTypeIcon,
numberOfCards: Int?, // TODO SUG revert?
numberOfCards: Int?,
isDarkTheme: Boolean,
checkState: GoalCheckmarkView.CheckState,
): RunningRecordTypeSpecialViewData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ interface ActivitySuggestionDao {
suspend fun get(id: Long): ActivitySuggestionDBO?

@Transaction
@Query("SELECT * FROM activitySuggestion WHERE :typeId in (forTypeId)")
@Query("SELECT * FROM activitySuggestion WHERE forTypeId = :typeId")
suspend fun getByTypeId(typeId: Long): List<ActivitySuggestionDBO>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(activityFilters: List<ActivitySuggestionDBO>)

@Query("DELETE FROM activitySuggestion WHERE id = :id")
suspend fun delete(id: Long)
@Query("DELETE FROM activitySuggestion WHERE id in (:ids)")
suspend fun delete(ids: List<Long>)

@Query("DELETE FROM activitySuggestion")
suspend fun clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ class ActivitySuggestionRepoImpl @Inject constructor(
afterSourceAccess = { cache = null },
)

override suspend fun remove(id: Long) = mutex.withLockedCache(
override suspend fun remove(ids: List<Long>) = mutex.withLockedCache(
logMessage = "remove",
accessSource = { dao.delete(id) },
afterSourceAccess = { cache = cache?.removeIf { it.id == id } },
accessSource = { dao.delete(ids) },
afterSourceAccess = { cache = cache?.removeIf { it.id in ids } },
)

override suspend fun clear() = mutex.withLockedCache(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,25 @@ class BackupPartialRepoImpl @Inject constructor(
it.hasActions && it.hasConditions
}?.let { complexRuleRepo.add(it) }
}
params.data.activitySuggestions.values.getNotExistingValues().forEach { suggestion ->
val newForTypeId = originalTypeIdToAddedId[suggestion.forTypeId]
?: return@forEach
val newSuggestionIds = suggestion.suggestionIds.mapNotNull {
originalTypeIdToAddedId[it]
}.takeIf {
it.isNotEmpty()
} ?: return@forEach
// Remove already existing suggestions for this typeId
// to avoid duplications.
activitySuggestionRepo.getByTypeId(newForTypeId)
.map { it.id }
.let { activitySuggestionRepo.remove(it) }
suggestion.copy(
id = 0,
forTypeId = newForTypeId,
suggestionIds = newSuggestionIds,
).let { activitySuggestionRepo.add(listOf(it)) }
}
return@withContext ResultCode.Success(
resourceRepo.getString(R.string.message_import_complete),
)
Expand Down Expand Up @@ -479,6 +498,7 @@ class BackupPartialRepoImpl @Inject constructor(
is FavouriteIcon -> IdData<FavouriteIcon>({ copy(id = it) }, { id })
is RecordTypeGoal -> IdData<RecordTypeGoal>({ copy(id = it) }, { id })
is ComplexRule -> IdData<ComplexRule>({ copy(id = it) }, { id })
is ActivitySuggestion -> IdData<ActivitySuggestion>({ copy(id = it) }, { id })
else -> IdData({ this }, { 0 })
} as IdData<T>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class AppDatabaseMigrations {
private val migration_24_25 = object : Migration(24, 25) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE IF NOT EXISTS `activitySuggestion` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `forTypeId` INTEGER NOT NULL, `suggestionIds` TEXT NOT NULL)"
"CREATE TABLE IF NOT EXISTS `activitySuggestion` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `forTypeId` INTEGER NOT NULL, `suggestionIds` TEXT NOT NULL)",
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.example.util.simpletimetracker.domain.activitySuggestion.interactor

import com.example.util.simpletimetracker.domain.activitySuggestion.model.ActivitySuggestion
import com.example.util.simpletimetracker.domain.activitySuggestion.repo.ActivitySuggestionRepo
import javax.inject.Inject

class ActivitySuggestionInteractor @Inject constructor(
private val activitySuggestionRepo: ActivitySuggestionRepo,
) {

suspend fun getAll(): List<ActivitySuggestion> {
return activitySuggestionRepo.getAll()
}

suspend fun get(id: Long): ActivitySuggestion? {
return activitySuggestionRepo.get(id)
}

suspend fun getByTypeId(id: Long): List<ActivitySuggestion> {
return activitySuggestionRepo.getByTypeId(id)
}

suspend fun add(data: List<ActivitySuggestion>) {
activitySuggestionRepo.add(data)
}

suspend fun remove(ids: List<Long>) {
activitySuggestionRepo.remove(ids)
}

suspend fun removeTypeId(id: Long) {
val idsToRemove = mutableListOf<Long>()
val suggestionsToChange = mutableListOf<ActivitySuggestion>()

getAll().forEach { suggestion ->
if (suggestion.forTypeId == id) {
idsToRemove += suggestion.id
return@forEach
}
if (id in suggestion.suggestionIds) {
val newSuggestions = suggestion.suggestionIds
.toMutableList()
.apply { removeAll { it == id } }
suggestionsToChange += suggestion.copy(
suggestionIds = newSuggestions,
)
}
}

remove(idsToRemove)
add(suggestionsToChange)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.util.simpletimetracker.domain.activitySuggestion.interactor

import com.example.util.simpletimetracker.domain.record.interactor.RecordInteractor
import com.example.util.simpletimetracker.domain.record.model.RunningRecord
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
import javax.inject.Inject

class GetCurrentActivitySuggestionsInteractor @Inject constructor(
private val recordInteractor: RecordInteractor,
private val activitySuggestionInteractor: ActivitySuggestionInteractor,
) {

suspend fun execute(
recordTypesMap: Map<Long, RecordType>,
runningRecords: List<RunningRecord>,
): List<RecordType> {
return execute(runningRecords).mapNotNull { typeId ->
recordTypesMap[typeId]?.takeIf { !it.hidden }
}
}

private suspend fun execute(
runningRecords: List<RunningRecord>,
): List<Long> {
val currentOrLast = runningRecords.minByOrNull { it.timeStarted }
?: recordInteractor.getAllPrev(System.currentTimeMillis()).firstOrNull()

val currentOrLastTypeId = currentOrLast?.typeIds?.firstOrNull()

return currentOrLastTypeId
?.let { activitySuggestionInteractor.getByTypeId(it) }
?.firstOrNull()
?.suggestionIds
.orEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ActivitySuggestionRepo {

suspend fun add(activityFilters: List<ActivitySuggestion>)

suspend fun remove(id: Long)
suspend fun remove(ids: List<Long>)

suspend fun clear()
}
Empty file.
Loading

0 comments on commit 16eaf44

Please sign in to comment.