diff --git a/core/base/src/main/res/values/strings.xml b/core/base/src/main/res/values/strings.xml index 78978fe1..ab3740f5 100644 --- a/core/base/src/main/res/values/strings.xml +++ b/core/base/src/main/res/values/strings.xml @@ -1245,6 +1245,8 @@ Haptic feedback when clicking keyboard buttons Format time Example: Show 130 minutes as 2h 10m + Units list sorting + Change units order Wrong currency rates? Note Currency rates are updated daily. There\'s no real-time market monitoring in the app @@ -1303,6 +1305,12 @@ Allow engineering Force engineering + + Usage + Alphabetical + Scale (Desc.) + Scale (Asc.) + App look and feel Auto diff --git a/data/model/src/main/java/com/sadellie/unitto/data/model/UnitsListSorting.kt b/data/model/src/main/java/com/sadellie/unitto/data/model/UnitsListSorting.kt new file mode 100644 index 00000000..a2689d24 --- /dev/null +++ b/data/model/src/main/java/com/sadellie/unitto/data/model/UnitsListSorting.kt @@ -0,0 +1,21 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.data.model + +enum class UnitsListSorting { USAGE, ALPHABETICAL, SCALE_DESC, SCALE_ASC } diff --git a/data/units/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt b/data/units/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt index d8d7e1b8..83e22057 100644 --- a/data/units/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt +++ b/data/units/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt @@ -23,6 +23,7 @@ import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.data.database.UnitsEntity import com.sadellie.unitto.data.model.AbstractUnit import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.sortByLev import com.sadellie.unitto.data.units.collections.accelerationCollection import com.sadellie.unitto.data.units.collections.angleCollection @@ -129,6 +130,8 @@ class AllUnitsRepository @Inject constructor() { * set to True. * @param searchQuery When not empty, will search by [AbstractUnit.renderedName] using [sortByLev]. * @param allUnitsGroups All [UnitGroup]s. Determines which units will be used for filtering. + * @param sorting Sorting mode from [UnitsListSorting] + * * @return Grouped by [UnitGroup] list of [AbstractUnit]s. */ fun filterUnits( @@ -136,9 +139,11 @@ class AllUnitsRepository @Inject constructor() { chosenUnitGroup: UnitGroup?, favoritesOnly: Boolean, searchQuery: String, - allUnitsGroups: List + allUnitsGroups: List, + sorting: UnitsListSorting = UnitsListSorting.USAGE ): Map> { - var basicFilteredUnits: Sequence = if (chosenUnitGroup == null) { + // Leave only shown unit groups + var units: Sequence = if (chosenUnitGroup == null) { allUnits.filter { it.group in allUnitsGroups } } else { val collection = getCollectionByGroup(chosenUnitGroup) @@ -146,20 +151,27 @@ class AllUnitsRepository @Inject constructor() { }.asSequence() if (favoritesOnly) { - basicFilteredUnits = basicFilteredUnits.filter { it.isFavorite } + units = units.filter { it.isFavorite } } if (hideBrokenCurrencies) { - basicFilteredUnits = basicFilteredUnits.filter { it.isEnabled } - } - val unitsToShow = if (searchQuery.isEmpty()) { - // Query is empty, i.e. we want to see all units and they need to be sorted by usage - basicFilteredUnits.sortedByDescending { it.counter } - } else { - // We sort by popularity and Levenshtein distance (short and long name). - basicFilteredUnits.sortedByDescending { it.counter }.sortByLev(searchQuery) + units = units.filter { it.isEnabled } } - return unitsToShow.groupBy { it.group } + units = when (sorting) { + UnitsListSorting.USAGE -> units.sortedByDescending { it.counter } + UnitsListSorting.ALPHABETICAL -> units.sortedBy { it.renderedName } + UnitsListSorting.SCALE_ASC -> units.sortedBy { it.basicUnit } + UnitsListSorting.SCALE_DESC -> units.sortedByDescending { it.basicUnit } + } + + units = if (searchQuery.isEmpty()) { + units.sortedByDescending { it.isFavorite } + } else { + // For search we sort by popularity and Levenshtein distance (short and long name). + units.sortByLev(searchQuery) + } + + return units.groupBy { it.group } } /** diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt index 741b2166..5942b3ff 100644 --- a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt @@ -31,6 +31,7 @@ import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS import com.sadellie.unitto.data.model.AbstractUnit import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.units.MyUnitIDS import io.github.sadellie.themmo.ThemingMode import kotlinx.coroutines.flow.Flow @@ -56,7 +57,8 @@ import javax.inject.Inject * @property enableToolsExperiment When true will enable experimental Tools screen. * @property radianMode AngleMode in mxParser. When true - Radian, when False - Degree. * @property unitConverterFavoritesOnly If true will show only units that are marked as favorite. - * @property unitConverterFormatTime If true will format time to be more human readable + * @property unitConverterFormatTime If true will format time to be more human readable. + * @property unitConverterSorting Units list sorting mode. */ data class UserPreferences( val themingMode: ThemingMode? = null, @@ -74,6 +76,7 @@ data class UserPreferences( val radianMode: Boolean = true, val unitConverterFavoritesOnly: Boolean = false, val unitConverterFormatTime: Boolean = false, + val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, ) /** @@ -99,6 +102,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS val RADIAN_MODE = booleanPreferencesKey("RADIAN_MODE_PREF_KEY") val UNIT_CONVERTER_FAVORITES_ONLY = booleanPreferencesKey("UNIT_CONVERTER_FAVORITES_ONLY_PREF_KEY") val UNIT_CONVERTER_FORMAT_TIME = booleanPreferencesKey("UNIT_CONVERTER_FORMAT_TIME_PREF_KEY") + val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY") } val userPreferencesFlow: Flow = dataStore.data @@ -146,6 +150,8 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS val radianMode: Boolean = preferences[PrefsKeys.RADIAN_MODE] ?: true val unitConverterFavoritesOnly: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] ?: false val unitConverterFormatTime: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false + val unitConverterSorting: UnitsListSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING]?.let { UnitsListSorting.valueOf(it) } + ?: UnitsListSorting.USAGE UserPreferences( themingMode = themingMode, @@ -163,6 +169,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS radianMode = radianMode, unitConverterFavoritesOnly = unitConverterFavoritesOnly, unitConverterFormatTime = unitConverterFormatTime, + unitConverterSorting = unitConverterSorting ) } @@ -317,4 +324,15 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] = enabled } } + + /** + * Update [UserPreferences.unitConverterSorting]. + * + * @see UserPreferences.unitConverterSorting + */ + suspend fun updateUnitConverterSorting(sorting: UnitsListSorting) { + dataStore.edit { preferences -> + preferences[PrefsKeys.UNIT_CONVERTER_SORTING] = sorting.name + } + } } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt index 7073da12..4b287ed9 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.RateReview import androidx.compose.material.icons.filled.Rule +import androidx.compose.material.icons.filled.Sort import androidx.compose.material.icons.filled.Timer import androidx.compose.material.icons.filled.Vibration import androidx.compose.material.icons.filled._123 @@ -51,9 +52,10 @@ import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.MenuButton -import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.common.UnittoListItem +import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.openLink +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.feature.settings.components.AlertDialogWithList import com.sadellie.unitto.feature.settings.navigation.aboutRoute import com.sadellie.unitto.feature.settings.navigation.themesRoute @@ -165,6 +167,21 @@ internal fun SettingsScreen( ) } + // UNITS LIST SORTING + item { + ListItem( + leadingContent = { + Icon( + Icons.Default.Sort, + stringResource(R.string.units_sorting) + ) + }, + headlineText = { Text(stringResource(R.string.units_sorting)) }, + supportingText = { Text(stringResource(R.string.units_sorting_support)) }, + modifier = Modifier.clickable { dialogState = DialogState.UNIT_LIST_SORTING } + ) + } + // FORMAT TIME item { UnittoListItem( @@ -280,6 +297,20 @@ internal fun SettingsScreen( dismissAction = { resetDialog() } ) } + DialogState.UNIT_LIST_SORTING -> { + AlertDialogWithList( + title = stringResource(R.string.units_sorting), + listItems = mapOf( + UnitsListSorting.USAGE to R.string.sort_by_usage, + UnitsListSorting.ALPHABETICAL to R.string.sort_by_alphabetical, + UnitsListSorting.SCALE_DESC to R.string.sort_by_scale_desc, + UnitsListSorting.SCALE_ASC to R.string.sort_by_scale_asc, + ), + selectedItemIndex = userPrefs.value.unitConverterSorting, + selectAction = viewModel::updateUnitConverterSorting, + dismissAction = { resetDialog() } + ) + } // Dismissing alert dialog else -> {} } @@ -289,5 +320,5 @@ internal fun SettingsScreen( * All possible states for alert dialog that opens when user clicks on settings. */ private enum class DialogState { - NONE, PRECISION, SEPARATOR, OUTPUT_FORMAT, START_SCREEN + NONE, PRECISION, SEPARATOR, OUTPUT_FORMAT, START_SCREEN, UNIT_LIST_SORTING } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt index 07740129..6f4ebfc4 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.core.ui.Formatter import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.unitgroups.UnitGroupsRepository import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -177,6 +178,15 @@ class SettingsViewModel @Inject constructor( } } + /** + * @see UserPreferencesRepository.updateUnitConverterSorting + */ + fun updateUnitConverterSorting(sorting: UnitsListSorting) { + viewModelScope.launch { + userPrefsRepository.updateUnitConverterSorting(sorting) + } + } + /** * Prevent from dragging over non-draggable items (headers and hidden) * diff --git a/feature/unitslist/src/main/java/com/sadellie/unitto/feature/unitslist/UnitsListViewModel.kt b/feature/unitslist/src/main/java/com/sadellie/unitto/feature/unitslist/UnitsListViewModel.kt index dab4dbc6..af2fea90 100644 --- a/feature/unitslist/src/main/java/com/sadellie/unitto/feature/unitslist/UnitsListViewModel.kt +++ b/feature/unitslist/src/main/java/com/sadellie/unitto/feature/unitslist/UnitsListViewModel.kt @@ -134,7 +134,8 @@ class UnitsListViewModel @Inject constructor( chosenUnitGroup = _chosenUnitGroup.value, favoritesOnly = _userPrefs.value.unitConverterFavoritesOnly, searchQuery = _searchQuery.value, - allUnitsGroups = _shownUnitGroups.value + allUnitsGroups = _shownUnitGroups.value, + sorting = _userPrefs.value.unitConverterSorting ) _unitsToShow.update { unitsToShow }