A fork of Simple Time Tracker with WearOS support. (Now integrated upstream)
0

Configure Feed

Select the types of activity you want to include in your feed.

adding statistics detail comparison

razeeman (Apr 24, 2022, 4:20 PM +0300) 5bc5175b 1d558976

+184 -29
+2 -2
core/src/main/java/com/example/util/simpletimetracker/core/dialog/TypesFilterDialogListener.kt
··· 4 4 5 5 interface TypesFilterDialogListener { 6 6 7 - fun onTypesFilterSelected(filter: TypesFilterParams) 8 - fun onTypesFilterDismissed() 7 + fun onTypesFilterSelected(tag: String, filter: TypesFilterParams) 8 + fun onTypesFilterDismissed(tag: String) 9 9 }
+9
core/src/main/res/drawable/compare.xml
··· 1 + <vector xmlns:android="http://schemas.android.com/apk/res/android" 2 + android:width="24dp" 3 + android:height="24dp" 4 + android:viewportWidth="24" 5 + android:viewportHeight="24"> 6 + <path 7 + android:fillColor="#FF000000" 8 + android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/> 9 + </vector>
+6 -7
features/feature_dialogs/src/main/java/com/example/util/simpletimetracker/feature_dialogs/typesFilter/view/TypesFilterDialogFragment.kt
··· 24 24 import com.example.util.simpletimetracker.core.utils.fragmentArgumentDelegate 25 25 import com.example.util.simpletimetracker.feature_dialogs.typesFilter.viewModel.TypesFilterViewModel 26 26 import com.example.util.simpletimetracker.navigation.params.screen.TypesFilterDialogParams 27 - import com.example.util.simpletimetracker.navigation.params.screen.TypesFilterParams 28 27 import com.google.android.flexbox.FlexDirection 29 28 import com.google.android.flexbox.FlexWrap 30 29 import com.google.android.flexbox.FlexboxLayoutManager ··· 45 44 private val viewModel: TypesFilterViewModel by viewModels( 46 45 factoryProducer = { viewModelFactory } 47 46 ) 48 - private val params: TypesFilterParams by fragmentArgumentDelegate( 49 - key = ARGS_PARAMS, default = TypesFilterParams() 47 + private val params: TypesFilterDialogParams by fragmentArgumentDelegate( 48 + key = ARGS_PARAMS, default = TypesFilterDialogParams() 50 49 ) 51 50 52 51 private val adapter: BaseRecyclerAdapter by lazy { ··· 77 76 } 78 77 79 78 override fun onDismiss(dialog: DialogInterface) { 80 - typesFilterDialogListener?.onTypesFilterDismissed() 79 + typesFilterDialogListener?.onTypesFilterDismissed(params.tag) 81 80 super.onDismiss(dialog) 82 81 } 83 82 ··· 104 103 } 105 104 106 105 override fun initViewModel(): Unit = with(viewModel) { 107 - extra = params 106 + extra = params.filter 108 107 viewData.observe(adapter::replace) 109 - typesFilter.observe { typesFilterDialogListener?.onTypesFilterSelected(it) } 108 + typesFilter.observe { typesFilterDialogListener?.onTypesFilterSelected(params.tag, it) } 110 109 } 111 110 112 111 companion object { 113 112 private const val ARGS_PARAMS = "args_params" 114 113 115 114 fun createBundle(data: TypesFilterDialogParams): Bundle = Bundle().apply { 116 - putParcelable(ARGS_PARAMS, data.filter) 115 + putParcelable(ARGS_PARAMS, data) 117 116 } 118 117 } 119 118 }
+10
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/adapter/StatisticsDetailPreviewCompareAdapterDelegate.kt
··· 1 + package com.example.util.simpletimetracker.feature_statistics_detail.adapter 2 + 3 + import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate 4 + import com.example.util.simpletimetracker.feature_statistics_detail.databinding.StatisticsDetailPreviewCompareItemBinding as Binding 5 + import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailPreviewCompareViewData as ViewData 6 + 7 + fun createStatisticsPreviewCompareAdapterDelegate() = 8 + createRecyclerBindingAdapterDelegate<ViewData, Binding>( 9 + Binding::inflate 10 + ) { _, _, _ -> /* Nothing to bind */ }
+30 -6
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/interactor/StatisticsDetailPreviewInteractor.kt
··· 6 6 import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 7 7 import com.example.util.simpletimetracker.domain.model.ChartFilterType 8 8 import com.example.util.simpletimetracker.feature_statistics_detail.mapper.StatisticsDetailViewDataMapper 9 + import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailPreviewCompareViewData 9 10 import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailPreviewMoreViewData 10 11 import com.example.util.simpletimetracker.navigation.params.screen.TypesFilterParams 11 12 import javax.inject.Inject ··· 14 15 private val prefsInteractor: PrefsInteractor, 15 16 private val recordTypeInteractor: RecordTypeInteractor, 16 17 private val categoryInteractor: CategoryInteractor, 17 - private val statisticsDetailViewDataMapper: StatisticsDetailViewDataMapper 18 + private val statisticsDetailViewDataMapper: StatisticsDetailViewDataMapper, 18 19 ) { 19 20 20 21 suspend fun getPreviewData( 21 - filterParams: TypesFilterParams 22 + filterParams: TypesFilterParams, 23 + isForComparison: Boolean, 22 24 ): List<ViewHolderType> { 23 25 val selectedIds = filterParams.selectedIds 24 26 val filter = filterParams.filterType ··· 32 34 statisticsDetailViewDataMapper.mapToPreview( 33 35 recordType = type, 34 36 isDarkTheme = isDarkTheme, 35 - isFirst = index == 0 37 + isFirst = index == 0, 38 + isForComparison = isForComparison, 36 39 ) 37 40 } 38 41 } ··· 42 45 .map { category -> 43 46 statisticsDetailViewDataMapper.mapToPreview( 44 47 category = category, 45 - isDarkTheme = isDarkTheme 48 + isDarkTheme = isDarkTheme, 49 + isForComparison = isForComparison, 46 50 ) 47 51 } 48 52 } 49 53 } 50 54 55 + return if (isForComparison) { 56 + buildComparisonViewData(viewData) 57 + } else { 58 + buildFilterViewData(viewData, isDarkTheme) 59 + } 60 + } 61 + 62 + private fun buildFilterViewData( 63 + viewData: List<ViewHolderType>, 64 + isDarkTheme: Boolean, 65 + ): List<ViewHolderType> { 51 66 return when { 52 67 viewData.isEmpty() -> { 53 68 statisticsDetailViewDataMapper ··· 58 73 viewData 59 74 } 60 75 else -> { 61 - viewData 62 - .first().let(::listOf) + 76 + viewData.first().let(::listOf) + 63 77 StatisticsDetailPreviewMoreViewData + 64 78 viewData.drop(1) 65 79 } 80 + } 81 + } 82 + 83 + private fun buildComparisonViewData( 84 + viewData: List<ViewHolderType>, 85 + ): List<ViewHolderType> { 86 + return if (viewData.isEmpty()) { 87 + viewData 88 + } else { 89 + StatisticsDetailPreviewCompareViewData.let(::listOf) + viewData 66 90 } 67 91 } 68 92 }
+13
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/mapper/StatisticsDetailViewDataMapper.kt
··· 120 120 recordType: RecordType, 121 121 isDarkTheme: Boolean, 122 122 isFirst: Boolean, 123 + isForComparison: Boolean, 123 124 ): StatisticsDetailPreviewViewData { 124 125 return StatisticsDetailPreviewViewData( 125 126 id = recordType.id, 127 + type = if (isForComparison) { 128 + StatisticsDetailPreviewViewData.Type.COMPARISON 129 + } else { 130 + StatisticsDetailPreviewViewData.Type.FILTER 131 + }, 126 132 name = recordType.name.takeIf { isFirst }.orEmpty(), 127 133 iconId = recordType.icon 128 134 .let(iconMapper::mapIcon), ··· 134 140 fun mapToPreview( 135 141 category: Category, 136 142 isDarkTheme: Boolean, 143 + isForComparison: Boolean, 137 144 ): StatisticsDetailPreviewViewData { 138 145 return StatisticsDetailPreviewViewData( 139 146 id = category.id, 147 + type = if (isForComparison) { 148 + StatisticsDetailPreviewViewData.Type.COMPARISON 149 + } else { 150 + StatisticsDetailPreviewViewData.Type.FILTER 151 + }, 140 152 name = category.name, 141 153 iconId = null, 142 154 color = category.color ··· 149 161 ): StatisticsDetailPreviewViewData { 150 162 return StatisticsDetailPreviewViewData( 151 163 id = 0, 164 + type = StatisticsDetailPreviewViewData.Type.FILTER, 152 165 name = "", 153 166 iconId = RecordTypeIcon.Image(R.drawable.unknown), 154 167 color = colorMapper.toUntrackedColor(isDarkTheme)
+8 -3
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/view/StatisticsDetailFragment.kt
··· 20 20 import com.example.util.simpletimetracker.feature_base_adapter.hint.createHintAdapterDelegate 21 21 import com.example.util.simpletimetracker.feature_base_adapter.statisticsTag.createStatisticsTagAdapterDelegate 22 22 import com.example.util.simpletimetracker.feature_statistics_detail.adapter.createStatisticsPreviewAdapterDelegate 23 + import com.example.util.simpletimetracker.feature_statistics_detail.adapter.createStatisticsPreviewCompareAdapterDelegate 23 24 import com.example.util.simpletimetracker.feature_statistics_detail.adapter.createStatisticsPreviewMoreAdapterDelegate 24 25 import com.example.util.simpletimetracker.feature_statistics_detail.customView.BarChartView 25 26 import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailChartCompositeViewData ··· 59 60 private val previewAdapter: BaseRecyclerAdapter by lazy { 60 61 BaseRecyclerAdapter( 61 62 createStatisticsPreviewMoreAdapterDelegate(), 63 + createStatisticsPreviewCompareAdapterDelegate(), 62 64 createStatisticsPreviewAdapterDelegate() 63 65 ) 64 66 } ··· 96 98 97 99 override fun initUx() = with(binding) { 98 100 cardStatisticsDetailFilter.setOnClick(viewModel::onFilterClick) 101 + cardStatisticsDetailCompare.setOnClick(viewModel::onCompareClick) 99 102 buttonsStatisticsDetailGrouping.listener = viewModel::onChartGroupingClick 100 103 buttonsStatisticsDetailLength.listener = viewModel::onChartLengthClick 101 104 buttonsStatisticsDetailSplitGrouping.listener = viewModel::onSplitChartGroupingClick ··· 134 137 viewModel.onVisible() 135 138 } 136 139 137 - override fun onTypesFilterSelected(filter: TypesFilterParams) { 138 - viewModel.onTypesFilterSelected(filter) 140 + override fun onTypesFilterSelected(tag: String, filter: TypesFilterParams) { 141 + viewModel.onTypesFilterSelected(tag, filter) 139 142 } 140 143 141 - override fun onTypesFilterDismissed() { 144 + override fun onTypesFilterDismissed(tag: String) { 142 145 viewModel.onTypesFilterDismissed() 143 146 } 144 147 145 148 private fun setPreview() = params.preview?.run { 146 149 StatisticsDetailPreviewViewData( 147 150 id = 0L, 151 + type = StatisticsDetailPreviewViewData.Type.FILTER, 148 152 name = name, 149 153 iconId = iconId?.toViewData(), 150 154 color = color ··· 152 156 } 153 157 154 158 private fun setPreviewViewData(viewData: List<ViewHolderType>) = with(binding) { 159 + // TODO create data class holder 155 160 val first = viewData.firstOrNull() 156 161 as? StatisticsDetailPreviewViewData 157 162 ?: return
+12
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewData/StatisticsDetailPreviewCompareViewData.kt
··· 1 + package com.example.util.simpletimetracker.feature_statistics_detail.viewData 2 + 3 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 4 + 5 + object StatisticsDetailPreviewCompareViewData : ViewHolderType { 6 + 7 + // Only one item in recycler 8 + override fun getUniqueId(): Long = 1L 9 + 10 + override fun isValidType(other: ViewHolderType): Boolean = 11 + other is StatisticsDetailPreviewCompareViewData 12 + }
+6 -1
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewData/StatisticsDetailPreviewViewData.kt
··· 6 6 7 7 data class StatisticsDetailPreviewViewData( 8 8 val id: Long, 9 + val type: Type, 9 10 val name: String, 10 11 val iconId: RecordTypeIcon? = null, 11 12 @ColorInt val color: Int ··· 14 15 override fun getUniqueId(): Long = id 15 16 16 17 override fun isValidType(other: ViewHolderType): Boolean = 17 - other is StatisticsDetailPreviewViewData 18 + other is StatisticsDetailPreviewViewData && other.type == type 19 + 20 + enum class Type { 21 + FILTER, COMPARISON 22 + } 18 23 }
+25 -5
features/feature_statistics_detail/src/main/java/com/example/util/simpletimetracker/feature_statistics_detail/viewModel/StatisticsDetailViewModel.kt
··· 99 99 private val typesFilterContainer: MutableList<TypesFilterParams> by lazy { 100 100 mutableListOf(extra.filter) 101 101 } 102 + private var comparisonTypesFilter: TypesFilterParams = TypesFilterParams() 102 103 103 104 fun onVisible() { 104 105 updateViewData() 105 106 } 106 107 107 108 fun onFilterClick() { 108 - router.navigate(TypesFilterDialogParams(typesFilter)) 109 + router.navigate(TypesFilterDialogParams(FILTER_TAG, typesFilter)) 109 110 } 110 111 111 - fun onTypesFilterSelected(newFilter: TypesFilterParams) { 112 - typesFilterContainer.clear() 113 - typesFilterContainer.add(newFilter) 112 + fun onCompareClick() { 113 + router.navigate(TypesFilterDialogParams(COMPARE_TAG, comparisonTypesFilter)) 114 + } 115 + 116 + fun onTypesFilterSelected(tag: String, newFilter: TypesFilterParams) { 117 + when (tag) { 118 + FILTER_TAG -> { 119 + typesFilterContainer.clear() 120 + typesFilterContainer.add(newFilter) 121 + } 122 + COMPARE_TAG -> { 123 + comparisonTypesFilter = newFilter 124 + } 125 + } 114 126 } 115 127 116 128 fun onTypesFilterDismissed() { ··· 258 270 } 259 271 260 272 private suspend fun loadPreviewViewData(): List<ViewHolderType> { 261 - return previewInteractor.getPreviewData(typesFilter) 273 + return previewInteractor.getPreviewData( 274 + filterParams = typesFilter, 275 + isForComparison = false, 276 + ) + previewInteractor.getPreviewData( 277 + filterParams = comparisonTypesFilter, 278 + isForComparison = true, 279 + ) 262 280 } 263 281 264 282 private fun updateStatsViewData() = viewModelScope.launch { ··· 369 387 370 388 companion object { 371 389 private const val DATE_TAG = "statistics_detail_date_tag" 390 + private const val FILTER_TAG = "statistics_detail_filter_tag" 391 + private const val COMPARE_TAG = "statistics_detail_compare_tag" 372 392 } 373 393 }
+22 -1
features/feature_statistics_detail/src/main/res/layout/statistics_detail_fragment.xml
··· 27 27 app:cardElevation="@dimen/record_type_card_elevation" 28 28 app:itemDuration="" 29 29 app:itemPercent="" 30 - app:layout_constraintEnd_toStartOf="@id/cardStatisticsDetailFilter" 30 + app:layout_constraintEnd_toStartOf="@id/cardStatisticsDetailCompare" 31 31 app:layout_constraintStart_toStartOf="parent" 32 32 app:layout_constraintTop_toTopOf="parent" 33 33 tools:itemColor="@color/red_800" /> 34 + 35 + <androidx.cardview.widget.CardView 36 + android:id="@+id/cardStatisticsDetailCompare" 37 + style="@style/SettingsCard" 38 + android:layout_width="44dp" 39 + android:layout_height="44dp" 40 + android:foreground="?selectableItemBackground" 41 + app:cardBackgroundColor="?appCardBackgroundColor" 42 + app:layout_constraintBottom_toBottomOf="@id/viewStatisticsDetailItem" 43 + app:layout_constraintDimensionRatio="h,1:1" 44 + app:layout_constraintEnd_toStartOf="@id/cardStatisticsDetailFilter" 45 + app:layout_constraintTop_toTopOf="@id/viewStatisticsDetailItem"> 46 + 47 + <androidx.appcompat.widget.AppCompatImageView 48 + android:layout_width="match_parent" 49 + android:layout_height="match_parent" 50 + android:scaleType="center" 51 + android:src="@drawable/compare" 52 + android:tint="?appInactiveColor" /> 53 + 54 + </androidx.cardview.widget.CardView> 34 55 35 56 <androidx.cardview.widget.CardView 36 57 android:id="@+id/cardStatisticsDetailFilter"
+34
features/feature_statistics_detail/src/main/res/layout/statistics_detail_preview_compare_item.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" 3 + xmlns:app="http://schemas.android.com/apk/res-auto" 4 + android:layout_width="wrap_content" 5 + android:layout_height="wrap_content" 6 + app:cardBackgroundColor="?appBackgroundColor" 7 + app:cardCornerRadius="8dp" 8 + app:cardElevation="4dp" 9 + app:cardPreventCornerOverlap="false" 10 + app:cardUseCompatPadding="true" 11 + app:layout_constraintEnd_toStartOf="@id/cardStatisticsDetailFilter" 12 + app:layout_constraintStart_toStartOf="parent" 13 + app:layout_constraintTop_toTopOf="parent"> 14 + 15 + <androidx.constraintlayout.widget.ConstraintLayout 16 + android:layout_width="wrap_content" 17 + android:layout_height="wrap_content" 18 + android:paddingHorizontal="8dp" 19 + android:paddingVertical="8dp"> 20 + 21 + <com.example.util.simpletimetracker.feature_views.IconView 22 + android:layout_width="16dp" 23 + android:layout_height="0dp" 24 + android:layout_marginEnd="8dp" 25 + app:itemIcon="@drawable/compare" 26 + app:itemIconColor="?appInactiveColor" 27 + app:layout_constraintBottom_toBottomOf="parent" 28 + app:layout_constraintDimensionRatio="h,1:1" 29 + app:layout_constraintStart_toStartOf="parent" 30 + app:layout_constraintTop_toTopOf="parent" /> 31 + 32 + </androidx.constraintlayout.widget.ConstraintLayout> 33 + 34 + </androidx.cardview.widget.CardView>
-2
features/feature_statistics_detail/src/main/res/layout/statistics_detail_preview_more_item.xml
··· 1 1 <?xml version="1.0" encoding="utf-8"?> 2 2 <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" 3 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 - android:id="@+id/layoutStatisticsDetailPreviewItem" 5 4 android:layout_width="wrap_content" 6 5 android:layout_height="wrap_content" 7 6 app:cardBackgroundColor="?appBackgroundColor" ··· 20 19 android:paddingVertical="8dp"> 21 20 22 21 <com.example.util.simpletimetracker.feature_views.IconView 23 - android:id="@+id/ivStatisticsDetailPreviewItemIcon" 24 22 android:layout_width="16dp" 25 23 android:layout_height="0dp" 26 24 android:layout_marginEnd="8dp"
+7 -2
navigation/src/main/java/com/example/util/simpletimetracker/navigation/params/screen/TypesFilterDialogParams.kt
··· 1 1 package com.example.util.simpletimetracker.navigation.params.screen 2 2 3 + import android.os.Parcelable 4 + import kotlinx.parcelize.Parcelize 5 + 6 + @Parcelize 3 7 data class TypesFilterDialogParams( 4 - val filter: TypesFilterParams 5 - ) : ScreenParams 8 + val tag: String = "", 9 + val filter: TypesFilterParams = TypesFilterParams() 10 + ) : ScreenParams, Parcelable