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