diff --git a/core/src/main/res/values-ar/strings.xml b/core/src/main/res/values-ar/strings.xml
index 3ffed13e..3b3e5cb7 100644
--- a/core/src/main/res/values-ar/strings.xml
+++ b/core/src/main/res/values-ar/strings.xml
@@ -385,6 +385,7 @@
تحصيص الوسوم
تحصيص يومي
تحصيص مدة التتبع
+ أهم الأنشطة القادمة
- تتبع
diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml
index 46344180..c06993de 100644
--- a/core/src/main/res/values-ca/strings.xml
+++ b/core/src/main/res/values-ca/strings.xml
@@ -385,6 +385,7 @@ Exemple:
Distribució d\'etiquetes
Distribució diària
Distribució de la durada de l\'enregistrament
+ Les properes activitats principals
diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml
index 6f628be5..83313e06 100644
--- a/core/src/main/res/values-de/strings.xml
+++ b/core/src/main/res/values-de/strings.xml
@@ -385,6 +385,7 @@ Beispiel:
Tag Verteilung
Tägliche Verteilung
Verteilung der Aufzeichnungsdauer
+ Top nächste Aktivitäten
diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml
index df979256..87d5147b 100644
--- a/core/src/main/res/values-es/strings.xml
+++ b/core/src/main/res/values-es/strings.xml
@@ -385,6 +385,7 @@ Ejemplo:
Distribución de etiquetas
Distribución diaria
Distribución de la duración del registro
+ Principales próximas actividades
diff --git a/core/src/main/res/values-fa/strings.xml b/core/src/main/res/values-fa/strings.xml
index 1b447307..6f74bfe0 100644
--- a/core/src/main/res/values-fa/strings.xml
+++ b/core/src/main/res/values-fa/strings.xml
@@ -385,6 +385,7 @@
پراکندگی برچسب
پراکندگی روزانه
پراکندگی طول تاریخچه
+ برترین فعالیت های بعدی
diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml
index 2a1f2909..9875df70 100644
--- a/core/src/main/res/values-fr/strings.xml
+++ b/core/src/main/res/values-fr/strings.xml
@@ -385,6 +385,7 @@ Exemple:
Répartition des étiquettes
Distribution quotidienne
Répartition des durées d\'enregistrement
+ Top des prochaines activités
diff --git a/core/src/main/res/values-hi/strings.xml b/core/src/main/res/values-hi/strings.xml
index 9a39d711..04781a3e 100644
--- a/core/src/main/res/values-hi/strings.xml
+++ b/core/src/main/res/values-hi/strings.xml
@@ -385,6 +385,7 @@ csv फ़ाइल में कॉमा से अलग किए गए य
टैग वितरण
दैनिक वितरण
अभिलेख अवधि वितरण
+ शीर्ष अगली गतिविधियाँ
diff --git a/core/src/main/res/values-in/strings.xml b/core/src/main/res/values-in/strings.xml
index 9b8fb2bf..02682fe0 100644
--- a/core/src/main/res/values-in/strings.xml
+++ b/core/src/main/res/values-in/strings.xml
@@ -385,6 +385,7 @@ Contoh:
Distribusi label
Distribusi harian
Distribusi durasi rekaman
+ Aktivitas teratas selanjutnya
diff --git a/core/src/main/res/values-it/strings.xml b/core/src/main/res/values-it/strings.xml
index 068666cc..6dd8b5c6 100644
--- a/core/src/main/res/values-it/strings.xml
+++ b/core/src/main/res/values-it/strings.xml
@@ -385,6 +385,7 @@ Esempio:
Distribuzione etichette
Distribuzione giornaliera
Distribuzione della durata del record
+ Le migliori attività successive
diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml
index 61a45217..74130c4b 100644
--- a/core/src/main/res/values-ja/strings.xml
+++ b/core/src/main/res/values-ja/strings.xml
@@ -385,6 +385,7 @@ CSV ファイルには、カンマで区切られた次の列が含まれてい
タグ配布
日次記録
記録期間分布
+ 次に注目のアクティビティ
- 記録
diff --git a/core/src/main/res/values-ko/strings.xml b/core/src/main/res/values-ko/strings.xml
index 45c48061..531af39d 100644
--- a/core/src/main/res/values-ko/strings.xml
+++ b/core/src/main/res/values-ko/strings.xml
@@ -385,6 +385,7 @@ csv 파일은 다음과 같은 열(column)들을 가져야합니다:
태그 분포
일별 분포
기록 기간 분포
+ 다음 주요 활동
diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml
index 35bd2c34..07ffee9e 100644
--- a/core/src/main/res/values-nl/strings.xml
+++ b/core/src/main/res/values-nl/strings.xml
@@ -385,6 +385,7 @@ Voorbeeld:
Label verdeling
Dagelijkse verdeling
Verdeling opname duur
+ Top volgende activiteiten
diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml
index 3b5fb6d2..d52648cb 100644
--- a/core/src/main/res/values-pt/strings.xml
+++ b/core/src/main/res/values-pt/strings.xml
@@ -385,6 +385,7 @@ Exemplo:
Distribuição de tags
Distribuição diária
Distribuição da duração do registro
+ Principais próximas atividades
- Registro
diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml
index 00d4c9d5..748a66a5 100644
--- a/core/src/main/res/values-ru/strings.xml
+++ b/core/src/main/res/values-ru/strings.xml
@@ -385,6 +385,7 @@ CSV-файл должен содержать следующие столбцы,
Распределение тегов
Распределение в течении дня
Распределение продолжительности записи
+ Топ следующих активностей
- Записей
diff --git a/core/src/main/res/values-sv/strings.xml b/core/src/main/res/values-sv/strings.xml
index bf60ef05..9b54a283 100644
--- a/core/src/main/res/values-sv/strings.xml
+++ b/core/src/main/res/values-sv/strings.xml
@@ -385,6 +385,7 @@ Exempel:
Fördelning av taggar
Daglig fördelning
Fördelning händelselängd
+ Topp nästa aktiviteter
diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml
index 2b83b923..1352bc5d 100644
--- a/core/src/main/res/values-tr/strings.xml
+++ b/core/src/main/res/values-tr/strings.xml
@@ -385,6 +385,7 @@ CSV dosyası virgülle ayrılmış şu sütunları içermelidir:
Etiket dağılımı
Günlük dağılım
Kayıt süresi dağılımı
+ Sonraki en iyi aktiviteler
diff --git a/core/src/main/res/values-uk/strings.xml b/core/src/main/res/values-uk/strings.xml
index 43ff096b..7d75c3fe 100644
--- a/core/src/main/res/values-uk/strings.xml
+++ b/core/src/main/res/values-uk/strings.xml
@@ -385,6 +385,7 @@
Розподіл тегів
Розподіл протягом дня
Розподіл тривалості запису
+ Топ наступних заходів
- Записів
diff --git a/core/src/main/res/values-vi/strings.xml b/core/src/main/res/values-vi/strings.xml
index f750301c..6675fd05 100644
--- a/core/src/main/res/values-vi/strings.xml
+++ b/core/src/main/res/values-vi/strings.xml
@@ -385,6 +385,7 @@ Ví dụ:
Phân bổ thẻ
Phân bổ hàng ngày
Phân bổ thời lượng bản ghi
+ Hoạt động tiếp theo hàng đầu
diff --git a/core/src/main/res/values-zh-rTW/strings.xml b/core/src/main/res/values-zh-rTW/strings.xml
index da58a8cd..4a17d5ab 100644
--- a/core/src/main/res/values-zh-rTW/strings.xml
+++ b/core/src/main/res/values-zh-rTW/strings.xml
@@ -385,6 +385,7 @@ csv 文件必須包含以逗號分隔的這些列:
標籤分配
每日分配
紀錄期間分配
+ 下一個熱門活動
diff --git a/core/src/main/res/values-zh/strings.xml b/core/src/main/res/values-zh/strings.xml
index 796bee2f..e258a492 100644
--- a/core/src/main/res/values-zh/strings.xml
+++ b/core/src/main/res/values-zh/strings.xml
@@ -385,6 +385,7 @@ csv 文件必须包含以逗号分隔的这些列:
标签分布
每日分发
记录持续时间分布
+ 下一个热门活动
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index d3b74792..b56cbc7d 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -385,6 +385,7 @@ Example:
Tag distribution
Daily distribution
Record duration distribution
+ Top next activities
- Records
diff --git a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailAdjacentActivitiesInteractor.kt b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailAdjacentActivitiesInteractor.kt
new file mode 100644
index 00000000..cc80537b
--- /dev/null
+++ b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailAdjacentActivitiesInteractor.kt
@@ -0,0 +1,104 @@
+package com.example.util.simpletimetracker.feature_statistics_detail.interactor
+
+import com.example.util.simpletimetracker.core.mapper.TimeMapper
+import com.example.util.simpletimetracker.domain.extension.getTypeIds
+import com.example.util.simpletimetracker.domain.extension.orZero
+import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor
+import com.example.util.simpletimetracker.domain.interactor.RecordInteractor
+import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor
+import com.example.util.simpletimetracker.domain.model.RangeLength
+import com.example.util.simpletimetracker.domain.model.RecordBase
+import com.example.util.simpletimetracker.domain.model.RecordType
+import com.example.util.simpletimetracker.domain.model.RecordsFilter
+import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
+import com.example.util.simpletimetracker.feature_statistics_detail.mapper.StatisticsDetailViewDataMapper
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+class StatisticsDetailAdjacentActivitiesInteractor @Inject constructor(
+ private val prefsInteractor: PrefsInteractor,
+ private val recordTypeInteractor: RecordTypeInteractor,
+ private val recordInteractor: RecordInteractor,
+ private val timeMapper: TimeMapper,
+ private val statisticsDetailViewDataMapper: StatisticsDetailViewDataMapper,
+) {
+
+ suspend fun getNextActivitiesViewData(
+ filter: List,
+ rangeLength: RangeLength,
+ rangePosition: Int,
+ ): List = withContext(Dispatchers.Default) {
+ // Show only if one activity is selected.
+ val typeId = filter.getTypeIds()
+ .takeIf { it.size == 1 }
+ ?.firstOrNull()
+ ?: return@withContext getEmptyViewData()
+
+ val isDarkTheme = prefsInteractor.getDarkMode()
+ val firstDayOfWeek = prefsInteractor.getFirstDayOfWeek()
+ val startOfDayShift = prefsInteractor.getStartOfDayShift()
+ val recordTypes = recordTypeInteractor.getAll().associateBy(RecordType::id)
+
+ val range = timeMapper.getRangeStartAndEnd(
+ rangeLength = rangeLength,
+ shift = rangePosition,
+ firstDayOfWeek = firstDayOfWeek,
+ startOfDayShift = startOfDayShift,
+ )
+ // Don't use records from viewModel because they are already filtered,
+ // we need all records here.
+ val actualRecords = if (range.timeStarted == 0L && range.timeEnded == 0L) {
+ recordInteractor.getAll()
+ } else {
+ recordInteractor.getFromRange(range)
+ }
+ val nextActivitiesIds = calculate(
+ typeId = typeId,
+ records = actualRecords,
+ )
+
+ return@withContext nextActivitiesIds.mapNotNull { type ->
+ statisticsDetailViewDataMapper.mapToPreview(
+ recordType = recordTypes[type] ?: return@mapNotNull null,
+ isDarkTheme = isDarkTheme,
+ isFirst = false,
+ isForComparison = false,
+ )
+ }
+ }
+
+ private fun calculate(
+ typeId: Long,
+ records: List,
+ ): List {
+ val counts = mutableMapOf()
+
+ val recordsSorted = records.sortedBy { it.timeStarted }
+ var currentRecord: RecordBase? = null
+ recordsSorted.forEach { record ->
+ val currentTimeEnded = currentRecord?.timeEnded
+ if (currentTimeEnded != null && record.timeStarted >= currentTimeEnded) {
+ record.typeIds.firstOrNull()?.let { id ->
+ counts[id] = counts[id].orZero() + 1
+ }
+ currentRecord = null
+ }
+ if (currentRecord == null && typeId in record.typeIds) {
+ currentRecord = record
+ }
+ }
+
+ return counts.keys
+ .sortedByDescending { counts[it].orZero() }
+ .take(MAX_COUNT)
+ }
+
+ private fun getEmptyViewData(): List {
+ return emptyList()
+ }
+
+ companion object {
+ private const val MAX_COUNT = 5
+ }
+}
\ No newline at end of file
diff --git a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/view/StatisticsDetailFragment.kt b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/view/StatisticsDetailFragment.kt
index 9c5e3b93..35bef290 100644
--- a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/view/StatisticsDetailFragment.kt
+++ b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/view/StatisticsDetailFragment.kt
@@ -61,6 +61,11 @@ class StatisticsDetailFragment :
createStatisticsPreviewAdapterDelegate()
)
}
+ private val nextActivitiesAdapter: BaseRecyclerAdapter by lazy {
+ BaseRecyclerAdapter(
+ createStatisticsPreviewAdapterDelegate()
+ )
+ }
private val dataSplitAdapter: BaseRecyclerAdapter by lazy {
BaseRecyclerAdapter(
createHintAdapterDelegate(),
@@ -87,6 +92,14 @@ class StatisticsDetailFragment :
}
adapter = previewAdapter
}
+ rvStatisticsDetailNextActivities.apply {
+ layoutManager = FlexboxLayoutManager(requireContext()).apply {
+ flexDirection = FlexDirection.ROW
+ justifyContent = JustifyContent.CENTER
+ flexWrap = FlexWrap.WRAP
+ }
+ adapter = nextActivitiesAdapter
+ }
rvStatisticsDetailSplit.apply {
layoutManager = LinearLayoutManager(context)
adapter = dataSplitAdapter
@@ -133,6 +146,7 @@ class StatisticsDetailFragment :
durationSplitChartViewData.observe(::updateDurationSplitChartViewData)
comparisonDurationSplitChartViewData.observe(::updateComparisonDurationSplitChartViewData)
splitChartGroupingViewData.observe(::updateSplitChartGroupingData)
+ nextActivitiesViewData.observe(::updateNextActivitiesViewData)
title.observe(binding.btnStatisticsDetailToday::setText)
rangeItems.observe(::updateRangeItems)
rangeButtonsVisibility.observe(::updateRangeButtonsVisibility)
@@ -294,6 +308,14 @@ class StatisticsDetailFragment :
binding.chartStatisticsDetailDurationSplitCompare.setViewData(viewData)
}
+ private fun updateNextActivitiesViewData(
+ viewData: List
+ ) {
+ binding.tvStatisticsDetailNextActivitiesHint.isVisible = viewData.isNotEmpty()
+ binding.rvStatisticsDetailNextActivities.isVisible = viewData.isNotEmpty()
+ nextActivitiesAdapter.replace(viewData)
+ }
+
private fun updateRangeItems(viewData: RangesViewData) = with(binding) {
spinnerStatisticsDetail.setData(viewData.items, viewData.selectedPosition)
}
diff --git a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewModel/StatisticsDetailViewModel.kt b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewModel/StatisticsDetailViewModel.kt
index dfb8734c..be900845 100644
--- a/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewModel/StatisticsDetailViewModel.kt
+++ b/features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewModel/StatisticsDetailViewModel.kt
@@ -25,6 +25,7 @@ import com.example.util.simpletimetracker.domain.model.RecordsFilter
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
import com.example.util.simpletimetracker.feature_statistics_detail.R
import com.example.util.simpletimetracker.feature_statistics_detail.customView.SeriesCalendarView
+import com.example.util.simpletimetracker.feature_statistics_detail.interactor.StatisticsDetailAdjacentActivitiesInteractor
import com.example.util.simpletimetracker.feature_statistics_detail.interactor.StatisticsDetailChartInteractor
import com.example.util.simpletimetracker.feature_statistics_detail.interactor.StatisticsDetailPreviewInteractor
import com.example.util.simpletimetracker.feature_statistics_detail.interactor.StatisticsDetailSplitChartInteractor
@@ -76,6 +77,7 @@ class StatisticsDetailViewModel @Inject constructor(
private val statsInteractor: StatisticsDetailStatsInteractor,
private val streaksInteractor: StatisticsDetailStreaksInteractor,
private val splitChartInteractor: StatisticsDetailSplitChartInteractor,
+ private val adjacentActivitiesInteractor: StatisticsDetailAdjacentActivitiesInteractor,
private val mapper: StatisticsDetailViewDataMapper,
private val rangeViewDataMapper: RangeViewDataMapper,
private val timeMapper: TimeMapper,
@@ -105,6 +107,9 @@ class StatisticsDetailViewModel @Inject constructor(
val splitChartGroupingViewData: LiveData> by lazy {
return@lazy MutableLiveData(loadSplitChartGroupingViewData())
}
+ val nextActivitiesViewData: LiveData> by lazy {
+ return@lazy MutableLiveData(emptyList())
+ }
val splitChartViewData: LiveData by lazy {
return@lazy MutableLiveData()
}
@@ -289,7 +294,7 @@ class StatisticsDetailViewModel @Inject constructor(
timeMapper.toTimestampShift(
toTime = timestamp,
range = rangeLength,
- firstDayOfWeek = prefsInteractor.getFirstDayOfWeek()
+ firstDayOfWeek = prefsInteractor.getFirstDayOfWeek(),
).toInt().let(::updatePosition)
}
}
@@ -337,7 +342,7 @@ class StatisticsDetailViewModel @Inject constructor(
val firstDayOfWeek = prefsInteractor.getFirstDayOfWeek()
val current = timeMapper.toTimestampShifted(
rangesFromToday = rangePosition,
- range = rangeLength
+ range = rangeLength,
)
router.navigate(
@@ -346,8 +351,8 @@ class StatisticsDetailViewModel @Inject constructor(
type = DateTimeDialogType.DATE,
timestamp = current,
useMilitaryTime = useMilitaryTime,
- firstDayOfWeek = firstDayOfWeek
- )
+ firstDayOfWeek = firstDayOfWeek,
+ ),
)
}
@@ -380,6 +385,7 @@ class StatisticsDetailViewModel @Inject constructor(
updateChartViewData()
updateSplitChartViewData()
updateDurationSplitChartViewData()
+ updateNextActivitiesViewData()
}
private fun getRangeLength(range: StatisticsDetailParams.RangeLengthParams): RangeLength {
@@ -390,7 +396,7 @@ class StatisticsDetailViewModel @Inject constructor(
is StatisticsDetailParams.RangeLengthParams.Year -> RangeLength.Year
is StatisticsDetailParams.RangeLengthParams.All -> RangeLength.All
is StatisticsDetailParams.RangeLengthParams.Custom -> Range(
- timeStarted = range.start, timeEnded = range.end
+ timeStarted = range.start, timeEnded = range.end,
).let(RangeLength::Custom)
is StatisticsDetailParams.RangeLengthParams.Last -> RangeLength.Last
}
@@ -414,7 +420,7 @@ class StatisticsDetailViewModel @Inject constructor(
filters = filters
.plus(dateFilter)
.map(RecordsFilter::toParams).toList(),
- )
+ ),
)
}
@@ -458,7 +464,7 @@ class StatisticsDetailViewModel @Inject constructor(
compareRecords = compareRecords,
showComparison = comparisonFilter.isNotEmpty(),
rangeLength = rangeLength,
- rangePosition = rangePosition
+ rangePosition = rangePosition,
)
}
@@ -529,7 +535,7 @@ class StatisticsDetailViewModel @Inject constructor(
isForComparison = isForComparison,
rangeLength = rangeLength,
rangePosition = rangePosition,
- splitChartGrouping = grouping
+ splitChartGrouping = grouping,
)
}
@@ -558,6 +564,18 @@ class StatisticsDetailViewModel @Inject constructor(
return mapper.mapToSplitChartGroupingViewData(rangeLength, splitChartGrouping)
}
+ private fun updateNextActivitiesViewData() = viewModelScope.launch {
+ nextActivitiesViewData.set(loadNextActivitiesViewData())
+ }
+
+ private suspend fun loadNextActivitiesViewData(): List {
+ return adjacentActivitiesInteractor.getNextActivitiesViewData(
+ filter = filter,
+ rangeLength = rangeLength,
+ rangePosition = rangePosition,
+ )
+ }
+
private fun updateTitle() = viewModelScope.launch {
title.set(loadTitle())
}
@@ -565,7 +583,12 @@ class StatisticsDetailViewModel @Inject constructor(
private suspend fun loadTitle(): String {
val startOfDayShift = prefsInteractor.getStartOfDayShift()
val firstDayOfWeek = prefsInteractor.getFirstDayOfWeek()
- return rangeViewDataMapper.mapToTitle(rangeLength, rangePosition, startOfDayShift, firstDayOfWeek)
+ return rangeViewDataMapper.mapToTitle(
+ rangeLength = rangeLength,
+ position = rangePosition,
+ startOfDayShift = startOfDayShift,
+ firstDayOfWeek = firstDayOfWeek,
+ )
}
private fun updateRanges() {
diff --git a/features/feature_statistics_detail/src/main/res/layout/statistics_detail_fragment.xml b/features/feature_statistics_detail/src/main/res/layout/statistics_detail_fragment.xml
index 6df7dd24..578ac4f3 100644
--- a/features/feature_statistics_detail/src/main/res/layout/statistics_detail_fragment.xml
+++ b/features/feature_statistics_detail/src/main/res/layout/statistics_detail_fragment.xml
@@ -387,14 +387,36 @@
tools:barColor="@color/black"
tools:barCount="7" />
+
+
+
+