From 771b56eec8b183cd5b75f61d52b5a782c512d02b Mon Sep 17 00:00:00 2001 From: Sad Ellie Date: Mon, 11 Sep 2023 16:55:38 +0300 Subject: [PATCH] I pressed some buttons on my keyboard Experimental feature: Unit groups in vertical list --- .../unitto/data/userprefs/UserPreferences.kt | 3 + .../feature/converter/ConverterViewModel.kt | 5 +- .../feature/converter/LeftSideScreen.kt | 26 +++- .../feature/converter/LeftSideUIState.kt | 1 + .../feature/converter/components/ChipsRow.kt | 140 ++++++++++++++++++ .../unitto/feature/settings/AboutScreen.kt | 4 +- 6 files changed, 168 insertions(+), 11 deletions(-) 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 40f43338..f3b8984c 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 @@ -111,6 +111,7 @@ data class MainPreferences( val unitConverterFormatTime: Boolean = false, val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, val middleZero: Boolean = false, + val enableToolsExperiment: Boolean = false, ) /** @@ -208,6 +209,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS val unitConverterFormatTime: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false val unitConverterSorting: UnitsListSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING]?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE val middleZero: Boolean = preferences[PrefsKeys.MIDDLE_ZERO] ?: false + val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false MainPreferences( digitsPrecision = digitsPrecision, @@ -222,6 +224,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS unitConverterFormatTime = unitConverterFormatTime, unitConverterSorting = unitConverterSorting, middleZero = middleZero, + enableToolsExperiment = enableToolsExperiment ) } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt index 888badde..85265bc0 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt @@ -153,7 +153,8 @@ internal class ConverterViewModel @Inject constructor( unitFrom = unitFrom, sorting = prefs.unitConverterSorting, shownUnitGroups = prefs.shownUnitGroups, - favorites = prefs.unitConverterFavoritesOnly + favorites = prefs.unitConverterFavoritesOnly, + verticalList = prefs.enableToolsExperiment, ) } .onEach { @@ -162,7 +163,7 @@ internal class ConverterViewModel @Inject constructor( unitGroup = it.unitGroup, favoritesOnly = it.favorites, sorting = it.sorting, - shownUnitGroups = it.shownUnitGroups + shownUnitGroups = it.shownUnitGroups, ) } .stateIn(viewModelScope, SharingStarted.Lazily, LeftSideUIState()) diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt index 7f95768a..8da3ea36 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt @@ -51,6 +51,7 @@ import com.sadellie.unitto.core.ui.common.UnittoSearchBar import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.feature.converter.components.BasicUnitListItem +import com.sadellie.unitto.feature.converter.components.ChipsFlexRow import com.sadellie.unitto.feature.converter.components.ChipsRow import com.sadellie.unitto.feature.converter.components.FavoritesButton import com.sadellie.unitto.feature.converter.components.SearchPlaceholder @@ -129,13 +130,24 @@ private fun LeftSideScreen( } } ) - ChipsRow( - chosenUnitGroup = uiState.unitGroup, - items = uiState.shownUnitGroups, - selectAction = updateUnitGroup, - lazyListState = chipsRowLazyListState, - navigateToSettingsAction = navigateToUnitGroups - ) + + if (uiState.verticalList) { + ChipsFlexRow( + chosenUnitGroup = uiState.unitGroup, + items = uiState.shownUnitGroups, + selectAction = updateUnitGroup, + lazyListState = chipsRowLazyListState, + navigateToSettingsAction = navigateToUnitGroups + ) + } else { + ChipsRow( + chosenUnitGroup = uiState.unitGroup, + items = uiState.shownUnitGroups, + selectAction = updateUnitGroup, + lazyListState = chipsRowLazyListState, + navigateToSettingsAction = navigateToUnitGroups + ) + } } } ) { paddingValues -> diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt index ae21917e..151f7a7f 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt @@ -31,4 +31,5 @@ internal data class LeftSideUIState( val shownUnitGroups: List = emptyList(), val unitGroup: UnitGroup? = unitFrom?.group, val sorting: UnitsListSorting = UnitsListSorting.USAGE, + val verticalList: Boolean = false, ) diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ChipsRow.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ChipsRow.kt index 0631167e..2d16f8f5 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ChipsRow.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ChipsRow.kt @@ -19,32 +19,53 @@ package com.sadellie.unitto.feature.converter.components import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateDp +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.sadellie.unitto.core.base.R import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS @@ -118,6 +139,89 @@ internal fun ChipsRow( } } +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun ChipsFlexRow( + items: List = ALL_UNIT_GROUPS, + chosenUnitGroup: UnitGroup?, + selectAction: (UnitGroup?) -> Unit, + navigateToSettingsAction: () -> Unit, + lazyListState: LazyListState +) { + var expanded by remember { mutableStateOf(false) } + val transition = updateTransition(expanded, label = "Expanded transition") + + val rowHeight by transition.animateDp({ tween() }, "Row height") { + if (it) 392.dp else 32.dp + } + + val expandRotation by transition.animateFloat({ tween() }, "Expand rotation") { + if (it) 180f else 0f + } + + Row( + modifier = Modifier + .padding(16.dp, 8.dp) + .heightIn(max = rowHeight) + .clipToBounds() + ) { + FlowRow( + modifier = Modifier + .verticalScroll(rememberScrollState(), expanded) + .fillMaxHeight() + .weight(1f), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items.forEach { item -> + val isSelected: Boolean = item == chosenUnitGroup + UnittoFilterChip( + isSelected = isSelected, + selectAction = { selectAction(if (item == chosenUnitGroup) null else item) } + ) { + AnimatedVisibility(visible = isSelected) { + Icon( + modifier = Modifier.height(18.dp), + imageVector = Icons.Default.Check, + contentDescription = stringResource(R.string.checked_filter_description) + ) + } + Text( + modifier = Modifier.padding(start = 8.dp), + text = stringResource(item.res), + style = MaterialTheme.typography.labelLarge, + color = if (isSelected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + UnittoFilterChip( + isSelected = false, + selectAction = navigateToSettingsAction, + paddingValues = PaddingValues(horizontal = 8.dp) + ) { + Icon( + modifier = Modifier.height(18.dp), + imageVector = Icons.Default.Settings, + contentDescription = stringResource(R.string.open_settings_description) + ) + } + } + + IconButton( + onClick = { expanded = !expanded }, + modifier = Modifier.size(32.dp) + ) { + Icon( + imageVector = Icons.Default.ExpandMore, + contentDescription = null, + modifier = Modifier + .size(32.dp) + .rotate(expandRotation) + ) + } + } +} + /** * Basic chip implementation * @@ -154,3 +258,39 @@ private fun UnittoFilterChip( content() } } + +@Preview +@Composable +fun PreviewUnittoChips() { + var selected by remember { mutableStateOf(UnitGroup.LENGTH) } + + fun selectAction(unitGroup: UnitGroup?) { + selected = unitGroup + } + + ChipsRow( + items = ALL_UNIT_GROUPS, + chosenUnitGroup = selected, + selectAction = { selectAction(it) }, + navigateToSettingsAction = {}, + lazyListState = rememberLazyListState() + ) +} + +@Preview(showSystemUi = true, showBackground = true, backgroundColor = 0xFFE0E0E0) +@Composable +fun PreviewChipsFlowRow() { + var selected by remember { mutableStateOf(UnitGroup.LENGTH) } + + fun selectAction(unitGroup: UnitGroup?) { + selected = unitGroup + } + + ChipsFlexRow( + items = ALL_UNIT_GROUPS, + chosenUnitGroup = selected, + selectAction = { selectAction(it) }, + navigateToSettingsAction = {}, + lazyListState = rememberLazyListState() + ) +} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt index 0bae6585..2e91d3eb 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt @@ -186,7 +186,7 @@ internal fun AboutScreen( supportingContent = { Text("${BuildConfig.APP_NAME} (${BuildConfig.APP_CODE})") }, modifier = Modifier.combinedClickable { if (userPrefs.value.enableToolsExperiment) { - Toast.makeText(mContext, "Tools already enabled!", Toast.LENGTH_LONG).show() + Toast.makeText(mContext, "Experiments features are already enabled!", Toast.LENGTH_LONG).show() return@combinedClickable } @@ -194,7 +194,7 @@ internal fun AboutScreen( if (aboutItemClick < 7) return@combinedClickable viewModel.enableToolsExperiment() - Toast.makeText(mContext, "Tools enabled!", Toast.LENGTH_LONG).show() + Toast.makeText(mContext, "Experimental features enabled!", Toast.LENGTH_LONG).show() } ) }