From 8932fc84e130e44421eaefdb7e2ffe2e077ce144 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 29 Mar 2025 12:36:07 +0100 Subject: [PATCH] add timestamp keycode and setting for adjusting format, fixes GH-846 --- .../keyboard_parser/floris/KeyCode.kt | 12 ++++-- .../keyboard/latin/inputlogic/InputLogic.java | 4 ++ .../keyboard/latin/settings/Defaults.kt | 1 + .../keyboard/latin/settings/Settings.java | 1 + .../keyboard/latin/utils/Timestamp.kt | 17 +++++++++ .../preferences/TextInputPreference.kt | 37 +++++++++++++++++++ .../settings/screens/AdvancedScreen.kt | 6 +++ .../settings/screens/AppearanceScreen.kt | 24 ++---------- app/src/main/res/values/strings.xml | 2 + .../keyboard/latin/InputLogicTest.kt | 8 ++++ layouts.md | 2 +- 11 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/helium314/keyboard/latin/utils/Timestamp.kt create mode 100644 app/src/main/java/helium314/keyboard/settings/preferences/TextInputPreference.kt diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index bf5c65f0..e4794250 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -167,6 +167,7 @@ object KeyCode { const val BACK = -10040 const val SELECT_LEFT = -10041 const val SELECT_RIGHT = -10042 + const val TIMESTAMP = -10043 /** to make sure a FlorisBoard code works when reading a JSON layout */ fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) { @@ -182,7 +183,8 @@ object KeyCode { SYMBOL_ALPHA, TOGGLE_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SPLIT_LAYOUT, SHIFT_ENTER, ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP, PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT, - MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK + MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK, + TIMESTAMP -> this // conversion @@ -194,8 +196,12 @@ object KeyCode { else -> throw IllegalStateException("key code $this not yet supported") } - // todo: three are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0 - /** convert a keyCode / codePoint to a KeyEvent.KEYCODE_, fallback to KeyEvent.KEYCODE_UNKNOWN */ + // todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0 + /** + * Convert a keyCode / codePoint to a KeyEvent.KEYCODE_. + * Fallback to KeyEvent.KEYCODE_UNKNOWN. + * To be uses for fake hardware key press. + * */ fun Int.toKeyEventCode(): Int = if (this > 0) when (this.toChar().uppercaseChar()) { '/' -> KeyEvent.KEYCODE_SLASH diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 14d5aa3b..79464dee 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -54,6 +54,7 @@ import helium314.keyboard.latin.utils.RecapitalizeStatus; import helium314.keyboard.latin.utils.ScriptUtils; import helium314.keyboard.latin.utils.StatsUtils; import helium314.keyboard.latin.utils.TextRange; +import helium314.keyboard.latin.utils.TimestampKt; import java.util.ArrayList; import java.util.Locale; @@ -775,6 +776,9 @@ public final class InputLogic { case KeyCode.SPLIT_LAYOUT: KeyboardSwitcher.getInstance().toggleSplitKeyboardMode(); break; + case KeyCode.TIMESTAMP: + mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME)); + break; case KeyCode.VOICE_INPUT: // switching to shortcut IME, shift state, keyboard,... is handled by LatinIME, // {@link KeyboardSwitcher#onEvent(Event)}, or {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. 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 09c0f88d..377e1989 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt @@ -154,6 +154,7 @@ object Defaults { const val PREF_ABC_AFTER_NUMPAD_SPACE = false const val PREF_REMOVE_REDUNDANT_POPUPS = false const val PREF_SPACE_BAR_TEXT = "" + const val PREF_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss" @JvmField val PREF_EMOJI_MAX_SDK = Build.VERSION.SDK_INT const val PREF_EMOJI_RECENT_KEYS = "" 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 55a80bd8..098b51b0 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -164,6 +164,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_ABC_AFTER_NUMPAD_SPACE = "abc_after_numpad_space"; public static final String PREF_REMOVE_REDUNDANT_POPUPS = "remove_redundant_popups"; public static final String PREF_SPACE_BAR_TEXT = "space_bar_text"; + public static final String PREF_TIMESTAMP_FORMAT = "timestamp_format"; // Emoji public static final String PREF_EMOJI_MAX_SDK = "emoji_max_sdk"; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/Timestamp.kt b/app/src/main/java/helium314/keyboard/latin/utils/Timestamp.kt new file mode 100644 index 00000000..ebe2bd0e --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/utils/Timestamp.kt @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.latin.utils + +import android.content.Context +import helium314.keyboard.latin.settings.Defaults +import helium314.keyboard.latin.settings.Settings +import java.text.SimpleDateFormat +import java.util.Calendar + +fun getTimestamp(context: Context): String { + val format = context.prefs().getString(Settings.PREF_TIMESTAMP_FORMAT, Defaults.PREF_TIMESTAMP_FORMAT) + val formatter = runCatching { SimpleDateFormat(format, Settings.getValues().mLocale) }.getOrNull() + ?: SimpleDateFormat(Defaults.PREF_TIMESTAMP_FORMAT, Settings.getValues().mLocale) + return formatter.format(Calendar.getInstance().time) +} + +fun checkTimestampFormat(format: String) = runCatching { SimpleDateFormat(format, Settings.getValues().mLocale) }.isSuccess diff --git a/app/src/main/java/helium314/keyboard/settings/preferences/TextInputPreference.kt b/app/src/main/java/helium314/keyboard/settings/preferences/TextInputPreference.kt new file mode 100644 index 00000000..3bf2ce8e --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/preferences/TextInputPreference.kt @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.settings.preferences + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import helium314.keyboard.keyboard.KeyboardSwitcher +import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.Setting +import helium314.keyboard.settings.dialogs.TextInputDialog + +@Composable +fun TextInputPreference(setting: Setting, default: String, checkTextValid: (String) -> Boolean = { true }) { + var showDialog by rememberSaveable { mutableStateOf(false) } + val prefs = LocalContext.current.prefs() + Preference( + name = setting.title, + onClick = { showDialog = true }, + description = prefs.getString(setting.key, default)?.takeIf { it.isNotEmpty() } + ) + if (showDialog) { + TextInputDialog( + onDismissRequest = { showDialog = false }, + onConfirmed = { + prefs.edit().putString(setting.key, it).apply() + KeyboardSwitcher.getInstance().setThemeNeedsReload() + }, + initialText = prefs.getString(setting.key, default) ?: "", + title = { Text(setting.title) }, + checkTextValid = checkTextValid + ) + } +} 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 c81f3025..badc404d 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AdvancedScreen.kt @@ -29,6 +29,7 @@ import helium314.keyboard.latin.common.splitOnWhitespace import helium314.keyboard.latin.settings.DebugSettings 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.NextScreenIcon import helium314.keyboard.settings.SettingsContainer @@ -45,6 +46,7 @@ import helium314.keyboard.settings.Theme import helium314.keyboard.settings.dialogs.TextInputDialog import helium314.keyboard.settings.preferences.BackupRestorePreference import helium314.keyboard.settings.preferences.LoadGestureLibPreference +import helium314.keyboard.settings.preferences.TextInputPreference import helium314.keyboard.settings.previewDark @Composable @@ -71,6 +73,7 @@ fun AdvancedSettingsScreen( Settings.PREF_ABC_AFTER_CLIP, Settings.PREF_CUSTOM_CURRENCY_KEY, Settings.PREF_MORE_POPUP_KEYS, + Settings.PREF_TIMESTAMP_FORMAT, SettingsWithoutKey.BACKUP_RESTORE, if (BuildConfig.DEBUG || prefs.getBoolean(DebugSettings.PREF_SHOW_DEBUG_SETTINGS, Defaults.PREF_SHOW_DEBUG_SETTINGS)) SettingsWithoutKey.DEBUG_SETTINGS else null, @@ -195,6 +198,9 @@ fun createAdvancedSettings(context: Context) = listOf( Setting(context, SettingsWithoutKey.BACKUP_RESTORE, R.string.backup_restore_title) { BackupRestorePreference(it) }, + Setting(context, Settings.PREF_TIMESTAMP_FORMAT, R.string.timestamp_format_title) { + TextInputPreference(it, Defaults.PREF_TIMESTAMP_FORMAT) { checkTimestampFormat(it) } + }, Setting(context, SettingsWithoutKey.DEBUG_SETTINGS, R.string.debug_settings_title) { Preference( name = it.title, 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 f8a43ca2..2a53c22c 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt @@ -21,6 +21,7 @@ import helium314.keyboard.latin.R import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.Log +import helium314.keyboard.latin.utils.checkTimestampFormat import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.prefs @@ -39,6 +40,7 @@ import helium314.keyboard.settings.dialogs.TextInputDialog import helium314.keyboard.settings.initPreview import helium314.keyboard.settings.preferences.BackgroundImagePref import helium314.keyboard.settings.preferences.CustomFontPreference +import helium314.keyboard.settings.preferences.TextInputPreference import helium314.keyboard.settings.previewDark @Composable @@ -263,26 +265,8 @@ fun createAppearanceSettings(context: Context) = listOf( description = { "${(100 * it).toInt()}%" } ) { KeyboardSwitcher.getInstance().setThemeNeedsReload() } }, - Setting(context, Settings.PREF_SPACE_BAR_TEXT, R.string.prefs_space_bar_text) { setting -> - var showDialog by rememberSaveable { mutableStateOf(false) } - val prefs = LocalContext.current.prefs() - Preference( - name = setting.title, - onClick = { showDialog = true }, - description = prefs.getString(setting.key, Defaults.PREF_SPACE_BAR_TEXT)?.takeIf { it.isNotEmpty() } - ) - if (showDialog) { - TextInputDialog( - onDismissRequest = { showDialog = false }, - onConfirmed = { - prefs.edit().putString(setting.key, it).apply() - KeyboardSwitcher.getInstance().setThemeNeedsReload() - }, - initialText = prefs.getString(setting.key, Defaults.PREF_SPACE_BAR_TEXT) ?: "", - title = { Text(setting.title) }, - checkTextValid = { true } - ) - } + Setting(context, Settings.PREF_SPACE_BAR_TEXT, R.string.prefs_space_bar_text) { + TextInputPreference(it, Defaults.PREF_SPACE_BAR_TEXT) }, Setting(context, SettingsWithoutKey.CUSTOM_FONT, R.string.custom_font) { CustomFontPreference(it) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa277977..03e7bcff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -196,6 +196,8 @@ Backup Restore + + Format for timestamp key Multilingual typing diff --git a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt index e8f2a5c3..bebee923 100644 --- a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt @@ -23,6 +23,7 @@ import helium314.keyboard.latin.inputlogic.InputLogic import helium314.keyboard.latin.inputlogic.SpaceState import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.ScriptUtils +import helium314.keyboard.latin.utils.getTimestamp import helium314.keyboard.latin.utils.prefs import org.junit.runner.RunWith import org.mockito.Mockito @@ -666,6 +667,13 @@ class InputLogicTest { // need to avoid getting into the mWordComposer.isBatchMode() part of handleBackspaceEvent } + @Test fun timestamp() { + reset() + chainInput("hello") + functionalKeyPress(KeyCode.TIMESTAMP) + assertEquals("hello" + getTimestamp(latinIME), text) + } + // ------- helper functions --------- // should be called before every test, so the same state is guaranteed diff --git a/layouts.md b/layouts.md index b442e934..9464abea 100644 --- a/layouts.md +++ b/layouts.md @@ -103,7 +103,7 @@ Usually the label is what is displayed on the key. However, there are some speci * In case a label clashes with text you want to add, put a `\` in front of the text you want, e.g. `\space` will write the label `space` instead of adding a space bar. * Note that you need to escape the `\` in json files by adding a second `\`. * If you want different key label and input text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. -You can also specify special key codes like `a|!code/key_action_previous`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. +You can also specify special key codes like `a|!code/key_action_previous` or `abc|!code/-10043`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. * It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`. * You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt). You can also use toolbar key icons using the uppercase name of the [toolbar key](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. `!icon/redo`