From 71956888675d36abc009cf9f1e0983654a06eedb Mon Sep 17 00:00:00 2001 From: Dimo Karaivanov Date: Thu, 29 Aug 2024 11:14:09 +0300 Subject: [PATCH] Recomposition (#609) * word re-composition function * reordered the Keypad settings a bit --- .../github/sspanak/tt9/ime/TypingHandler.java | 14 ++++--- .../sspanak/tt9/ime/helpers/TextField.java | 39 +++++++++++++++++-- .../sspanak/tt9/ime/modes/InputMode.java | 1 + .../sspanak/tt9/ime/modes/ModePredictive.java | 24 +++++++++++- .../preferences/settings/SettingsTyping.java | 4 ++ .../java/io/github/sspanak/tt9/util/Text.java | 29 ++++++++++++++ app/src/main/res/values-bg/strings.xml | 2 + app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values-es/strings.xml | 2 + app/src/main/res/values-fr/strings.xml | 2 + app/src/main/res/values-it/strings.xml | 2 + app/src/main/res/values-iw/strings.xml | 2 + app/src/main/res/values-lt/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 2 + app/src/main/res/values-pt-rBR/strings.xml | 2 + app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values-tr/strings.xml | 2 + app/src/main/res/values-uk/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/prefs_screen_keypad.xml | 19 +++++---- 20 files changed, 137 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java index 9b27ca6f..d79e9f83 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java @@ -1,6 +1,5 @@ package io.github.sspanak.tt9.ime; -import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -132,10 +131,13 @@ public abstract class TypingHandler extends KeyPadHandler { suggestionOps.commitCurrent(false); mInputMode.reset(); - int repeats = hold ? Math.min(Math.max(textField.getWordBeforeCursorLength(), 1), SettingsStore.BACKSPACE_ACCELERATION_MAX_CHARS) : 1; - for (int i = repeats; i > 0; i--) { - super.sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); - } + int prevChars = hold ? Math.min(Math.max(textField.getPaddedWordBeforeCursorLength(), 1), SettingsStore.BACKSPACE_ACCELERATION_MAX_CHARS) : 1; + textField.deleteChars(prevChars); + } + + if (!hold && suggestionOps.isEmpty() && mInputMode.recompose(textField.getWordBeforeCursor())) { + textField.deleteChars(mInputMode.getSequenceLength()); + getSuggestions(); } return true; @@ -289,7 +291,7 @@ public abstract class TypingHandler extends KeyPadHandler { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); textSelection.onSelectionUpdate(newSelStart, newSelEnd); - // in case the app has modified the InputField and moved the cursor without notifiying us... + // in case the app has modified the InputField and moved the cursor without notifying us... if (appHacks.onUpdateSelection(mInputMode, suggestionOps, oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)) { return; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java index 4ffa12de..545cb351 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java @@ -104,12 +104,29 @@ public class TextField extends InputField { } + @NonNull public String getWordBeforeCursor() { + if (getTextAfterCursor(1).startsWithWord()) { + return ""; + } + + String before = getStringBeforeCursor(); + if (before.isEmpty() || !Character.isAlphabetic(before.charAt(before.length() - 1))) { + return ""; + } + + int lastSpace = Math.max(before.lastIndexOf(' ') + 1, 0); + int length = Math.max(before.length(), lastSpace); + + return before.substring(lastSpace, length); + } + + /** - * Returns the length of the word before the cursor. If the cursor is inside a word, 0 is returned, - * because there is no full word before it. The scanning length is up to the maximum returned by - * getTextBeforeCursor(). + * Returns the length of the first word before the cursor including any whitespace after it. + * If the cursor is inside a word, 0 is returned, because there is no full word before it. + * The scanning length is up to the maximum returned by getTextBeforeCursor(). */ - public int getWordBeforeCursorLength() { + public int getPaddedWordBeforeCursorLength() { if (getTextAfterCursor(1).startsWithWord()) { return 0; } @@ -124,6 +141,20 @@ public class TextField extends InputField { } + /** + * Deletes one character by emulating a backspace key event (useful for deleting emoji and Unicode + * characters); Or deletes a region of text at once (faster, but may leave half a surrogate pair). + */ + public void deleteChars(int numberOfChars) { + if (numberOfChars == 1) { + connection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + connection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); + } else if (numberOfChars > 1) { + connection.deleteSurroundingText(numberOfChars, 0); + } + } + + /** * deletePrecedingSpace * Deletes the preceding space before the given word. The word must be before the cursor. diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java index 62903891..79533d39 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java @@ -102,6 +102,7 @@ abstract public class InputMode { public boolean shouldDeletePrecedingSpace(InputType inputType) { return false; } public boolean shouldIgnoreText(String text) { return text == null || text.isEmpty(); } public boolean shouldSelectNextSuggestion() { return false; } + public boolean recompose(String word) { return false; } public void reset() { autoAcceptTimeout = -1; diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java index 0b5ab6d4..6511c3fb 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java @@ -14,6 +14,7 @@ import io.github.sspanak.tt9.languages.EmojiLanguage; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.NaturalLanguage; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.util.Characters; import io.github.sspanak.tt9.util.Logger; @@ -123,6 +124,27 @@ public class ModePredictive extends InputMode { } + @Override + public boolean recompose(String word) { + if (!settings.getBackspaceRecomposing() || word == null || word.length() < 2 || word.contains(" ")) { + Logger.d(LOG_TAG, "Not recomposing invalid word: '" + word + "'"); + textCase = CASE_CAPITALIZE; + return false; + } + + try { + reset(); + digitSequence = language.getDigitSequenceForWord(word); + textCase = new Text(language, word).getTextCase(); + setWordStem(word, true); + } catch (InvalidLanguageCharactersException e) { + Logger.d(LOG_TAG, "Not recomposing word: '" + word + "'. " + e.getMessage()); + return false; + } + + return true; + } + @Override public void reset() { super.reset(); @@ -246,7 +268,7 @@ public class ModePredictive extends InputMode { .setIsStemFuzzy(isStemFuzzy) .setStem(stem) .setLanguage(searchLanguage) - .setInputWord(currentWord) + .setInputWord(currentWord.isEmpty() ? stem : currentWord) .setWordsChangedHandler(this::onPredictions) .load(); } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java index fcfa6cae..5d490b9f 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsTyping.java @@ -19,6 +19,10 @@ class SettingsTyping extends SettingsInput { return prefs.getBoolean("backspace_acceleration", false); } + public boolean getBackspaceRecomposing() { + return prefs.getBoolean("backspace_recomposing", true); + } + public String getDoubleZeroChar() { String character = prefs.getString("pref_double_zero_char", "."); diff --git a/app/src/main/java/io/github/sspanak/tt9/util/Text.java b/app/src/main/java/io/github/sspanak/tt9/util/Text.java index 91bfe6ee..94270dae 100644 --- a/app/src/main/java/io/github/sspanak/tt9/util/Text.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/Text.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import java.util.Locale; +import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.languages.Language; public class Text extends TextTools { @@ -36,11 +37,39 @@ public class Text extends TextTools { return text != null && !text.isEmpty() && Characters.isGraphic(text.charAt(text.length() - 1)); } + public int getTextCase() { + if (isUpperCase()) { + return InputMode.CASE_UPPER; + } else if (isCapitalized()) { + return InputMode.CASE_CAPITALIZE; + } else if (isMixedCase()) { + return InputMode.CASE_DICTIONARY; + } else { + return InputMode.CASE_LOWER; + } + } + public boolean isEmpty() { return text == null || text.isEmpty(); } + private boolean isCapitalized() { + if (text == null || text.length() < 2) { + return false; + } + if (!Character.isUpperCase(text.charAt(0))) { + return false; + } + + for (int i = 1, end = text.length(); i < end; i++) { + if (Character.isUpperCase(text.charAt(i))) { + return false; + } + } + + return true; + } public boolean isMixedCase() { return diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index b94a00ab..d40b9871 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -23,6 +23,8 @@ Тъмен облик Автоматични главни букви на всеки ред Започвай всеки ред с главна буква, дори и да е в средата на изречение. + Редактиране на думи + Връщай списъка с предложения при натискане на Backspace на края на дума.\n(Не работи в някои приложения.) Езици Изтрий всички Икона за състояние diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7ac929aa..0b0e063d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -37,8 +37,9 @@ Automatisch Sätze mit einem Großbuchstaben beginnen. Automatische Großbuchstaben auf jeder Zeile Jede Zeile mit einem Großbuchstaben beginnen, auch wenn es mitten im Satz ist. + Wortnachbearbeitung + Die Vorschlagsliste zurückbringen, indem Sie am Ende eines Wortes die Rücktaste drücken.\n(Nicht in allen Apps unterstützt) Sprachen - Statusicon Ein Icon anzeigen, wenn die Tastatureingabe aktiv ist. Die Reihenfolge der Tasten umkehren diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fed9419d..bd04d071 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -30,6 +30,8 @@ Teclado Mayúsculas automáticas en cada línea Comenzar cada línea con una letra mayúscula, incluso si está en medio de una oración. + Recomposición de palabras + Recuperar la lista de sugerencias presionando la tecla Retroceso al final de una palabra.\n(No compatible con algunas aplicaciones) Idiomas Tema oscuro Espacio diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bc7c6244..540b59f8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -24,6 +24,8 @@ Thème sombre Majuscules automatiques sur chaque ligne Commencer chaque ligne avec une majuscule, même si elle est au milieu d\'une phrase. + Recomposition de mots + Ramener la liste de suggestions en appuyant sur Retour arrière à la fin d\'un mot.\n(Non pris en charge dans certaines applications) Langues Supprimer tous Icône d\'état diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0522a7d5..ba594cc8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -39,6 +39,8 @@ Iniziare automaticamente le frasi con una lettera maiuscola. Maiuscole automatiche su ogni riga Iniziare ogni riga con una lettera maiuscola, anche se è nel mezzo di una frase. + Ricomposizione delle parole + Riportare l\'elenco dei suggerimenti premendo Backspace alla fine di una parola.\n(Non supportato in alcune app) Lingue Icona di stato diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index cd6ea0e6..2fa14f34 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -41,6 +41,8 @@ התחל אוטומטית משפטים באות גדולה. רישיות אוטומטיות בכל שורה להתחיל כל שורה באות ראשונה גדולה, גם אם היא באמצע משפט. + עריכת מילים + להחזיר את רשימת ההצעות על ידי לחיצה על מקש Backspace בסוף מילה.\n(לא נתמך בחלק מהאפליקציות) שפות ערכת נושא שחורה תו ללחיצה מרובה על מקש 0 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index bccba867..1b4efed0 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -41,6 +41,8 @@ Automatiškai pradėti sakinius didžiąja raide. Automatiniai didžiosios raidės kiekvienoje eilutėje Pradėti kiekvieną eilutę didžiąja raide, net jei ji yra sakinio viduryje. + Žodžių redagavimas + Sugrąžinti siūlymų sąrašą paspaudžiant klavišą Backspace žodžio pabaigoje.\n(Nepalaikoma kai kuriose programėlėse) Kalbos Tamsi išvaizda Taip diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d9ef5d90..a8889bb5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -36,6 +36,8 @@ Automatisch zinnen beginnen met een hoofdletter. Automatische hoofdletters op elke regel Elke regel beginnen met een hoofdletter, zelfs als het midden van een zin is. + Woordherbewerking + Breng de suggestielijst terug door Backspace te drukken aan het einde van een woord.\n(Niet ondersteund in sommige apps) Talen Statusicoon Een icoon tonen wanneer toetsenbordinvoer actief is. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 89a7174b..c4769923 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -42,6 +42,8 @@ Iniciar automaticamente as frases com letras maiúsculas. Letras maiúsculas automáticas em cada linha Começar cada linha com uma letra maiúscula, mesmo que esteja no meio de uma frase. + Recomposição de palavras + Trazer de volta a lista de sugestões pressionando Backspace no final de uma palavra.\n(Não suportado em alguns aplicativos) Idiomas Modo Escuro Caractere para toque duplo na tecla 0 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6b4bb56b..b49a3ba5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -24,6 +24,8 @@ Темная тема Автоматические заглавные буквы на каждой строке Начинать каждую строку с заглавной буквы, даже если она в середине предложения. + Редактирование слов + Вернуть список предложений, нажав клавишу Backspace в конце слова.\n(Не поддерживается в некоторых приложениях) Языки Удалить все Иконка состояния diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index cb310f61..ce44adbb 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -37,6 +37,8 @@ Cümlelere otomatik olarak büyük harfle başlar. Her Satıra Büyük Harf ile Başlama Cümlenin ortasında olsa bile her yeni satıra büyük harf ile başlar. + Kelime Yeniden Düzenleme + Kelimenin sonunda Geri tuşuna basarak öneri listesini geri getirmek.\n(Bazı uygulamalarda desteklenmez) Diller Durum diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e9e06144..63a2d283 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -43,6 +43,8 @@ Автоматично починати речення з великої букви Автоматичні великі літери на кожному рядку Починати кожен рядок з великої літери, навіть якщо він у середині речення. + Редагування слів + Повернути список пропозицій, натиснувши клавішу Backspace в кінці слова.\n(Не підтримується в деяких додатках) Мови Темна тема Так diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c950f751..fa47e596 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,6 +55,8 @@ Start every line with a capital letter, even it is in the middle of a sentence. Fast Delete Erase entire words by holding Backspace.\n(Not supported in some apps) + Word Re-composition + Bring back the suggestion list by pressing Backspace at the end of a word. (Not supported in some apps) Languages Dark Theme Yes diff --git a/app/src/main/res/xml/prefs_screen_keypad.xml b/app/src/main/res/xml/prefs_screen_keypad.xml index 37c38253..b61840a0 100644 --- a/app/src/main/res/xml/prefs_screen_keypad.xml +++ b/app/src/main/res/xml/prefs_screen_keypad.xml @@ -9,19 +9,17 @@ app:key="screen_hotkeys" app:title="@string/pref_category_function_keys" /> + + - - - - +