diff --git a/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt b/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt index 9b8650e66..92786f52c 100644 --- a/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt @@ -54,7 +54,16 @@ import helium314.keyboard.latin.R import helium314.keyboard.settings.preferences.PreferenceCategory import helium314.keyboard.latin.utils.prefs +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.relocation.BringIntoViewRequester +import androidx.compose.foundation.relocation.bringIntoViewRequester +import helium314.keyboard.settings.preferences.LocalPreferenceLongClick +import kotlinx.coroutines.delay +@OptIn(ExperimentalFoundationApi::class) @Composable fun SearchSettingsScreen( onClickBack: () -> Unit, @@ -104,7 +113,46 @@ fun SearchSettingsScreen( result.filter { it.second.isNotEmpty() } } + // jump from a long press in the search results (see SettingsJump): show the setting + // here, opening its fine-tune section if needed, scroll to it and highlight it + val listState = rememberLazyListState() + var highlightKey by remember { mutableStateOf(null) } + val jumpTarget = jumpTarget() + val jumpOpened = jumpOpenedSections() + val jump = jumpState() + // the setting to scroll to once the list shows it; separate from the jump state, so that + // ending the jump does not cancel the scrolling + var scrollKey by remember { mutableStateOf(null) } + LaunchedEffect(groups, jumpTarget) { + val target = jumpTarget ?: return@LaunchedEffect + val shown = groups.flatMap { it.second } + val prefs = context.prefs() + when (val step = nextJumpStep(target, settings, shown, jumpOpened) { prefs.getBoolean(it, false) }) { + // the screen shows more settings then, and this runs again + is JumpStep.OpenSections -> jump.setOpenedSections(step.sections) + JumpStep.GiveUp -> { jump.setOpenedSections(emptySet()); jump.done() } + is JumpStep.Show -> { + // closing the other sections changes the list, this runs again after that + if (step.keepOpen != jumpOpened) jump.setOpenedSections(step.keepOpen) + else { scrollKey = target; jump.done() } + } + } + } + LaunchedEffect(scrollKey) { + val target = scrollKey ?: return@LaunchedEffect + val index = groups.indexOfFirst { target in it.second } + if (index >= 0) { + // to the card with the setting, then the setting itself is brought into view (see below) + listState.animateScrollToItem(index + if (headerContent != null) 1 else 0) + highlightKey = target + delay(2500) + highlightKey = null + } + scrollKey = null + } + LazyColumn( + state = listState, modifier = Modifier .padding(innerPadding) .fillMaxSize(), @@ -131,7 +179,17 @@ fun SearchSettingsScreen( keys.forEach { key -> androidx.compose.runtime.key(key) { - SettingsActivity.settingsContainer[key]?.Preference() + val highlight by animateColorAsState( + if (key == highlightKey) MaterialTheme.colorScheme.primaryContainer + else androidx.compose.ui.graphics.Color.Transparent, + label = "jump highlight" + ) + // a setting further down in a large card is not on screen after scrolling to the card + val bringIntoView = remember { BringIntoViewRequester() } + LaunchedEffect(highlightKey) { if (key == highlightKey) bringIntoView.bringIntoView() } + Box(Modifier.bringIntoViewRequester(bringIntoView).background(highlight)) { + SettingsActivity.settingsContainer[key]?.Preference() + } } } } @@ -162,10 +220,25 @@ fun SearchSettingsScreen( } } }, - itemContent = { it.Preference() } + // long press on a search result opens the screen where the setting is + itemContent = { setting -> SearchResult(setting) } ) } +/** A search result: a long press opens the screen of the setting and shows it there */ +@Composable +private fun SearchResult(setting: Setting) { + val navController = LocalSettingsNavController.current + val destination = SettingsActivity.settingsContainer.screenOf(setting.key) + if (navController == null || destination == null) { + setting.Preference() + return + } + CompositionLocalProvider(LocalPreferenceLongClick provides { jumpToSetting(navController, destination, setting.key) }) { + setting.Preference() + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun SearchScreen( diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt b/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt index fe0f217f5..756a89f36 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt @@ -27,7 +27,8 @@ import helium314.keyboard.settings.screens.createToolbarSettings import helium314.keyboard.latin.voice.createVoiceSettings class SettingsContainer(context: Context) { - private val list = createSettings(context) + private val settingsWithScreens = createSettings(context) + private val list = settingsWithScreens.map { it.first } private val map: Map = HashMap(list.size).apply { list.forEach { if (!containsKey(it.key)) { @@ -36,8 +37,15 @@ class SettingsContainer(context: Context) { } } + /** settings screen (destination) that shows a setting, for jumping there from the search */ + private val screens: Map = HashMap().apply { + settingsWithScreens.forEach { (setting, destination) -> if (destination != null) putIfAbsent(setting.key, destination) } + } + operator fun get(key: Any): Setting? = map[key] + fun screenOf(key: String): String? = screens[key] + // filtering could be more elaborate, but should be good enough for a start // always have all settings in search, because: // don't show disabled settings -> users confused @@ -153,10 +161,11 @@ private val modules = listOf( SettingsModule(SettingsWithoutKey.SCREEN_NAV_UPDATES, SettingsDestination.Updates, R.string.settings_screen_updates, iconRes = R.drawable.ic_settings_updates) ) -private fun createSettings(context: Context): List = buildList { +/** all settings, each with the destination of the screen that shows it (null for the navigation entries) */ +private fun createSettings(context: Context): List> = buildList { modules.forEach { module -> - add(module.createNavigationSetting(context)) - module.provider?.invoke(context)?.let { addAll(it) } + add(module.createNavigationSetting(context) to null) + module.provider?.invoke(context)?.forEach { add(it to module.destination) } } } diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsJump.kt b/app/src/main/java/helium314/keyboard/settings/SettingsJump.kt new file mode 100644 index 000000000..711761ce0 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/SettingsJump.kt @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.settings + +import android.content.SharedPreferences +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavController + +/* + * Jump from a long press in the settings search to the screen of a setting, which then scrolls to the + * setting and highlights it. What the screen is asked to show, and the fine-tune sections opened for it, + * belong to the back stack entry of that screen: nothing is written to the preferences, nothing is kept + * in a global object, and it ends when the screen is left. + */ + +private const val JUMP_TO = "settings_jump_to" +private const val JUMP_OPENED_SECTIONS = "settings_jump_opened_sections" +internal const val FINE_TUNE_SECTION_PREFIX = "expand_fine_tune_" + +/** The nav controller of the settings, used by the search results to open the screen of a setting */ +val LocalSettingsNavController = staticCompositionLocalOf { null } + +/** Opens [destination] and asks it to show the setting [key] */ +fun jumpToSetting(navController: NavController, destination: String, key: String) { + navController.navigate(destination) + navController.currentBackStackEntry?.savedStateHandle?.set(JUMP_TO, key) +} + +/** State of the screen this is called in: null outside of the navigation (e.g. in a preview) */ +@Composable +private fun screenState(): SavedStateHandle? = (LocalViewModelStoreOwner.current as? NavBackStackEntry)?.savedStateHandle + +/** The setting this screen is asked to show, if any */ +@Composable +internal fun jumpTarget(): String? { + val state = screenState() ?: return null + val target by state.getStateFlow(JUMP_TO, null).collectAsState() + return target +} + +/** Fine-tune sections opened on this screen to show a setting; they are not saved */ +@Composable +fun jumpOpenedSections(): Set { + val state = screenState() ?: return emptySet() + val opened by state.getStateFlow(JUMP_OPENED_SECTIONS, ArrayList()).collectAsState() + return opened.toSet() +} + +/** Whether a fine-tune section is open: as the user left it, or opened to show a setting (see [jumpOpenedSections]) */ +@Composable +fun fineTuneExpanded(prefs: SharedPreferences, key: String, default: Boolean): Boolean = + prefs.getBoolean(key, default) || key in jumpOpenedSections() + +/** Changes of this screen's jump state, for use outside of composition */ +class JumpState internal constructor(private val state: SavedStateHandle?) { + fun setOpenedSections(sections: Collection) { state?.set(JUMP_OPENED_SECTIONS, ArrayList(sections)) } + /** The user opens or closes the section [key]: from now on it is as the user wants */ + fun userToggledSection(key: String) { + val opened = state?.get>(JUMP_OPENED_SECTIONS) ?: return + if (key in opened) setOpenedSections(opened - key) + } + fun done() { state?.remove(JUMP_TO) } +} + +@Composable +fun jumpState() = JumpState(screenState()) + +/** What to do next to show [target] on a screen */ +internal sealed interface JumpStep { + /** Open these closed fine-tune sections, the setting may be in one of them */ + data class OpenSections(val sections: Set) : JumpStep + /** The setting is shown; of the sections opened for the jump, keep only [keepOpen] (the one with the setting) */ + data class Show(val keepOpen: Set) : JumpStep + /** The setting is not on this screen, or hidden for another reason: close the opened sections again */ + data object GiveUp : JumpStep +} + +/** + * [settings]: the screen's list (setting keys and section titles), [shown]: the keys in the list now, + * [opened]: sections opened for this jump, [isOpen]: whether a section is open as the user left it. + */ +internal fun nextJumpStep( + target: String, + settings: List, + shown: Collection, + opened: Set, + isOpen: (String) -> Boolean, +): JumpStep { + if (target in shown) { + val index = settings.indexOf(target) + val owner = settings.subList(0, index.coerceAtLeast(0)).lastOrNull { it is String && it in opened } as String? + return JumpStep.Show(setOfNotNull(owner)) + } + if (opened.isNotEmpty()) return JumpStep.GiveUp + val closed = settings.filterIsInstance().filter { it.startsWith(FINE_TUNE_SECTION_PREFIX) && !isOpen(it) } + return if (closed.isEmpty()) JumpStep.GiveUp else JumpStep.OpenSections(closed.toSet()) +} diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt index c0f2a50be..33e43cb66 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection @@ -65,6 +66,8 @@ fun SettingsNavHost( if (!navController.popBackStack()) onClickBack() } + // the search results open the screen of a setting with it, see SettingsJump + CompositionLocalProvider(LocalSettingsNavController provides navController) { NavHost( navController = navController, startDestination = startDestination ?: SettingsDestination.Settings, @@ -223,6 +226,7 @@ fun SettingsNavHost( helium314.keyboard.settings.screens.CjkSettingsScreen(onClickBack = ::goBack) } } + } } object SettingsDestination { diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/ExpandablePreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/ExpandablePreference.kt index bce5bb9f1..bac5683d2 100644 --- a/app/src/main/java/helium314/keyboard/settings/preferences/ExpandablePreference.kt +++ b/app/src/main/java/helium314/keyboard/settings/preferences/ExpandablePreference.kt @@ -15,6 +15,8 @@ import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.prefs import helium314.keyboard.settings.Setting import helium314.keyboard.settings.SettingsActivity +import helium314.keyboard.settings.jumpOpenedSections +import helium314.keyboard.settings.jumpState import kotlinx.coroutines.flow.MutableStateFlow @Composable @@ -26,15 +28,19 @@ fun ExpandablePreference( val prefs = ctx.prefs() val activity = ctx.getActivity() as? SettingsActivity val prefChanged = (activity?.prefChanged ?: MutableStateFlow(0)).collectAsState() - val expanded = remember(prefChanged.value) { + val saved = remember(prefChanged.value) { prefs.getBoolean(setting.key, defaultExpanded) } + // may be open only to show a setting from the search, without changing what is saved + val expanded = saved || setting.key in jumpOpenedSections() + val jump = jumpState() Preference( name = setting.title, description = setting.description, onClick = { val newExpanded = !expanded prefs.edit { putBoolean(setting.key, newExpanded) } + jump.userToggledSection(setting.key) activity?.prefChanged() } ) { diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/Preference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/Preference.kt index 653bcd1a8..390676443 100644 --- a/app/src/main/java/helium314/keyboard/settings/preferences/Preference.kt +++ b/app/src/main/java/helium314/keyboard/settings/preferences/Preference.kt @@ -2,7 +2,9 @@ package helium314.keyboard.settings.preferences import androidx.annotation.DrawableRes +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -22,9 +24,11 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.draw.clip import androidx.compose.foundation.background import androidx.compose.ui.text.style.Hyphens @@ -54,6 +58,10 @@ fun PreferenceCategory( } } +/** A long press action for the [Preference] shown inside, e.g. "show in its screen" for search results */ +val LocalPreferenceLongClick = compositionLocalOf<(() -> Unit)?> { null } + +@OptIn(ExperimentalFoundationApi::class) @Composable fun Preference( name: String, @@ -64,10 +72,19 @@ fun Preference( @DrawableRes icon: Int? = null, value: @Composable (RowScope.() -> Unit)? = null, ) { + val onLongClick = LocalPreferenceLongClick.current Row( modifier = modifier .fillMaxWidth() - .clickable(enabled = enabled) { onClick() } + .then( + if (onLongClick == null) Modifier.clickable(enabled = enabled) { onClick() } + else Modifier.combinedClickable( + enabled = enabled, + onLongClickLabel = stringResource(R.string.settings_search_show_in_screen), + onLongClick = onLongClick, + onClick = onClick, + ) + ) .heightIn(min = 44.dp) .padding(vertical = 10.dp, horizontal = 12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -105,6 +122,7 @@ fun Preference( } if (value != null) { CompositionLocalProvider( + LocalPreferenceLongClick provides null, LocalTextStyle provides LocalTextStyle.current.copy( textAlign = TextAlign.End, hyphens = Hyphens.Auto diff --git a/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt index 5937f5f03..dcea19c49 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt @@ -40,6 +40,7 @@ import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.checkTimestampFormat import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.dialogs.ConfirmationDialog import helium314.keyboard.settings.NextScreenIcon import helium314.keyboard.settings.SettingsContainer @@ -83,7 +84,7 @@ fun AdvancedSettingsScreen( val activity = ctx.getActivity() as? SettingsActivity val prefChanged = (activity?.prefChanged ?: MutableStateFlow(0)).collectAsState() - val fineTuneAdvancedExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_ADVANCED, Defaults.PREF_EXPAND_FINE_TUNE_ADVANCED) + val fineTuneAdvancedExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_ADVANCED, Defaults.PREF_EXPAND_FINE_TUNE_ADVANCED) val items = remember(fineTuneAdvancedExpanded, prefChanged.value) { listOfNotNull( diff --git a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt index 7269cedb1..4a0e4c82c 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt @@ -23,6 +23,7 @@ import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.preferences.ExpandablePreference import helium314.keyboard.settings.preferences.ListPreference import helium314.keyboard.settings.SettingsWithoutKey @@ -70,10 +71,10 @@ fun AppearanceScreen( val splitEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE) || prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, Defaults.PREF_ENABLE_SPLIT_KEYBOARD) - val fineTuneThemeExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_THEME, Defaults.PREF_EXPAND_FINE_TUNE_THEME) - val fineTuneKeyPreviewsExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_KEY_PREVIEWS, Defaults.PREF_EXPAND_FINE_TUNE_KEY_PREVIEWS) - val fineTuneKeyboardLayoutExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_KEYBOARD_LAYOUT, Defaults.PREF_EXPAND_FINE_TUNE_KEYBOARD_LAYOUT) - val fineTuneFoldableExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_FOLDABLE, Defaults.PREF_EXPAND_FINE_TUNE_FOLDABLE) + val fineTuneThemeExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_THEME, Defaults.PREF_EXPAND_FINE_TUNE_THEME) + val fineTuneKeyPreviewsExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_KEY_PREVIEWS, Defaults.PREF_EXPAND_FINE_TUNE_KEY_PREVIEWS) + val fineTuneKeyboardLayoutExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_KEYBOARD_LAYOUT, Defaults.PREF_EXPAND_FINE_TUNE_KEYBOARD_LAYOUT) + val fineTuneFoldableExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_FOLDABLE, Defaults.PREF_EXPAND_FINE_TUNE_FOLDABLE) val items = remember( dayNightMode, themeBordersEnabled, popupOnEnabled, showHintsEnabled, narrowGapsEnabled, splitEnabled, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/GestureTypingScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/GestureTypingScreen.kt index d4830f95e..2e0fa7e08 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/GestureTypingScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/GestureTypingScreen.kt @@ -18,6 +18,7 @@ import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.Setting import helium314.keyboard.settings.SearchSettingsScreen import helium314.keyboard.settings.SettingsActivity @@ -73,8 +74,8 @@ fun GestureTypingScreen( val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(true) val missingDict = enabledSubtypes.any { isMainDictionaryMissing(ctx, it.locale()) } - val fineTuneGestureExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_GESTURE, Defaults.PREF_EXPAND_FINE_TUNE_GESTURE) - val fineTuneSwipesExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_SWIPES, Defaults.PREF_EXPAND_FINE_TUNE_SWIPES) + val fineTuneGestureExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_GESTURE, Defaults.PREF_EXPAND_FINE_TUNE_GESTURE) + val fineTuneSwipesExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_SWIPES, Defaults.PREF_EXPAND_FINE_TUNE_SWIPES) val items = remember( hasGestureLib, gestureEnabled, gestureFloatingPreviewEnabled, previewTrailEnabled, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt index 6768bf8f0..6efdae732 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt @@ -35,6 +35,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_MAIN import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_MORE import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL import helium314.keyboard.keyboard.internal.keyboard_parser.morePopupKeysResId +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.NextScreenIcon import helium314.keyboard.settings.SearchSettingsScreen import helium314.keyboard.settings.Setting @@ -67,9 +68,9 @@ fun PreferencesScreen( val showHints = prefs.getBoolean(Settings.PREF_SHOW_HINTS, Defaults.PREF_SHOW_HINTS) val clipboardHistoryEnabled = prefs.getBoolean(Settings.PREF_ENABLE_CLIPBOARD_HISTORY, Defaults.PREF_ENABLE_CLIPBOARD_HISTORY) - val fineTuneHapticsExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_HAPTICS, Defaults.PREF_EXPAND_FINE_TUNE_HAPTICS) - val fineTuneAdditionalKeysExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_ADDITIONAL_KEYS, Defaults.PREF_EXPAND_FINE_TUNE_ADDITIONAL_KEYS) - val fineTuneClipboardExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_CLIPBOARD, Defaults.PREF_EXPAND_FINE_TUNE_CLIPBOARD) + val fineTuneHapticsExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_HAPTICS, Defaults.PREF_EXPAND_FINE_TUNE_HAPTICS) + val fineTuneAdditionalKeysExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_ADDITIONAL_KEYS, Defaults.PREF_EXPAND_FINE_TUNE_ADDITIONAL_KEYS) + val fineTuneClipboardExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_CLIPBOARD, Defaults.PREF_EXPAND_FINE_TUNE_CLIPBOARD) val items = remember( hasVibrator, vibrateOn, hasAmplitude, showNumberRow, showNumberRowInSymbols, showHints, clipboardHistoryEnabled, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/SuggestionsScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/SuggestionsScreen.kt index cab846b78..674211adc 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/SuggestionsScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/SuggestionsScreen.kt @@ -43,6 +43,7 @@ import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.ToolbarMode import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.SearchSettingsScreen import helium314.keyboard.settings.Setting import helium314.keyboard.settings.SettingsActivity @@ -70,7 +71,7 @@ fun SuggestionsScreen( val suggestionsVisible = Settings.readToolbarMode(prefs) in setOf(ToolbarMode.SUGGESTION_STRIP, ToolbarMode.EXPANDABLE) val suggestionsEnabled = suggestionsVisible && prefs.getBoolean(Settings.PREF_SHOW_SUGGESTIONS, Defaults.PREF_SHOW_SUGGESTIONS) val bigramPredictionsEnabled = prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, Defaults.PREF_BIGRAM_PREDICTIONS) - val fineTunePredictionExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_PREDICTION, Defaults.PREF_EXPAND_FINE_TUNE_PREDICTION) + val fineTunePredictionExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_PREDICTION, Defaults.PREF_EXPAND_FINE_TUNE_PREDICTION) val items = remember(suggestionsVisible, suggestionsEnabled, bigramPredictionsEnabled, fineTunePredictionExpanded, prefChanged.value) { listOf( diff --git a/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt index abcc9279d..0a6e2bf92 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt @@ -26,6 +26,7 @@ import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.SearchSettingsScreen import helium314.keyboard.settings.Setting import helium314.keyboard.settings.SettingsActivity @@ -49,7 +50,7 @@ fun TextCorrectionScreen( val prefChanged = (activity?.prefChanged ?: MutableStateFlow(0)).collectAsState() val autocorrectEnabled = prefs.getBoolean(Settings.PREF_AUTO_CORRECTION, Defaults.PREF_AUTO_CORRECTION) - val fineTuneAutocorrectExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_AUTOCORRECT, Defaults.PREF_EXPAND_FINE_TUNE_AUTOCORRECT) + val fineTuneAutocorrectExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_AUTOCORRECT, Defaults.PREF_EXPAND_FINE_TUNE_AUTOCORRECT) val items = remember(autocorrectEnabled, fineTuneAutocorrectExpanded, prefChanged.value) { listOf( diff --git a/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt index 0abc70cb7..0aee151b5 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt @@ -42,6 +42,7 @@ import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.isCustomAiKeyConfigured import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.upgradeToolbarPrefs +import helium314.keyboard.settings.fineTuneExpanded import helium314.keyboard.settings.SearchSettingsScreen import helium314.keyboard.settings.Setting import helium314.keyboard.settings.SettingsActivity @@ -71,7 +72,7 @@ fun ToolbarScreen( val toolbarMode = Settings.readToolbarMode(prefs) val isSplitToolbar = prefs.getBoolean(Settings.PREF_SPLIT_TOOLBAR, Defaults.PREF_SPLIT_TOOLBAR) - val fineTuneToolbarExpanded = prefs.getBoolean(Settings.PREF_EXPAND_FINE_TUNE_TOOLBAR, Defaults.PREF_EXPAND_FINE_TUNE_TOOLBAR) + val fineTuneToolbarExpanded = fineTuneExpanded(prefs, Settings.PREF_EXPAND_FINE_TUNE_TOOLBAR, Defaults.PREF_EXPAND_FINE_TUNE_TOOLBAR) val items = remember(toolbarMode, isSplitToolbar, fineTuneToolbarExpanded, prefChanged.value) { listOf( diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0fa138bf8..aa40e1d5f 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1756,4 +1756,6 @@ Ermöglicht lokale GGUF / llama.cpp-Inferenz für Korrekturlesen und Umschreiben 10 Minuten Verknüpfungen zur automatischen Vervollständigung Schlagen Sie Teilübereinstimmungen für Text- und Bildverknüpfungen ohne Präfix vor, nachdem Sie mehr als zwei Zeichen eingegeben haben (Verknüpfungen mit Präfix werden auch automatisch vervollständigt). + + Auf seiner Seite zeigen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 74684389e..0207ad45d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2455,4 +2455,6 @@ New dictionary: Normal Active style: %s None (Plain text) + + Show in its screen diff --git a/app/src/test/java/helium314/keyboard/settings/SettingsJumpTest.kt b/app/src/test/java/helium314/keyboard/settings/SettingsJumpTest.kt new file mode 100644 index 000000000..d40a45287 --- /dev/null +++ b/app/src/test/java/helium314/keyboard/settings/SettingsJumpTest.kt @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.settings + +import org.junit.Assert.assertEquals +import org.junit.Test + +class SettingsJumpTest { + // a screen: a title (Int), settings, a fine-tune section with two settings, another closed section + private val closedScreen = listOf(1, "a", "expand_fine_tune_x", "b", "expand_fine_tune_y") + private val allOpenScreen = listOf(1, "a", "expand_fine_tune_x", "x1", "x2", "b", "expand_fine_tune_y", "y1") + + private fun shown(settings: List) = settings.filterIsInstance() + + @Test + fun visibleSettingIsShownWithoutOpeningAnything() { + assertEquals(JumpStep.Show(emptySet()), nextJumpStep("b", closedScreen, shown(closedScreen), emptySet()) { false }) + } + + @Test + fun settingInAClosedSectionOpensTheClosedSectionsThenKeepsOnlyItsOwn() { + val step = nextJumpStep("y1", closedScreen, shown(closedScreen), emptySet()) { false } + assertEquals(JumpStep.OpenSections(setOf("expand_fine_tune_x", "expand_fine_tune_y")), step) + // the screen now shows the settings of both sections + val opened = (step as JumpStep.OpenSections).sections + assertEquals(JumpStep.Show(setOf("expand_fine_tune_y")), nextJumpStep("y1", allOpenScreen, shown(allOpenScreen), opened) { false }) + } + + @Test + fun sectionsTheUserOpenedAreNotTouched() { + // x is open as saved by the user, only y is opened for the jump + val screen = listOf(1, "a", "expand_fine_tune_x", "x1", "x2", "b", "expand_fine_tune_y") + val step = nextJumpStep("y1", screen, shown(screen), emptySet()) { it == "expand_fine_tune_x" } + assertEquals(JumpStep.OpenSections(setOf("expand_fine_tune_y")), step) + assertEquals(JumpStep.Show(setOf("expand_fine_tune_y")), + nextJumpStep("y1", allOpenScreen, shown(allOpenScreen), setOf("expand_fine_tune_y")) { it == "expand_fine_tune_x" }) + } + + @Test + fun settingThatIsHiddenForAnotherReasonGivesUp() { + // not in any section, e.g. it needs another setting to be on + val step = nextJumpStep("z", closedScreen, shown(closedScreen), emptySet()) { false } + assertEquals(JumpStep.OpenSections(setOf("expand_fine_tune_x", "expand_fine_tune_y")), step) + assertEquals(JumpStep.GiveUp, nextJumpStep("z", allOpenScreen, shown(allOpenScreen), (step as JumpStep.OpenSections).sections) { false }) + // and right away if there are no closed sections + assertEquals(JumpStep.GiveUp, nextJumpStep("z", listOf("a", "b"), listOf("a", "b"), emptySet()) { false }) + } +}