diff --git a/app/src/androidTest/java/com/example/util/simpletimetracker/StatisticsDetailTest.kt b/app/src/androidTest/java/com/example/util/simpletimetracker/StatisticsDetailTest.kt index 973f8545..0c394314 100644 --- a/app/src/androidTest/java/com/example/util/simpletimetracker/StatisticsDetailTest.kt +++ b/app/src/androidTest/java/com/example/util/simpletimetracker/StatisticsDetailTest.kt @@ -8,7 +8,6 @@ import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withSubstring import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.example.util.simpletimetracker.utils.BaseUiTest diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml index 38645752..5da8e38b 100644 --- a/core/src/main/res/values-ca/strings.xml +++ b/core/src/main/res/values-ca/strings.xml @@ -396,6 +396,7 @@ Si la configuració \"%s\" és habilitada, l\'aplicació enviarà esdeven Sense comentaris Qualsevol comentari Seleccioneu + Inverteix la selecció Emoticones i Emocions diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml index ff83f68f..8ad56a52 100644 --- a/core/src/main/res/values-de/strings.xml +++ b/core/src/main/res/values-de/strings.xml @@ -396,6 +396,7 @@ Wenn die Einstellung \"%s\" aktiviert ist, sendet die App Ereignisse zu g Kein Kommentar Jeglicher Kommentar Wählen + Auswahl umkehren Lächeln & Stimmung diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml index 1bb5debb..d67bb5f0 100644 --- a/core/src/main/res/values-es/strings.xml +++ b/core/src/main/res/values-es/strings.xml @@ -396,6 +396,7 @@ Si la configuración \"%s\" está habilitada, la aplicación enviará eve Sin comentarios Cualquier comentario Seleccionar + Invertir selección Emoticonos y Emociones diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml index 500a243d..f0f614db 100644 --- a/core/src/main/res/values-fr/strings.xml +++ b/core/src/main/res/values-fr/strings.xml @@ -396,6 +396,7 @@ Si le paramètre \"%s\" est activé, l\'application enverra des événeme Aucun commentaire Avez-vous des commentaires Sélectionner + Inverser la sélection Smileys & Émotion diff --git a/core/src/main/res/values-hi/strings.xml b/core/src/main/res/values-hi/strings.xml index 44e836aa..e3888932 100644 --- a/core/src/main/res/values-hi/strings.xml +++ b/core/src/main/res/values-hi/strings.xml @@ -396,6 +396,7 @@ कोई टिप्पणी नहीं कोई टिप्पणी चुनना + उलट चयन स्माइली और इमोशन diff --git a/core/src/main/res/values-in/strings.xml b/core/src/main/res/values-in/strings.xml index 79ce0a2d..5f56bbd6 100644 --- a/core/src/main/res/values-in/strings.xml +++ b/core/src/main/res/values-in/strings.xml @@ -396,6 +396,7 @@ Jika setelan \"%s\" diaktifkan, aplikasi akan mengirimkan peristiwa pada Tidak ada komentar Ada komentar Pilih + Pilihan sebaliknya Senyuman & Emosi diff --git a/core/src/main/res/values-it/strings.xml b/core/src/main/res/values-it/strings.xml index 2969315a..b0e7a538 100644 --- a/core/src/main/res/values-it/strings.xml +++ b/core/src/main/res/values-it/strings.xml @@ -396,6 +396,7 @@ Se l\'impostazione \"%s\" è abilitata, l\'app invierà eventi sull\'atti No comment Qualsiasi commento Selezionare + Inverti selezione Faccine & Emozioni diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml index ce3a70a0..94e9e41d 100644 --- a/core/src/main/res/values-ja/strings.xml +++ b/core/src/main/res/values-ja/strings.xml @@ -396,6 +396,7 @@ コメント無し 任意のコメント 選択する + 選択範囲を反転する 顔文字とエモート diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml index 59a6291d..48e45601 100644 --- a/core/src/main/res/values-nl/strings.xml +++ b/core/src/main/res/values-nl/strings.xml @@ -396,6 +396,7 @@ Als de instelling \"%s\" is ingeschakeld, stuurt de app gebeurtenissen ov Geen commentaar Elke opmerking Selecteer + Omgekeerde selectie Smileys & Emotie diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml index b7e709f9..a94c9e8f 100644 --- a/core/src/main/res/values-pt/strings.xml +++ b/core/src/main/res/values-pt/strings.xml @@ -392,6 +392,7 @@ Se a configuração \"%s\" estiver habilitada, o aplicativo enviará even Sem comentários Qualquer comentário Selecione + Seleção invertida Smileys & Emoções diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml index e91818de..919be3f4 100644 --- a/core/src/main/res/values-ru/strings.xml +++ b/core/src/main/res/values-ru/strings.xml @@ -396,6 +396,7 @@ Без комментариев Любой комментарий Выбрать + Инвертировать выбор Смайлы и эмоции diff --git a/core/src/main/res/values-sv/strings.xml b/core/src/main/res/values-sv/strings.xml index 8c871489..811aca3a 100644 --- a/core/src/main/res/values-sv/strings.xml +++ b/core/src/main/res/values-sv/strings.xml @@ -396,6 +396,7 @@ Om inställningen \"%s\" är aktiverad, kommer appen att skicka händelse Ingen kommentar Någon kommentar Välj + Invertera urval Ansiktsuttryck & Känslor diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml index 808dfc31..c7a45c0a 100644 --- a/core/src/main/res/values-tr/strings.xml +++ b/core/src/main/res/values-tr/strings.xml @@ -396,6 +396,7 @@ Ek olarak:
Yorum yok Herhangi bir yorum Seçme + Zıt seçim İfadeler diff --git a/core/src/main/res/values-uk/strings.xml b/core/src/main/res/values-uk/strings.xml index 0475735c..e613df3a 100644 --- a/core/src/main/res/values-uk/strings.xml +++ b/core/src/main/res/values-uk/strings.xml @@ -396,6 +396,7 @@ Без коментарів Будь-який коментар Виберіть + Інвертувати виділення Смайли та емоції diff --git a/core/src/main/res/values-zh-rTW/strings.xml b/core/src/main/res/values-zh-rTW/strings.xml index c3c3d13d..465d6a4a 100644 --- a/core/src/main/res/values-zh-rTW/strings.xml +++ b/core/src/main/res/values-zh-rTW/strings.xml @@ -396,6 +396,7 @@ 沒有意見 任何意見 選擇 + 反轉選擇 表情 diff --git a/core/src/main/res/values-zh/strings.xml b/core/src/main/res/values-zh/strings.xml index e9b388f5..1024e434 100644 --- a/core/src/main/res/values-zh/strings.xml +++ b/core/src/main/res/values-zh/strings.xml @@ -396,6 +396,7 @@ 没有意见 任何意见 选择 + 反转选择 微笑与情感 diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index b5a4cbc2..11528bd6 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -396,6 +396,7 @@ If \"%s\" setting is enabled, app will send events on activity started or No comment Any comment Select + Invert selection Smileys & Emotion diff --git a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/customView/RecordsCalendarView.kt b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/customView/RecordsCalendarView.kt index 565a35c4..a3c803d4 100644 --- a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/customView/RecordsCalendarView.kt +++ b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/customView/RecordsCalendarView.kt @@ -477,7 +477,7 @@ class RecordsCalendarView @JvmOverloads constructor( canvas.restore() } - if (iconDrawn) { + if (iconDrawn) { availableHeight = (availableHeight - iconMaxSize - recordVerticalPadding) .coerceAtLeast(0f) } diff --git a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt index cea91a80..ff59c73a 100644 --- a/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt +++ b/features/feature_records/src/main/java/com/example/util/simpletimetracker/feature_records/interactor/RecordsViewDataInteractor.kt @@ -71,7 +71,7 @@ class RecordsViewDataInteractor @Inject constructor( ) val records = recordInteractor.getFromRange(rangeStart, rangeEnd) - val data = getRecordsViewData( + val data = getRecordsViewData( records = records, runningRecords = runningRecords, recordTypes = recordTypes, diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonAdapterDelegate.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonAdapterDelegate.kt new file mode 100644 index 00000000..c4c88d33 --- /dev/null +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonAdapterDelegate.kt @@ -0,0 +1,20 @@ +package com.example.util.simpletimetracker.feature_records_filter.adapter + +import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate +import com.example.util.simpletimetracker.feature_views.extension.setOnClickWith +import com.example.util.simpletimetracker.feature_records_filter.adapter.RecordsFilterButtonViewData as ViewData +import com.example.util.simpletimetracker.feature_records_filter.databinding.ItemRecordsFilterButtonBinding as Binding + +fun createRecordsFilterButtonAdapterDelegate( + onClick: (ViewData) -> Unit, +) = createRecyclerBindingAdapterDelegate( + Binding::inflate +) { binding, item, _ -> + + with(binding) { + item as ViewData + + btnRecordsFilterButtonItem.text = item.text + btnRecordsFilterButtonItem.setOnClickWith(item, onClick) + } +} diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonViewData.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonViewData.kt new file mode 100644 index 00000000..5604a966 --- /dev/null +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/adapter/RecordsFilterButtonViewData.kt @@ -0,0 +1,18 @@ +package com.example.util.simpletimetracker.feature_records_filter.adapter + +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType + +data class RecordsFilterButtonViewData( + val type: Type, + val text: String, +) : ViewHolderType { + + override fun getUniqueId(): Long = type.ordinal.toLong() + + override fun isValidType(other: ViewHolderType): Boolean = + other is RecordsFilterButtonViewData + + enum class Type { + INVERT_SELECTION, + } +} \ No newline at end of file diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt index 3d34cc7f..c1516759 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/interactor/RecordsFilterViewDataInteractor.kt @@ -38,6 +38,7 @@ import com.example.util.simpletimetracker.feature_base_adapter.hint.HintViewData import com.example.util.simpletimetracker.feature_base_adapter.record.RecordViewData import com.example.util.simpletimetracker.feature_base_adapter.recordFilter.RecordFilterViewData import com.example.util.simpletimetracker.feature_records_filter.R +import com.example.util.simpletimetracker.feature_records_filter.adapter.RecordsFilterButtonViewData import com.example.util.simpletimetracker.feature_records_filter.adapter.RecordsFilterCommentViewData import com.example.util.simpletimetracker.feature_records_filter.adapter.RecordsFilterRangeViewData import com.example.util.simpletimetracker.feature_records_filter.mapper.RecordsFilterViewDataMapper @@ -393,8 +394,12 @@ class RecordsFilterViewDataInteractor @Inject constructor( val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() val useProportionalMinutes = prefsInteractor.getUseProportionalMinutes() val showSeconds = prefsInteractor.getShowSeconds() + val button = RecordsFilterButtonViewData( + type = RecordsFilterButtonViewData.Type.INVERT_SELECTION, + text = resourceRepo.getString(R.string.records_filter_invert_selection), + ) - return filters + return button.let(::listOf) + filters .getManuallyFilteredRecordIds() .mapNotNull { recordInteractor.get(it) } // TODO do better .mapNotNull { record -> diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/model/RecordsFilterSelectionState.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/model/RecordsFilterSelectionState.kt index 4ecb2094..20b2846f 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/model/RecordsFilterSelectionState.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/model/RecordsFilterSelectionState.kt @@ -7,4 +7,7 @@ sealed interface RecordsFilterSelectionState { object Hidden : RecordsFilterSelectionState data class Visible(val type: RecordFilterViewData.Type) : RecordsFilterSelectionState -} \ No newline at end of file +} + +val RecordsFilterSelectionState.type: RecordFilterViewData.Type? + get() = (this as? RecordsFilterSelectionState.Visible)?.type \ No newline at end of file diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/view/RecordsFilterFragment.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/view/RecordsFilterFragment.kt index db1801ad..f8c03e15 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/view/RecordsFilterFragment.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/view/RecordsFilterFragment.kt @@ -28,6 +28,7 @@ import com.example.util.simpletimetracker.feature_base_adapter.record.createReco import com.example.util.simpletimetracker.feature_base_adapter.recordFilter.createRecordFilterAdapterDelegate import com.example.util.simpletimetracker.feature_base_adapter.recordType.createRecordTypeAdapterDelegate import com.example.util.simpletimetracker.feature_base_adapter.recordsDateDivider.createRecordsDateDividerAdapterDelegate +import com.example.util.simpletimetracker.feature_records_filter.adapter.createRecordsFilterButtonAdapterDelegate import com.example.util.simpletimetracker.feature_records_filter.adapter.createRecordsFilterCommentAdapterDelegate import com.example.util.simpletimetracker.feature_records_filter.adapter.createRecordsFilterRangeAdapterDelegate import com.example.util.simpletimetracker.feature_records_filter.model.RecordsFilterSelectedRecordsViewData @@ -69,6 +70,7 @@ class RecordsFilterFragment : createCategoryAdapterDelegate(viewModel::onCategoryClick), createRecordAdapterDelegate(viewModel::onRecordClick), createRecordsFilterCommentAdapterDelegate(viewModel::onCommentChange), + createRecordsFilterButtonAdapterDelegate(viewModel::onInnerFilterButtonClick), createRecordsFilterRangeAdapterDelegate( viewModel::onRangeTimeStartedClick, viewModel::onRangeTimeEndedClick, diff --git a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt index 0d82cd20..f5f78ad9 100644 --- a/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt +++ b/features/feature_records_filter/src/main/java/com/example/util/simpletimetracker/feature_records_filter/viewModel/RecordsFilterViewModel.kt @@ -18,6 +18,7 @@ import com.example.util.simpletimetracker.domain.extension.getSelectedTags import com.example.util.simpletimetracker.domain.extension.getTypeIds import com.example.util.simpletimetracker.domain.extension.getTypeIdsFromCategories import com.example.util.simpletimetracker.domain.extension.hasCategoryFilter +import com.example.util.simpletimetracker.domain.extension.hasManuallyFiltered import com.example.util.simpletimetracker.domain.interactor.CategoryInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor @@ -34,10 +35,12 @@ import com.example.util.simpletimetracker.feature_base_adapter.loader.LoaderView import com.example.util.simpletimetracker.feature_base_adapter.record.RecordViewData import com.example.util.simpletimetracker.feature_base_adapter.recordFilter.RecordFilterViewData import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData +import com.example.util.simpletimetracker.feature_records_filter.adapter.RecordsFilterButtonViewData import com.example.util.simpletimetracker.feature_records_filter.interactor.RecordsFilterViewDataInteractor import com.example.util.simpletimetracker.feature_records_filter.mapper.RecordsFilterViewDataMapper import com.example.util.simpletimetracker.feature_records_filter.model.RecordsFilterSelectedRecordsViewData import com.example.util.simpletimetracker.feature_records_filter.model.RecordsFilterSelectionState +import com.example.util.simpletimetracker.feature_records_filter.model.type import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.navigation.params.screen.RecordsFilterParam import com.example.util.simpletimetracker.navigation.params.screen.RecordsFilterParams @@ -243,6 +246,14 @@ class RecordsFilterViewModel @Inject constructor( updateRecords() } + fun onInnerFilterButtonClick(viewData: RecordsFilterButtonViewData) { + when (viewData.type) { + RecordsFilterButtonViewData.Type.INVERT_SELECTION -> { + handleInvertSelection() + } + } + } + private suspend fun handleTypeClick(id: Long) { val currentIds = filters.getTypeIds().toMutableList() val currentIdsFromCategories = filters.getTypeIdsFromCategories( @@ -311,8 +322,7 @@ class RecordsFilterViewModel @Inject constructor( } private fun handleTagClick(item: CategoryViewData.Record) { - val currentState = (filterSelectionState as? RecordsFilterSelectionState.Visible) - ?.type ?: return + val currentState = filterSelectionState.type ?: return val currentTags = when (currentState) { RecordFilterViewData.Type.SELECTED_TAGS -> filters.getSelectedTags() @@ -344,6 +354,7 @@ class RecordsFilterViewModel @Inject constructor( .apply { addOrRemove(id) } filters.removeAll { it is RecordsFilter.ManuallyFiltered } if (newIds.isNotEmpty()) filters.add(RecordsFilter.ManuallyFiltered(newIds)) + checkManualFilterVisibility() } private suspend fun checkTagFilterConsistency() { @@ -388,12 +399,42 @@ class RecordsFilterViewModel @Inject constructor( if (currentSelectionType == RecordFilterViewData.Type.CATEGORY) { filterSelectionState = RecordsFilterSelectionState.Visible(RecordFilterViewData.Type.ACTIVITY) } + checkManualFilterVisibility() + + updateFilters() + updateFilterSelectionViewData() + updateRecords() + } + + private fun handleInvertSelection() { + val filteredIds = filters.getManuallyFilteredRecordIds() + .toMutableList() + val selectedIds = recordsViewData.value + ?.recordsViewData + .orEmpty() + .filterIsInstance() + .filter { it.id !in filteredIds } + .map { it.id } + + filters.removeAll { it is RecordsFilter.ManuallyFiltered } + if (selectedIds.isNotEmpty()) filters.add(RecordsFilter.ManuallyFiltered(selectedIds)) + checkManualFilterVisibility() updateFilters() updateFilterSelectionViewData() updateRecords() } + private fun checkManualFilterVisibility() { + if ( + !filters.hasManuallyFiltered() && + filterSelectionState.type == RecordFilterViewData.Type.MANUALLY_FILTERED + ) { + filterSelectionState = RecordsFilterSelectionState.Hidden + updateFilterSelectionVisibility() + } + } + private suspend fun getTypesCache(): List { return types.takeUnless { it.isEmpty() } ?: run { recordTypeInteractor.getAll().also { types = it } } @@ -469,8 +510,7 @@ class RecordsFilterViewModel @Inject constructor( } private suspend fun loadFilterSelectionViewData(): List { - val type = (filterSelectionState as? RecordsFilterSelectionState.Visible) - ?.type ?: return emptyList() + val type = filterSelectionState.type ?: return emptyList() return when (type) { RecordFilterViewData.Type.ACTIVITY, diff --git a/features/feature_records_filter/src/main/res/layout/item_records_filter_button.xml b/features/feature_records_filter/src/main/res/layout/item_records_filter_button.xml new file mode 100644 index 00000000..f066689a --- /dev/null +++ b/features/feature_records_filter/src/main/res/layout/item_records_filter_button.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file