From b95906b31f45c8f7d719a9cf5b5c7a4ab9d8946b Mon Sep 17 00:00:00 2001 From: Sad Ellie Date: Sat, 28 Jan 2023 19:00:46 +0400 Subject: [PATCH] Added settings to control haptic feedback (#16) --- core/base/src/main/res/values/strings.xml | 2 + .../data/preferences/UserPreferences.kt | 22 ++++- .../unitto/feature/converter/MainScreen.kt | 8 +- .../unitto/feature/converter/MainViewModel.kt | 14 +-- .../feature/converter/components/Keyboard.kt | 88 +++++++++---------- .../converter/components/KeyboardButton.kt | 4 +- .../unitto/feature/settings/SettingsScreen.kt | 20 ++++- .../feature/settings/SettingsViewModel.kt | 9 ++ 8 files changed, 109 insertions(+), 58 deletions(-) diff --git a/core/base/src/main/res/values/strings.xml b/core/base/src/main/res/values/strings.xml index d8061699..76656d81 100644 --- a/core/base/src/main/res/values/strings.xml +++ b/core/base/src/main/res/values/strings.xml @@ -999,6 +999,8 @@ Separator Output format Unit groups + Vibrations + Haptic feedback when clicking keyboard buttons Wrong currency rates? Note Currency rates are updated daily. There\'s no real-time market monitoring in the app diff --git a/data/src/main/java/com/sadellie/unitto/data/preferences/UserPreferences.kt b/data/src/main/java/com/sadellie/unitto/data/preferences/UserPreferences.kt index adcf137f..b7705176 100644 --- a/data/src/main/java/com/sadellie/unitto/data/preferences/UserPreferences.kt +++ b/data/src/main/java/com/sadellie/unitto/data/preferences/UserPreferences.kt @@ -50,7 +50,8 @@ import javax.inject.Inject * @property outputFormat Current [OutputFormat] that is applied to converted value (not input) * @property latestLeftSideUnit Latest [AbstractUnit] that was on the left side * @property latestRightSideUnit Latest [AbstractUnit] that was on the right side - * @property shownUnitGroups [UnitGroup]s that user wants to see. Excludes other [UnitGroup]s + * @property shownUnitGroups [UnitGroup]s that user wants to see. Excludes other [UnitGroup]s, + * @property enableVibrations When true will use haptic feedback in app. */ data class UserPreferences( val themingMode: ThemingMode? = null, @@ -61,7 +62,8 @@ data class UserPreferences( val outputFormat: Int = OutputFormat.PLAIN, val latestLeftSideUnit: String = MyUnitIDS.kilometer, val latestRightSideUnit: String = MyUnitIDS.mile, - val shownUnitGroups: List = ALL_UNIT_GROUPS + val shownUnitGroups: List = ALL_UNIT_GROUPS, + val enableVibrations: Boolean = true ) /** @@ -82,6 +84,7 @@ UserPreferencesRepository @Inject constructor(private val dataStore: DataStore

= dataStore.data @@ -123,6 +126,7 @@ UserPreferencesRepository @Inject constructor(private val dataStore: DataStore

+ preferences[PrefsKeys.ENABLE_VIBRATIONS] = enabled + } + } } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainScreen.kt index 8ae6272e..fab4c833 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainScreen.kt @@ -53,6 +53,7 @@ internal fun MainScreen( ) { var launched: Boolean by rememberSaveable { mutableStateOf(false) } val mainScreenUIState = viewModel.uiStateFlow.collectAsStateWithLifecycle() + val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() Scaffold( modifier = Modifier, @@ -83,7 +84,8 @@ internal fun MainScreen( processInput = { viewModel.processInput(it) }, deleteDigit = { viewModel.deleteDigit() }, clearInput = { viewModel.clearInput() }, - onOutputTextFieldClick = { viewModel.toggleFormatTime() } + onOutputTextFieldClick = { viewModel.toggleFormatTime() }, + allowVibration = userPrefs.value.enableVibrations ) } ) @@ -109,7 +111,8 @@ private fun MainScreenContent( processInput: (String) -> Unit = {}, deleteDigit: () -> Unit = {}, clearInput: () -> Unit = {}, - onOutputTextFieldClick: () -> Unit + onOutputTextFieldClick: () -> Unit, + allowVibration: Boolean ) { PortraitLandscape( modifier = modifier, @@ -138,6 +141,7 @@ private fun MainScreenContent( deleteDigit = deleteDigit, clearInput = clearInput, converterMode = mainScreenUIState.mode, + allowVibration = allowVibration ) } ) diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainViewModel.kt index 27ce601c..98344903 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/MainViewModel.kt @@ -84,7 +84,7 @@ class MainViewModel @Inject constructor( private val allUnitsRepository: AllUnitsRepository ) : ViewModel() { - private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn( + val userPrefs = userPrefsRepository.userPreferencesFlow.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5000), UserPreferences() @@ -444,7 +444,7 @@ class MainViewModel @Inject constructor( // Now we evaluate expression in input val evaluationResult: BigDecimal = try { Expressions().eval(cleanInput) - .setScale(_userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN) + .setScale(userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN) .trimZeros() } catch (e: Exception) { when (e) { @@ -470,9 +470,9 @@ class MainViewModel @Inject constructor( } else { _calculated.update { evaluationResult - .setMinimumRequiredScale(_userPrefs.value.digitsPrecision) + .setMinimumRequiredScale(userPrefs.value.digitsPrecision) .trimZeros() - .toStringWith(_userPrefs.value.outputFormat) + .toStringWith(userPrefs.value.outputFormat) } } @@ -481,11 +481,11 @@ class MainViewModel @Inject constructor( val conversionResult: BigDecimal = unitFrom.convert( unitTo, evaluationResult, - _userPrefs.value.digitsPrecision + userPrefs.value.digitsPrecision ) // Converted - _result.update { conversionResult.toStringWith(_userPrefs.value.outputFormat) } + _result.update { conversionResult.toStringWith(userPrefs.value.outputFormat) } } private fun setInputSymbols(symbol: String, add: Boolean = true) { @@ -564,7 +564,7 @@ class MainViewModel @Inject constructor( private fun startObserving() { viewModelScope.launch(Dispatchers.Default) { - merge(_input, _unitFrom, _unitTo, _showLoading, _userPrefs).collectLatest { + merge(_input, _unitFrom, _unitTo, _showLoading, userPrefs).collectLatest { convertInput() } } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/Keyboard.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/Keyboard.kt index e38c6d6d..c463f530 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/Keyboard.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/Keyboard.kt @@ -74,11 +74,12 @@ internal fun Keyboard( deleteDigit: () -> Unit = {}, clearInput: () -> Unit = {}, converterMode: ConverterMode, + allowVibration: Boolean ) { Crossfade(converterMode, modifier = modifier) { when (it) { - ConverterMode.DEFAULT -> DefaultKeyboard(addDigit, clearInput, deleteDigit) - ConverterMode.BASE -> BaseKeyboard(addDigit, clearInput, deleteDigit) + ConverterMode.DEFAULT -> DefaultKeyboard(addDigit, clearInput, deleteDigit, allowVibration) + ConverterMode.BASE -> BaseKeyboard(addDigit, clearInput, deleteDigit, allowVibration) } } @@ -88,7 +89,8 @@ internal fun Keyboard( private fun DefaultKeyboard( addDigit: (String) -> Unit, clearInput: () -> Unit, - deleteDigit: () -> Unit + deleteDigit: () -> Unit, + allowVibration: Boolean ) { Column { // Button modifier @@ -99,38 +101,34 @@ private fun DefaultKeyboard( // Column modifier val cModifier = Modifier.weight(1f) Row(cModifier) { - KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_EXPONENT, isPrimary = false, onClick = { addDigit( - KEY_EXPONENT - ) }) - KeyboardButton(bModifier, KEY_SQRT, isPrimary = false, onClick = { addDigit(KEY_SQRT) }) + KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_EXPONENT, isPrimary = false, allowVibration = allowVibration, onClick = { addDigit(KEY_EXPONENT) }) + KeyboardButton(bModifier, KEY_SQRT, isPrimary = false, allowVibration = allowVibration, onClick = { addDigit(KEY_SQRT) }) } Row(cModifier) { - KeyboardButton(bModifier, KEY_7, onClick = addDigit) - KeyboardButton(bModifier, KEY_8, onClick = addDigit) - KeyboardButton(bModifier, KEY_9, onClick = addDigit) - KeyboardButton(bModifier, KEY_DIVIDE_DISPLAY, isPrimary = false) { addDigit(KEY_DIVIDE) } + KeyboardButton(bModifier, KEY_7, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_8, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_9, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_DIVIDE_DISPLAY, isPrimary = false, allowVibration = allowVibration) { addDigit(KEY_DIVIDE) } } Row(cModifier) { - KeyboardButton(bModifier, KEY_4, onClick = addDigit) - KeyboardButton(bModifier, KEY_5, onClick = addDigit) - KeyboardButton(bModifier, KEY_6, onClick = addDigit) - KeyboardButton(bModifier, KEY_MULTIPLY_DISPLAY, isPrimary = false) { addDigit( - KEY_MULTIPLY - ) } + KeyboardButton(bModifier, KEY_4, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_5, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_6, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_MULTIPLY_DISPLAY, isPrimary = false, allowVibration = allowVibration, ) { addDigit(KEY_MULTIPLY) } } Row(cModifier) { - KeyboardButton(bModifier, KEY_1, onClick = addDigit) - KeyboardButton(bModifier, KEY_2, onClick = addDigit) - KeyboardButton(bModifier, KEY_3, onClick = addDigit) - KeyboardButton(bModifier, KEY_MINUS_DISPLAY, isPrimary = false) { addDigit(KEY_MINUS) } + KeyboardButton(bModifier, KEY_1, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_2, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_3, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_MINUS_DISPLAY, isPrimary = false, allowVibration = allowVibration) { addDigit(KEY_MINUS) } } Row(cModifier) { - KeyboardButton(bModifier, KEY_0, onClick = addDigit) - KeyboardButton(bModifier, Formatter.fractional) { addDigit(KEY_DOT) } - KeyboardButton(bModifier, KEY_CLEAR, onLongClick = clearInput) { deleteDigit() } - KeyboardButton(bModifier, KEY_PLUS, isPrimary = false) { addDigit(KEY_PLUS) } + KeyboardButton(bModifier, KEY_0, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, Formatter.fractional, allowVibration = allowVibration, ) { addDigit(KEY_DOT) } + KeyboardButton(bModifier, KEY_CLEAR, allowVibration = allowVibration, onLongClick = clearInput) { deleteDigit() } + KeyboardButton(bModifier, KEY_PLUS, isPrimary = false, allowVibration = allowVibration, ) { addDigit(KEY_PLUS) } } } } @@ -139,7 +137,8 @@ private fun DefaultKeyboard( private fun BaseKeyboard( addDigit: (String) -> Unit, clearInput: () -> Unit, - deleteDigit: () -> Unit + deleteDigit: () -> Unit, + allowVibration: Boolean ) { Column { // Button modifier @@ -150,38 +149,39 @@ private fun BaseKeyboard( // Column modifier val cModifier = Modifier.weight(1f) Row(cModifier) { - KeyboardButton(bModifier, KEY_BASE_A, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_BASE_B, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_BASE_C, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_A, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_B, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_C, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) } Row(cModifier) { - KeyboardButton(bModifier, KEY_BASE_D, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_BASE_E, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_BASE_F, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_D, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_E, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_F, isPrimary = false, allowVibration = allowVibration, onClick = addDigit) } Row(cModifier) { - KeyboardButton(bModifier, KEY_7, onClick = addDigit) - KeyboardButton(bModifier, KEY_8, onClick = addDigit) - KeyboardButton(bModifier, KEY_9, onClick = addDigit) + KeyboardButton(bModifier, KEY_7, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_8, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_9, allowVibration = allowVibration, onClick = addDigit) } Row(cModifier) { - KeyboardButton(bModifier, KEY_4, onClick = addDigit) - KeyboardButton(bModifier, KEY_5, onClick = addDigit) - KeyboardButton(bModifier, KEY_6, onClick = addDigit) + KeyboardButton(bModifier, KEY_4, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_5, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_6, allowVibration = allowVibration, onClick = addDigit) } Row(cModifier) { - KeyboardButton(bModifier, KEY_1, onClick = addDigit) - KeyboardButton(bModifier, KEY_2, onClick = addDigit) - KeyboardButton(bModifier, KEY_3, onClick = addDigit) + KeyboardButton(bModifier, KEY_1, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_2, allowVibration = allowVibration, onClick = addDigit) + KeyboardButton(bModifier, KEY_3, allowVibration = allowVibration, onClick = addDigit) } Row(cModifier) { - KeyboardButton(bModifier, KEY_0, onClick = addDigit) + KeyboardButton(bModifier, KEY_0, allowVibration = allowVibration, onClick = addDigit) KeyboardButton( Modifier .fillMaxSize() .weight(2f) .padding(4.dp), KEY_CLEAR, + allowVibration = allowVibration, onLongClick = clearInput ) { deleteDigit() } } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/KeyboardButton.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/KeyboardButton.kt index 55109b0c..03458c09 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/KeyboardButton.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/KeyboardButton.kt @@ -44,6 +44,7 @@ import com.sadellie.unitto.core.ui.theme.NumbersTextStyleTitleLarge * * @param modifier Modifier that is applied to a [Button] component. * @param digit Symbol to show on button. + * @param allowVibration When true will vibrate on button press. * @param isPrimary If true will use `inverseOnSurface` color, else `secondaryContainer`. Primary * buttons are digits. * @param onLongClick Action to perform when holding this button. @@ -53,6 +54,7 @@ import com.sadellie.unitto.core.ui.theme.NumbersTextStyleTitleLarge internal fun KeyboardButton( modifier: Modifier = Modifier, digit: String, + allowVibration: Boolean, isPrimary: Boolean = true, onLongClick: (() -> Unit)? = null, onClick: (String) -> Unit = {} @@ -83,6 +85,6 @@ internal fun KeyboardButton( } LaunchedEffect(key1 = isPressed) { - if (isPressed) view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + if (isPressed and allowVibration) view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) } } 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 128830d6..19254959 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 @@ -26,6 +26,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.Vibration import androidx.compose.material.icons.filled._123 import androidx.compose.material3.Icon import androidx.compose.material3.ListItem @@ -44,10 +45,11 @@ import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.OUTPUT_FORMAT import com.sadellie.unitto.core.base.PRECISIONS import com.sadellie.unitto.core.base.SEPARATORS +import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar +import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.openLink -import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.feature.settings.components.AlertDialogWithList import com.sadellie.unitto.feature.settings.navigation.aboutRoute import com.sadellie.unitto.feature.settings.navigation.themesRoute @@ -144,6 +146,22 @@ internal fun SettingsScreen( // ADDITIONAL GROUP item { Header(stringResource(R.string.additional_settings_group)) } + // VIBRATIONS + item { + UnittoListItem( + label = stringResource(R.string.enable_vibrations), + leadingContent = { + Icon( + Icons.Default.Vibration, + stringResource(R.string.enable_vibrations) + ) + }, + supportText = stringResource(R.string.enable_vibrations_support), + switchState = userPrefs.value.enableVibrations, + onSwitchChange = viewModel::updateVibrations + ) + } + // RATE THIS APP if (BuildConfig.STORE_LINK.isNotEmpty()) { item { 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 09d0a110..38ba5f40 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 @@ -100,6 +100,15 @@ class SettingsViewModel @Inject constructor( } } + /** + * See [UserPreferencesRepository.updateVibrations] + */ + fun updateVibrations(enabled: Boolean) { + viewModelScope.launch { + userPrefsRepository.updateVibrations(enabled) + } + } + /** * See [UnitGroupsRepository.markUnitGroupAsHidden] and * [UserPreferencesRepository.updateShownUnitGroups]