From 9b7f5260072df273823b5078cce424356d34464b Mon Sep 17 00:00:00 2001 From: razeeman Date: Sat, 3 Feb 2024 14:50:17 +0300 Subject: [PATCH] add top next activities to statistics detail --- core/src/main/res/values-ar/strings.xml | 1 + core/src/main/res/values-ca/strings.xml | 1 + core/src/main/res/values-de/strings.xml | 1 + core/src/main/res/values-es/strings.xml | 1 + core/src/main/res/values-fa/strings.xml | 1 + core/src/main/res/values-fr/strings.xml | 1 + core/src/main/res/values-hi/strings.xml | 1 + core/src/main/res/values-in/strings.xml | 1 + core/src/main/res/values-it/strings.xml | 1 + core/src/main/res/values-ja/strings.xml | 1 + core/src/main/res/values-ko/strings.xml | 1 + core/src/main/res/values-nl/strings.xml | 1 + core/src/main/res/values-pt/strings.xml | 1 + core/src/main/res/values-ru/strings.xml | 1 + core/src/main/res/values-sv/strings.xml | 1 + core/src/main/res/values-tr/strings.xml | 1 + core/src/main/res/values-uk/strings.xml | 1 + core/src/main/res/values-vi/strings.xml | 1 + core/src/main/res/values-zh-rTW/strings.xml | 1 + core/src/main/res/values-zh/strings.xml | 1 + core/src/main/res/values/strings.xml | 1 + ...sticsDetailAdjacentActivitiesInteractor.kt | 104 ++++++++++++++++++ .../view/StatisticsDetailFragment.kt | 22 ++++ .../viewModel/StatisticsDetailViewModel.kt | 41 +++++-- .../res/layout/statistics_detail_fragment.xml | 26 ++++- 25 files changed, 203 insertions(+), 11 deletions(-) create mode 100644 features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailAdjacentActivitiesInteractor.kt 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" /> + + + + -- 2.51.2