diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java index 76a6d56df..e4849c259 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java @@ -482,7 +482,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setOneHandedModeEnabled(boolean enabled) { - if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) { + setOneHandedModeEnabled(enabled, false); + } + + public void setOneHandedModeEnabled(boolean enabled, boolean force) { + if (!force && mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) { return; } final Settings settings = Settings.getInstance(); @@ -515,6 +519,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { !settings.getCurrent().mIsSplitKeyboardEnabled, mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE ); + setOneHandedModeEnabled(settings.getCurrent().mOneHandedModeEnabled, true); reloadKeyboard(); } diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index 57d060f2e..acbc922c6 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -18,6 +18,7 @@ import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.SettingsSubtype import helium314.keyboard.latin.settings.SettingsSubtype.Companion.toSettingsSubtype +import helium314.keyboard.latin.settings.createPrefKeyForBooleanSettings import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils.USER_DICTIONARY_SUFFIX @@ -562,6 +563,45 @@ fun checkVersionUpgrade(context: Context) { prefs.edit().putString(it.key, newValue).apply() } } + if (oldVersion <= 3101) { + val e = prefs.edit() + prefs.all.toMap().forEach { (key, value) -> + if (key == "side_padding_scale") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 0, 2), value as Float) + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 2, 2), value) + } else if (key == "side_padding_scale_landscape") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 1, 2), value as Float) + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 3, 2), value) + } else if (key == "bottom_padding_scale") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, 0, 1), value as Float) + } else if (key == "bottom_padding_scale_landscape") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, 1, 1), value as Float) + } else if (key == "split_spacer_scale") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, 0, 1), value as Float) + } else if (key == "split_spacer_scale_landscape") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, 1, 1), value as Float) + } else if (key == "one_handed_mode_enabled_p_true") { + e.putBoolean(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_MODE_PREFIX, 0, 2), value as Boolean) + } else if (key == "one_handed_mode_enabled_p_false") { + e.putBoolean(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_MODE_PREFIX, 1, 2), value as Boolean) + } else if (key == "one_handed_mode_scale_p_true") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_SCALE_PREFIX, 0, 2), value as Float) + } else if (key == "one_handed_mode_scale_p_false") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_SCALE_PREFIX, 1, 2), value as Float) + } else if (key == "one_handed_mode_gravity_p_true") { + e.putInt(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_GRAVITY_PREFIX, 0, 2), value as Int) + } else if (key == "one_handed_mode_gravity_p_false") { + e.putInt(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_GRAVITY_PREFIX, 1, 2), value as Int) + } else if (key == "keyboard_height_scale") { + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, 1, 1), value as Float) + e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, 1, 1), value) + } else { + return@forEach + } + e.remove(key) + } + e.apply() + } upgradeToolbarPrefs(prefs) LayoutUtilsCustom.onLayoutFileChanged() // just to be sure prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) } diff --git a/app/src/main/java/helium314/keyboard/latin/KeyboardWrapperView.kt b/app/src/main/java/helium314/keyboard/latin/KeyboardWrapperView.kt index 35a689740..ab5998348 100644 --- a/app/src/main/java/helium314/keyboard/latin/KeyboardWrapperView.kt +++ b/app/src/main/java/helium314/keyboard/latin/KeyboardWrapperView.kt @@ -78,12 +78,13 @@ class KeyboardWrapperView @JvmOverloads constructor( val changePercent = 2 * sign * (x - motionEvent.rawX) / context.resources.displayMetrics.density if (abs(changePercent) < 1) return@setOnTouchListener true x = motionEvent.rawX - val oldScale = Settings.readOneHandedModeScale(context.prefs(), Settings.getValues().mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE) + val landscape = Settings.getValues().mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE + val split = Settings.getValues().mIsSplitKeyboardEnabled + val oldScale = Settings.readOneHandedModeScale(context.prefs(), landscape, split) val newScale = (oldScale + changePercent / 100f).coerceAtMost(2.5f).coerceAtLeast(0.5f) if (newScale == oldScale) return@setOnTouchListener true Settings.getInstance().writeOneHandedModeScale(newScale) - oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload - KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true) + KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true, true) } else -> x = 0f } diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt index f8a91e0ea..98e93a64f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt @@ -44,6 +44,7 @@ object Defaults { LayoutType.CLIPBOARD_BOTTOM -> "clip_bottom_row" } + private const val DEFAULT_SIZE_SCALE = 1.0f // 100% const val PREF_THEME_STYLE = KeyboardTheme.STYLE_MATERIAL const val PREF_ICON_STYLE = KeyboardTheme.STYLE_MATERIAL const val PREF_THEME_COLORS = KeyboardTheme.THEME_LIGHT @@ -80,15 +81,16 @@ object Defaults { "hu${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN:qwerty" const val PREF_ENABLE_SPLIT_KEYBOARD = false const val PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = false - const val PREF_SPLIT_SPACER_SCALE = SettingsValues.DEFAULT_SIZE_SCALE - const val PREF_SPLIT_SPACER_SCALE_LANDSCAPE = SettingsValues.DEFAULT_SIZE_SCALE - const val PREF_KEYBOARD_HEIGHT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE - const val PREF_BOTTOM_PADDING_SCALE = SettingsValues.DEFAULT_SIZE_SCALE - const val PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = 0f - const val PREF_SIDE_PADDING_SCALE = 0f - const val PREF_SIDE_PADDING_SCALE_LANDSCAPE = 0f - const val PREF_FONT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE - const val PREF_EMOJI_FONT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE + @JvmField + val PREF_SPLIT_SPACER_SCALE = Array(2) { DEFAULT_SIZE_SCALE } + @JvmField + val PREF_KEYBOARD_HEIGHT_SCALE = Array(2) { DEFAULT_SIZE_SCALE } + @JvmField + val PREF_BOTTOM_PADDING_SCALE = arrayOf(DEFAULT_SIZE_SCALE, 0f) + @JvmField + val PREF_SIDE_PADDING_SCALE = Array(4) { 0f } + const val PREF_FONT_SCALE = DEFAULT_SIZE_SCALE + const val PREF_EMOJI_FONT_SCALE = DEFAULT_SIZE_SCALE const val PREF_EMOJI_KEY_FIT = true const val PREF_EMOJI_SKIN_TONE = "" const val PREF_SPACE_HORIZONTAL_SWIPE = "move_cursor" diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 87449c563..ea6e8f59f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -87,13 +87,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_ADDITIONAL_SUBTYPES = "additional_subtypes"; public static final String PREF_ENABLE_SPLIT_KEYBOARD = "split_keyboard"; public static final String PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = "split_keyboard_landscape"; - public static final String PREF_SPLIT_SPACER_SCALE = "split_spacer_scale"; - public static final String PREF_SPLIT_SPACER_SCALE_LANDSCAPE = "split_spacer_scale_landscape"; - public static final String PREF_KEYBOARD_HEIGHT_SCALE = "keyboard_height_scale"; - public static final String PREF_BOTTOM_PADDING_SCALE = "bottom_padding_scale"; - public static final String PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = "bottom_padding_scale_landscape"; - public static final String PREF_SIDE_PADDING_SCALE = "side_padding_scale"; - public static final String PREF_SIDE_PADDING_SCALE_LANDSCAPE = "side_padding_scale_landscape"; + public static final String PREF_SPLIT_SPACER_SCALE_PREFIX = "split_spacer_scale"; + public static final String PREF_KEYBOARD_HEIGHT_SCALE_PREFIX = "keyboard_height_scale"; + public static final String PREF_BOTTOM_PADDING_SCALE_PREFIX = "bottom_padding_scale"; + public static final String PREF_SIDE_PADDING_SCALE_PREFIX = "side_padding_scale"; public static final String PREF_FONT_SCALE = "font_scale"; public static final String PREF_EMOJI_FONT_SCALE = "emoji_font_scale"; public static final String PREF_EMOJI_KEY_FIT = "emoji_key_fit"; @@ -126,10 +123,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_USE_APPS = "use_apps"; public static final String PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = "long_press_symbols_for_numpad"; - // one-handed mode gravity, enablement and scale, stored separately per orientation - public static final String PREF_ONE_HANDED_MODE_PREFIX = "one_handed_mode_enabled_p_"; - public static final String PREF_ONE_HANDED_GRAVITY_PREFIX = "one_handed_mode_gravity_p_"; - public static final String PREF_ONE_HANDED_SCALE_PREFIX = "one_handed_mode_scale_p_"; + public static final String PREF_ONE_HANDED_MODE_PREFIX = "one_handed_mode_enabled"; + public static final String PREF_ONE_HANDED_GRAVITY_PREFIX = "one_handed_mode_gravity"; + public static final String PREF_ONE_HANDED_SCALE_PREFIX = "one_handed_mode_scale"; public static final String PREF_SHOW_NUMBER_ROW = "show_number_row"; public static final String PREF_LOCALIZED_NUMBER_ROW = "localized_number_row"; @@ -365,31 +361,43 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, Defaults.PREF_SHOW_SETUP_WIZARD_ICON); } - public static boolean readOneHandedModeEnabled(final SharedPreferences prefs, final boolean isLandscape) { - return prefs.getBoolean(PREF_ONE_HANDED_MODE_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_MODE); + public static boolean readOneHandedModeEnabled(final SharedPreferences prefs, final boolean landscape, final boolean split) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_MODE_PREFIX, index, 2); + return prefs.getBoolean(key, Defaults.PREF_ONE_HANDED_MODE); } public void writeOneHandedModeEnabled(final boolean enabled) { - mPrefs.edit().putBoolean(PREF_ONE_HANDED_MODE_PREFIX + - (mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), enabled).apply(); + final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE; + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_MODE_PREFIX, index, 2); + mPrefs.edit().putBoolean(key, enabled).apply(); } - public static float readOneHandedModeScale(final SharedPreferences prefs, final boolean isLandscape) { - return prefs.getFloat(PREF_ONE_HANDED_SCALE_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_SCALE); + public static float readOneHandedModeScale(final SharedPreferences prefs, final boolean landscape, final boolean split) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_SCALE_PREFIX, index, 2); + return prefs.getFloat(key, Defaults.PREF_ONE_HANDED_SCALE); } public void writeOneHandedModeScale(final Float scale) { - mPrefs.edit().putFloat(PREF_ONE_HANDED_SCALE_PREFIX + - (mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), scale).apply(); + final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE; + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_SCALE_PREFIX, index, 2); + mPrefs.edit().putFloat(key, scale).apply(); } - public static int readOneHandedModeGravity(final SharedPreferences prefs, final boolean isLandscape) { - return prefs.getInt(PREF_ONE_HANDED_GRAVITY_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_GRAVITY); + public static int readOneHandedModeGravity(final SharedPreferences prefs, final boolean landscape, final boolean split) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_GRAVITY_PREFIX, index, 2); + return prefs.getInt(key, Defaults.PREF_ONE_HANDED_GRAVITY); } public void writeOneHandedModeGravity(final int gravity) { - mPrefs.edit().putInt(PREF_ONE_HANDED_GRAVITY_PREFIX + - (mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), gravity).apply(); + final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE; + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled); + final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_GRAVITY_PREFIX, index, 2); + mPrefs.edit().putInt(key, gravity).apply(); } public void writeSplitKeyboardEnabled(final boolean enabled, final boolean isLandscape) { @@ -402,21 +410,32 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(pref, isLandscape ? Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE : Defaults.PREF_ENABLE_SPLIT_KEYBOARD); } - public static float readSplitSpacerScale(final SharedPreferences prefs, final boolean isLandscape) { - final String pref = isLandscape ? PREF_SPLIT_SPACER_SCALE_LANDSCAPE : PREF_SPLIT_SPACER_SCALE; - return prefs.getFloat(pref, isLandscape ? Defaults.PREF_SPLIT_SPACER_SCALE_LANDSCAPE : Defaults.PREF_SPLIT_SPACER_SCALE); + public static float readSplitSpacerScale(final SharedPreferences prefs, final boolean landscape) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape); + final Float[] defaults = Defaults.PREF_SPLIT_SPACER_SCALE; + final float defaultValue = defaults[index]; + return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_SPLIT_SPACER_SCALE_PREFIX, index, 1), defaultValue); } public static float readBottomPaddingScale(final SharedPreferences prefs, final boolean landscape) { - if (landscape) - return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, Defaults.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE); - return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE, Defaults.PREF_BOTTOM_PADDING_SCALE); + final int index = SettingsKt.findIndexOfDefaultSetting(landscape); + final Float[] defaults = Defaults.PREF_BOTTOM_PADDING_SCALE; + final float defaultValue = defaults[index]; + return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_BOTTOM_PADDING_SCALE_PREFIX, index, 1), defaultValue); } - public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape) { - if (landscape) - return prefs.getFloat(PREF_SIDE_PADDING_SCALE_LANDSCAPE, Defaults.PREF_SIDE_PADDING_SCALE_LANDSCAPE); - return prefs.getFloat(PREF_SIDE_PADDING_SCALE, Defaults.PREF_SIDE_PADDING_SCALE); + public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape, final boolean split) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split); + final Float[] defaults = Defaults.PREF_SIDE_PADDING_SCALE; + final float defaultValue = defaults[index]; + return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_SIDE_PADDING_SCALE_PREFIX, index, 2), defaultValue); + } + + public static float readHeightScale(final SharedPreferences prefs, final boolean landscape) { + final int index = SettingsKt.findIndexOfDefaultSetting(landscape); + final Float[] defaults = Defaults.PREF_KEYBOARD_HEIGHT_SCALE; + final float defaultValue = defaults[index]; + return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, index, 1), defaultValue); } public static boolean readHasHardwareKeyboard(final Configuration conf) { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.kt b/app/src/main/java/helium314/keyboard/latin/settings/Settings.kt index 5a472d058..d532a16a3 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.kt @@ -14,3 +14,13 @@ fun customIconIds(context: Context, prefs: SharedPreferences) = customIconNames( val id = runCatching { context.resources.getIdentifier(entry.value, "drawable", context.packageName) }.getOrNull() id?.let { entry.key to it } } + +/** Derive an index from a number of boolean [settingValues], used to access the matching default value in a defaults arraY */ +fun findIndexOfDefaultSetting(vararg settingValues: Boolean): Int { + var i = -1 + return settingValues.sumOf { i++; if (it) 1.shl(i) else 0 } +} + +/** Create pref key that is derived from a [number] of boolean conditions. The [index] is as created by [findIndexOfDefaultSetting]. */ +fun createPrefKeyForBooleanSettings(prefix: String, index: Int, number: Int): String = + "${prefix}_${Array(number) { index.shr(it) % 2 == 1 }.joinToString("_")}" diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index 26ae4658c..689a63700 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -42,8 +42,6 @@ import java.util.Locale; */ // Non-final for testing via mock library. public class SettingsValues { - public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100% - // From resources: public final SpacingAndPunctuations mSpacingAndPunctuations; public final long mDoubleSpacePeriodTimeout; @@ -242,7 +240,7 @@ public class SettingsValues { mSecondaryStripVisible = mToolbarMode != ToolbarMode.HIDDEN || ! mToolbarHidingGlobal; mIncognitoModeEnabled = prefs.getBoolean(Settings.PREF_ALWAYS_INCOGNITO_MODE, Defaults.PREF_ALWAYS_INCOGNITO_MODE) || mInputAttributes.mNoLearning || mInputAttributes.mIsPasswordField; - mKeyboardHeightScale = prefs.getFloat(Settings.PREF_KEYBOARD_HEIGHT_SCALE, Defaults.PREF_KEYBOARD_HEIGHT_SCALE); + mKeyboardHeightScale = Settings.readHeightScale(prefs, isLandscape); mSpaceSwipeHorizontal = Settings.readHorizontalSpaceSwipe(prefs); mSpaceSwipeVertical = Settings.readVerticalSpaceSwipe(prefs); mLanguageSwipeDistance = prefs.getInt(Settings.PREF_LANGUAGE_SWIPE_DISTANCE, Defaults.PREF_LANGUAGE_SWIPE_DISTANCE); @@ -255,11 +253,11 @@ public class SettingsValues { mClipboardHistoryEnabled = prefs.getBoolean(Settings.PREF_ENABLE_CLIPBOARD_HISTORY, Defaults.PREF_ENABLE_CLIPBOARD_HISTORY); mClipboardHistoryRetentionTime = prefs.getInt(Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, Defaults.PREF_CLIPBOARD_HISTORY_RETENTION_TIME); - mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, isLandscape); - mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, isLandscape); + mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, isLandscape, mIsSplitKeyboardEnabled); + mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, isLandscape, mIsSplitKeyboardEnabled); if (mOneHandedModeEnabled) { final float baseScale = res.getFraction(R.fraction.config_one_handed_mode_width, 1, 1); - final float extraScale = Settings.readOneHandedModeScale(prefs, isLandscape); + final float extraScale = Settings.readOneHandedModeScale(prefs, isLandscape, mIsSplitKeyboardEnabled); mOneHandedModeScale = 1 - (1 - baseScale) * extraScale; } else mOneHandedModeScale = 1f; @@ -282,7 +280,7 @@ public class SettingsValues { ); mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled); mBottomPaddingScale = Settings.readBottomPaddingScale(prefs, isLandscape); - mSidePaddingScale = Settings.readSidePaddingScale(prefs, isLandscape); + mSidePaddingScale = Settings.readSidePaddingScale(prefs, isLandscape, mIsSplitKeyboardEnabled); mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, Defaults.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD); mAutoShowToolbar = mToolbarMode == ToolbarMode.EXPANDABLE && prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, Defaults.PREF_AUTO_SHOW_TOOLBAR); mAutoHideToolbar = mSuggestionsEnabledPerUserSettings && prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, Defaults.PREF_AUTO_HIDE_TOOLBAR); diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt index d38f598ac..28c8dcc6f 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt @@ -109,7 +109,8 @@ private fun DictionaryDetails(dict: File) { DeleteButton { showDeleteDialog = true } ExpandButton { showDetails = !showDetails } } - AnimatedVisibility(showDetails, enter = fadeIn(), exit = fadeOut()) { // default animation looks better, but makes the dialog flash + // default animations look better but make the dialog flash, see also MultiSliderPreference + AnimatedVisibility(showDetails, enter = fadeIn(), exit = fadeOut()) { Text( header.info(LocalConfiguration.current.locale()), style = MaterialTheme.typography.bodyMedium, diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/MultiSliderPreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/MultiSliderPreference.kt new file mode 100644 index 000000000..e991344c9 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/preferences/MultiSliderPreference.kt @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.settings.preferences + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Checkbox +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +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.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import helium314.keyboard.latin.R +import helium314.keyboard.latin.settings.createPrefKeyForBooleanSettings +import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.Theme +import helium314.keyboard.settings.WithSmallTitle +import helium314.keyboard.settings.dialogs.ThreeButtonAlertDialog +import helium314.keyboard.settings.previewDark + +// too specialized for using a more generic dialog +// actual key for each setting is baseKey with one _true/_false appended per dimension (need to keep order!) +// todo: possible adjustments, maybe depending on user feedback +// should dimension checkboxes have any other effect than just showing / hiding sliders? +// one could argue that e.g. when disabling the split checkbox, then split mode should not affect the setting +// store checkbox states? +// if so, per setting or global? +// show a description? currently commented because it could get long, even without showing the variations +// maybe if we store the checkbox state in a setting, we could use it for determining what to show +@Composable +fun MultiSliderPreference( + name: String, + baseKey: String, + dimensions: List, + defaults: Array, + range: ClosedFloatingPointRange, + description: (Float) -> String, + onDone: () -> Unit +) { + if (defaults.size != 1.shl(dimensions.size)) + throw ArithmeticException("defaults size does not match with dimensions, expected ${1.shl(dimensions.size)}, got ${defaults.size}") + var showDialog by remember { mutableStateOf(false) } + //val (_, keys) = remember { createVariantsAndKeys(dimensions, baseKey) } + //val prefs = LocalContext.current.prefs() + Preference( + name = name, + onClick = { showDialog = true }, + //description = keys.mapIndexed { i, it -> description(prefs.getFloat(it, defaults[i])) }.joinToString(" $SPLIT ") + ) + if (showDialog) + MultiSliderDialog( + onDismissRequest = { showDialog = false }, + title = { Text(name) }, + baseKey = baseKey, + onDone = onDone, + defaultValues = defaults, + range = range, + dimensions = dimensions, + positionString = description + ) +} + +// SliderDialog, but for multiple sliders with same range, each with a different setting and title +@Composable +private fun MultiSliderDialog( + onDismissRequest: () -> Unit, + title: @Composable () -> Unit, + baseKey: String, + onDone: () -> Unit, + defaultValues: Array, + range: ClosedFloatingPointRange, + dimensions: List, + modifier: Modifier = Modifier, + positionString: (Float) -> String, +) { + val (variants, keys) = createVariantsAndKeys(dimensions, baseKey) + var checked by remember { mutableStateOf(List(variants.size) { true }) } + val prefs = LocalContext.current.prefs() + val done = remember { mutableMapOf Unit>() } + + ThreeButtonAlertDialog( + onDismissRequest = onDismissRequest, + onConfirmed = { done.values.forEach { it.invoke() }; onDone() }, + modifier = modifier, + title = title, + content = { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.bodyLarge + ) { + val state = rememberScrollState() + Column(Modifier.verticalScroll(state)) { + if (dimensions.size > 1) { + dimensions.forEachIndexed { i, dimension -> + DimensionCheckbox(checked[i], dimension) { + checked = checked.mapIndexed { j, c -> if (i == j) it else c } + } + } + } + variants.forEachIndexed { i, variant -> + val key = keys[i] + var sliderPosition by remember { mutableFloatStateOf(prefs.getFloat(key, defaultValues[i])) } + if (!done.contains(variant)) + done[variant] = { + if (sliderPosition == defaultValues[i]) + prefs.edit().remove(key).apply() + else + prefs.edit().putFloat(key, sliderPosition).apply() + } + val forbiddenDimensions = dimensions.filterIndexed { index, _ -> !checked[index] } + val visible = variant.split(SPLIT).none { it in forbiddenDimensions } + // default animations make the dialog flash (see also DictionaryDialog) + AnimatedVisibility(visible, exit = fadeOut(), enter = fadeIn()) { + WithSmallTitle(variant.ifEmpty { stringResource(R.string.button_default) }) { + Slider( + value = sliderPosition, + onValueChange = { sliderPosition = it }, + valueRange = range, + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text(positionString(sliderPosition)) + TextButton({ sliderPosition = defaultValues[i] }) { Text(stringResource(R.string.button_default)) } + } + Spacer(Modifier.height(6.dp)) + } + } + } + } + } + }, + ) +} + +@Composable +private fun DimensionCheckbox(checked: Boolean, dimension: String, onCheckedChange: (Boolean) -> Unit) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().clickable { onCheckedChange(!checked) } + ) { + Checkbox( + checked = checked, + onCheckedChange = { onCheckedChange(it) } + ) + Text(dimension) + } +} + +private fun createVariantsAndKeys(dimensions: List, baseKey: String): Pair, List> { + val variants = mutableListOf("") + val keys = mutableListOf(createPrefKeyForBooleanSettings(baseKey, 0, dimensions.size)) + var i = 1 + dimensions.forEach { dimension -> + variants.toList().forEach { variant -> + if (variant.isEmpty()) variants.add(dimension) + else variants.add(variant + SPLIT + dimension) + keys.add(createPrefKeyForBooleanSettings(baseKey, i, dimensions.size)) + i++ + } + } + return variants to keys +} + +private const val SPLIT = " / " + +@Preview +@Composable +private fun Preview() { + Theme(previewDark) { + MultiSliderDialog( + onDismissRequest = { }, + onDone = { }, + positionString = { "${it.toInt()}%"}, + defaultValues = Array(8) { 100f - it % 2 * 50f }, + range = 0f..500f, + title = { Text("bottom padding scale") }, + dimensions = listOf("landscape", "unfolded", "split"), + baseKey = "" + ) + } +} 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 06396f1b2..96bdb869f 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt @@ -37,6 +37,7 @@ import helium314.keyboard.settings.dialogs.CustomizeIconsDialog import helium314.keyboard.settings.initPreview import helium314.keyboard.settings.preferences.BackgroundImagePref import helium314.keyboard.settings.preferences.CustomFontPreference +import helium314.keyboard.settings.preferences.MultiSliderPreference import helium314.keyboard.settings.preferences.TextInputPreference import helium314.keyboard.settings.previewDark @@ -65,18 +66,15 @@ fun AppearanceScreen( SettingsWithoutKey.BACKGROUND_IMAGE_LANDSCAPE, R.string.settings_category_miscellaneous, Settings.PREF_ENABLE_SPLIT_KEYBOARD, - if (prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, Defaults.PREF_ENABLE_SPLIT_KEYBOARD)) - Settings.PREF_SPLIT_SPACER_SCALE else null, Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, - if (prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE)) - Settings.PREF_SPLIT_SPACER_SCALE_LANDSCAPE else null, + if (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)) + Settings.PREF_SPLIT_SPACER_SCALE_PREFIX else null, if (prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, Defaults.PREF_THEME_KEY_BORDERS)) Settings.PREF_NARROW_KEY_GAPS else null, - Settings.PREF_KEYBOARD_HEIGHT_SCALE, - Settings.PREF_BOTTOM_PADDING_SCALE, - Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, - Settings.PREF_SIDE_PADDING_SCALE, - Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, + Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, + Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, + Settings.PREF_SIDE_PADDING_SCALE_PREFIX, Settings.PREF_SPACE_BAR_TEXT, SettingsWithoutKey.CUSTOM_FONT, Settings.PREF_FONT_SCALE, @@ -196,11 +194,12 @@ fun createAppearanceSettings(context: Context) = listOf( Setting(context, Settings.PREF_ENABLE_SPLIT_KEYBOARD, R.string.enable_split_keyboard) { SwitchPreference(it, Defaults.PREF_ENABLE_SPLIT_KEYBOARD) { KeyboardSwitcher.getInstance().reloadKeyboard() } }, - Setting(context, Settings.PREF_SPLIT_SPACER_SCALE, R.string.split_spacer_scale) { setting -> - SliderPreference( + Setting(context, Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, R.string.split_spacer_scale) { setting -> + MultiSliderPreference( name = setting.title, - key = setting.key, - default = Defaults.PREF_SPLIT_SPACER_SCALE, + baseKey = setting.key, + dimensions = listOf(stringResource(R.string.landscape)), + defaults = Defaults.PREF_SPLIT_SPACER_SCALE, range = 0.5f..2f, description = { "${(100 * it).toInt()}%" } ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } @@ -208,59 +207,35 @@ fun createAppearanceSettings(context: Context) = listOf( Setting(context, Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, R.string.enable_split_keyboard_landscape) { SwitchPreference(it, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE) { KeyboardSwitcher.getInstance().reloadKeyboard() } }, - Setting(context, Settings.PREF_SPLIT_SPACER_SCALE_LANDSCAPE, R.string.split_spacer_scale_landscape) { setting -> - SliderPreference( - name = setting.title, - key = setting.key, - default = Defaults.PREF_SPLIT_SPACER_SCALE_LANDSCAPE, - range = 0.5f..2f, - description = { "${(100 * it).toInt()}%" } - ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } - }, Setting(context, Settings.PREF_NARROW_KEY_GAPS, R.string.prefs_narrow_key_gaps) { SwitchPreference(it, Defaults.PREF_NARROW_KEY_GAPS) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } }, - Setting(context, Settings.PREF_KEYBOARD_HEIGHT_SCALE, R.string.prefs_keyboard_height_scale) { setting -> - SliderPreference( + Setting(context, Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, R.string.prefs_keyboard_height_scale) { setting -> + MultiSliderPreference( name = setting.title, - key = setting.key, - default = Defaults.PREF_KEYBOARD_HEIGHT_SCALE, + baseKey = setting.key, + dimensions = listOf(stringResource(R.string.landscape)), + defaults = Defaults.PREF_KEYBOARD_HEIGHT_SCALE, range = 0.3f..1.5f, description = { "${(100 * it).toInt()}%" } ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } }, - Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE, R.string.prefs_bottom_padding_scale) { setting -> - SliderPreference( + Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, R.string.prefs_bottom_padding_scale) { setting -> + MultiSliderPreference( name = setting.title, - key = setting.key, - default = Defaults.PREF_BOTTOM_PADDING_SCALE, + baseKey = setting.key, + dimensions = listOf(stringResource(R.string.landscape)), + defaults = Defaults.PREF_BOTTOM_PADDING_SCALE, range = 0f..5f, description = { "${(100 * it).toInt()}%" } ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } }, - Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, R.string.prefs_bottom_padding_scale_landscape) { setting -> - SliderPreference( + Setting(context, Settings.PREF_SIDE_PADDING_SCALE_PREFIX, R.string.prefs_side_padding_scale) { setting -> + MultiSliderPreference( name = setting.title, - key = setting.key, - default = Defaults.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, - range = 0f..5f, - description = { "${(100 * it).toInt()}%" } - ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } - }, - Setting(context, Settings.PREF_SIDE_PADDING_SCALE, R.string.prefs_side_padding_scale) { setting -> - SliderPreference( - name = setting.title, - key = setting.key, - default = Defaults.PREF_SIDE_PADDING_SCALE, - range = 0f..3f, - description = { "${(100 * it).toInt()}%" } - ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } - }, - Setting(context, Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, R.string.prefs_side_padding_scale_landscape) { setting -> - SliderPreference( - name = setting.title, - key = setting.key, - default = Defaults.PREF_SIDE_PADDING_SCALE_LANDSCAPE, + baseKey = setting.key, + dimensions = listOf(stringResource(R.string.landscape), stringResource(R.string.split)), + defaults = Defaults.PREF_SIDE_PADDING_SCALE, range = 0f..3f, description = { "${(100 * it).toInt()}%" } ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 31d49c76b..db2b22b83 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,7 +51,7 @@ Enable split keyboard (landscape) Split distance - + Split distance (landscape) Switch to other input methods @@ -357,12 +357,12 @@ Keyboard height scale Bottom padding scale - + Bottom padding scale (landscape) Side padding scale - + Side padding scale (landscape) Keyboard font scale @@ -895,4 +895,6 @@ New dictionary: Invalid name Custom subtype + + Landscape