TraditionalT9.class cleanup: moved the text operations into separate TextField and InputType helpers
This commit is contained in:
parent
2d8ca63f0f
commit
c5c27cdcf1
7 changed files with 457 additions and 401 deletions
|
|
@ -14,9 +14,9 @@ import java.util.List;
|
|||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.db.DictionaryDb;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputFieldHelper;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextHelper;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputType;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
|
|
@ -26,7 +26,8 @@ import io.github.sspanak.tt9.ui.UI;
|
|||
public class TraditionalT9 extends KeyPadHandler {
|
||||
// internal settings/data
|
||||
private boolean isActive = false;
|
||||
private EditorInfo inputField;
|
||||
private TextField textField;
|
||||
private InputType inputType;
|
||||
|
||||
// input mode
|
||||
private ArrayList<Integer> allowedInputModes = new ArrayList<>();
|
||||
|
|
@ -97,7 +98,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
determineAllowedInputModes();
|
||||
mInputMode = InputModeValidator.validateMode(settings, mInputMode, allowedInputModes);
|
||||
|
||||
mInputMode.setTextFieldCase(InputFieldHelper.determineTextCase(currentInputConnection, inputField));
|
||||
mInputMode.setTextFieldCase(textField.determineTextCase(inputType));
|
||||
// Some modes may want to change the default text case based on grammar rules.
|
||||
determineNextTextCase();
|
||||
InputModeValidator.validateTextCase(settings, mInputMode, settings.getTextCase());
|
||||
|
|
@ -124,8 +125,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
protected void onStart(EditorInfo input) {
|
||||
this.inputField = input;
|
||||
if (currentInputConnection == null || inputField == null || InputFieldHelper.isLimitedField(inputField)) {
|
||||
inputType = new InputType(currentInputConnection, input);
|
||||
textField = new TextField(currentInputConnection, input);
|
||||
|
||||
if (!inputType.isValid() || inputType.isLimited()) {
|
||||
// When the input is invalid or simple, let Android handle it.
|
||||
onStop();
|
||||
return;
|
||||
|
|
@ -167,7 +170,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
// 1. Dialer fields seem to handle backspace on their own and we must ignore it,
|
||||
// otherwise, keyDown race condition occur for all keys.
|
||||
// 2. Allow the assigned key to function normally, when there is no text (e.g. "Back" navigates back)
|
||||
if (mEditing == EDITING_DIALER || !InputFieldHelper.isThereText(currentInputConnection)) {
|
||||
if (mEditing == EDITING_DIALER || !textField.isThereText()) {
|
||||
Logger.d("onBackspace", "backspace ignored");
|
||||
mInputMode.reset();
|
||||
return false;
|
||||
|
|
@ -206,7 +209,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected boolean onUp() {
|
||||
if (previousSuggestion()) {
|
||||
mInputMode.setWordStem(mLanguage, mSuggestionView.getCurrentSuggestion(), true);
|
||||
setComposingTextWithWordStemIndication(mSuggestionView.getCurrentSuggestion());
|
||||
textField.setComposingTextWithHighlightedStem(mSuggestionView.getCurrentSuggestion(), mInputMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +220,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected boolean onDown() {
|
||||
if (nextSuggestion()) {
|
||||
mInputMode.setWordStem(mLanguage, mSuggestionView.getCurrentSuggestion(), true);
|
||||
setComposingTextWithWordStemIndication(mSuggestionView.getCurrentSuggestion());
|
||||
textField.setComposingTextWithHighlightedStem(mSuggestionView.getCurrentSuggestion(), mInputMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +291,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
currentWord = mInputMode.getWord();
|
||||
|
||||
mInputMode.onAcceptSuggestion(mLanguage, currentWord);
|
||||
commitText(currentWord);
|
||||
textField.setText(currentWord);
|
||||
clearSuggestions();
|
||||
autoCorrectSpace(currentWord, true, key, hold, repeat > 0);
|
||||
resetKeyRepeat();
|
||||
|
|
@ -301,13 +304,13 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
protected boolean onPound() {
|
||||
commitText("#");
|
||||
textField.setText("#");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected boolean onStar() {
|
||||
commitText("*");
|
||||
textField.setText("*");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +380,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
mSuggestionView.scrollToSuggestion(-1);
|
||||
setComposingTextWithWordStemIndication(mSuggestionView.getCurrentSuggestion());
|
||||
textField.setComposingTextWithHighlightedStem(mSuggestionView.getCurrentSuggestion(), mInputMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -389,7 +392,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
mSuggestionView.scrollToSuggestion(1);
|
||||
setComposingTextWithWordStemIndication(mSuggestionView.getCurrentSuggestion());
|
||||
textField.setComposingTextWithHighlightedStem(mSuggestionView.getCurrentSuggestion(), mInputMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -402,7 +405,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
private void commitCurrentSuggestion(boolean entireSuggestion) {
|
||||
if (!isSuggestionViewHidden() && currentInputConnection != null) {
|
||||
if (entireSuggestion) {
|
||||
setComposingText(mSuggestionView.getCurrentSuggestion());
|
||||
textField.setComposingText(mSuggestionView.getCurrentSuggestion());
|
||||
}
|
||||
currentInputConnection.finishComposingText();
|
||||
}
|
||||
|
|
@ -415,7 +418,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
setSuggestions(null);
|
||||
|
||||
if (currentInputConnection != null) {
|
||||
setComposingText("");
|
||||
textField.setComposingText("");
|
||||
currentInputConnection.finishComposingText();
|
||||
}
|
||||
}
|
||||
|
|
@ -436,7 +439,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
// for a more intuitive experience.
|
||||
String word = mSuggestionView.getCurrentSuggestion();
|
||||
word = word.substring(0, Math.min(mInputMode.getSequenceLength(), word.length()));
|
||||
setComposingTextWithWordStemIndication(word);
|
||||
textField.setComposingTextWithHighlightedStem(word, mInputMode);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -464,13 +467,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private void commitText(String text) {
|
||||
if (text != null && currentInputConnection != null) {
|
||||
currentInputConnection.commitText(text, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getComposingText() {
|
||||
String text = mSuggestionView.getCurrentSuggestion();
|
||||
if (text.length() > 0 && text.length() > mInputMode.getSequenceLength()) {
|
||||
|
|
@ -481,24 +477,8 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private void setComposingText(CharSequence text) {
|
||||
if (text != null && currentInputConnection != null) {
|
||||
currentInputConnection.setComposingText(text, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setComposingTextWithWordStemIndication(CharSequence word) {
|
||||
if (mInputMode.getWordStem().length() > 0) {
|
||||
setComposingText(TextHelper.highlightComposingText(word, 0, mInputMode.getWordStem().length(), mInputMode.isStemFilterFuzzy()));
|
||||
} else {
|
||||
setComposingText(word);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void refreshComposingText() {
|
||||
setComposingText(getComposingText());
|
||||
textField.setComposingText(getComposingText());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -564,18 +544,15 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private void jumpBeforeComposingText() {
|
||||
if (currentInputConnection != null) {
|
||||
currentInputConnection.setComposingText(getComposingText(), 0);
|
||||
currentInputConnection.finishComposingText();
|
||||
}
|
||||
|
||||
textField.setComposingText(getComposingText(), 0);
|
||||
textField.finishComposingText();
|
||||
setSuggestions(null);
|
||||
mInputMode.reset();
|
||||
}
|
||||
|
||||
|
||||
private void determineAllowedInputModes() {
|
||||
allowedInputModes = InputFieldHelper.determineInputModes(inputField);
|
||||
allowedInputModes = textField.determineInputModes(inputType);
|
||||
|
||||
int lastInputModeId = settings.getInputMode();
|
||||
if (allowedInputModes.contains(lastInputModeId)) {
|
||||
|
|
@ -586,23 +563,23 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
mInputMode = InputMode.getInstance(settings, allowedInputModes.get(0));
|
||||
}
|
||||
|
||||
if (InputFieldHelper.isDialerField(inputField)) {
|
||||
if (inputType.isDialer()) {
|
||||
mEditing = EDITING_DIALER;
|
||||
} else if (mInputMode.is123() && allowedInputModes.size() == 1) {
|
||||
mEditing = EDITING_STRICT_NUMERIC;
|
||||
} else {
|
||||
mEditing = InputFieldHelper.isFilterField(inputField) ? EDITING_NOSHOW : EDITING;
|
||||
mEditing = inputType.isFilter() ? EDITING_NOSHOW : EDITING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void autoCorrectSpace(String currentWord, boolean isWordAcceptedManually, int incomingKey, boolean hold, boolean repeat) {
|
||||
if (mInputMode.shouldDeletePrecedingSpace(inputField)) {
|
||||
InputFieldHelper.deletePrecedingSpace(currentInputConnection, currentWord);
|
||||
if (mInputMode.shouldDeletePrecedingSpace(inputType)) {
|
||||
textField.deletePrecedingSpace(currentWord);
|
||||
}
|
||||
|
||||
if (mInputMode.shouldAddAutoSpace(currentInputConnection, inputField, isWordAcceptedManually, incomingKey, hold, repeat)) {
|
||||
commitText(" ");
|
||||
if (mInputMode.shouldAddAutoSpace(inputType, textField, isWordAcceptedManually, incomingKey, hold, repeat)) {
|
||||
textField.setText(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -610,8 +587,8 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
private void determineNextTextCase() {
|
||||
mInputMode.determineNextWordTextCase(
|
||||
settings,
|
||||
InputFieldHelper.isThereText(currentInputConnection),
|
||||
InputFieldHelper.getTextBeforeCursor(currentInputConnection)
|
||||
textField.isThereText(),
|
||||
textField.getTextBeforeCursor()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -624,7 +601,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
currentInputConnection.finishComposingText();
|
||||
clearSuggestions();
|
||||
|
||||
UI.showAddWordDialog(this, mLanguage.getId(), InputFieldHelper.getSurroundingWord(currentInputConnection));
|
||||
UI.showAddWordDialog(this, mLanguage.getId(), textField.getSurroundingWord());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -636,13 +613,13 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
String word = settings.getLastWord();
|
||||
settings.clearLastWord();
|
||||
|
||||
if (word.length() == 0 || word.equals(InputFieldHelper.getSurroundingWord(currentInputConnection))) {
|
||||
if (word.length() == 0 || word.equals(textField.getSurroundingWord())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.d("restoreAddedWordIfAny", "Restoring word: '" + word + "'...");
|
||||
commitText(word);
|
||||
textField.setText(word);
|
||||
mInputMode.reset();
|
||||
} catch (Exception e) {
|
||||
Logger.w("tt9/restoreLastWord", "Could not restore the last added word. " + e.getMessage());
|
||||
|
|
|
|||
|
|
@ -1,284 +0,0 @@
|
|||
package io.github.sspanak.tt9.ime.helpers;
|
||||
|
||||
import android.text.InputType;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
|
||||
|
||||
public class InputFieldHelper {
|
||||
private static final Pattern beforeCursorWordRegex = Pattern.compile("(\\w+)(?!\n)$");
|
||||
private static final Pattern afterCursorWordRegex = Pattern.compile("^(?<!\n)(\\w+)");
|
||||
|
||||
|
||||
public static boolean isThereText(InputConnection currentInputConnection) {
|
||||
if (currentInputConnection == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtractedText extractedText = currentInputConnection.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
return extractedText != null && extractedText.text.length() > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isThereSpaceAhead
|
||||
* Checks whether there is a space after the cursor.
|
||||
*/
|
||||
public static boolean isThereSpaceAhead(InputConnection inputConnection) {
|
||||
CharSequence after = inputConnection != null ? inputConnection.getTextAfterCursor(1, 0) : null;
|
||||
return after != null && after.equals(" ");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isLimitedField
|
||||
* Special or limited input type means the input connection is not rich,
|
||||
* or it can not process or show things like candidate text, nor retrieve the current text.
|
||||
*
|
||||
* https://developer.android.com/reference/android/text/InputType#TYPE_NULL
|
||||
*/
|
||||
public static boolean isLimitedField(EditorInfo inputField) {
|
||||
return inputField != null && inputField.inputType == InputType.TYPE_NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isDialerField
|
||||
* Dialer fields seem to take care of numbers and backspace on their own,
|
||||
* so we need to be aware of them.
|
||||
*
|
||||
* NOTE: A Dialer field is not the same as a Phone field in a phone book.
|
||||
*/
|
||||
public static boolean isDialerField(EditorInfo inputField) {
|
||||
return
|
||||
inputField != null
|
||||
&& inputField.inputType == InputType.TYPE_CLASS_PHONE
|
||||
&& inputField.packageName.equals("com.android.dialer");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isEmailField(EditorInfo inputField) {
|
||||
if (inputField == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int variation = inputField.inputType & InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return
|
||||
variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||
|| variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isFilterField
|
||||
* handle filter list cases... do not hijack DPAD center and make sure back's go through proper
|
||||
*/
|
||||
public static boolean isFilterField(EditorInfo inputField) {
|
||||
if (inputField == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int inputType = inputField.inputType & InputType.TYPE_MASK_CLASS;
|
||||
int inputVariation = inputField.inputType & InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return inputType == InputType.TYPE_CLASS_TEXT && inputVariation == InputType.TYPE_TEXT_VARIATION_FILTER;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isPasswordField(EditorInfo inputField) {
|
||||
if (inputField == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int variation = inputField.inputType & InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return
|
||||
variation == InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
|| variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
|| variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isPersonNameField(EditorInfo inputField) {
|
||||
return inputField != null && (inputField.inputType & InputType.TYPE_MASK_VARIATION) == InputType.TYPE_TEXT_VARIATION_PERSON_NAME;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isSpecializedTextField(EditorInfo inputField) {
|
||||
return isEmailField(inputField) || isPasswordField(inputField) || isUriField(inputField);
|
||||
}
|
||||
|
||||
|
||||
private static boolean isUriField(EditorInfo inputField) {
|
||||
return inputField != null && (inputField.inputType & InputType.TYPE_MASK_VARIATION) == InputType.TYPE_TEXT_VARIATION_URI;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* determineInputModes
|
||||
* Determine the typing mode based on the input field being edited. Returns an ArrayList of the allowed modes.
|
||||
*
|
||||
* @return ArrayList<SettingsStore.MODE_ABC | SettingsStore.MODE_123 | SettingsStore.MODE_PREDICTIVE>
|
||||
*/
|
||||
public static ArrayList<Integer> determineInputModes(EditorInfo inputField) {
|
||||
final int INPUT_TYPE_SHARP_007H_PHONE_BOOK = 65633;
|
||||
|
||||
ArrayList<Integer> allowedModes = new ArrayList<>();
|
||||
|
||||
if (inputField == null) {
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
return allowedModes;
|
||||
}
|
||||
|
||||
if (
|
||||
inputField.inputType == INPUT_TYPE_SHARP_007H_PHONE_BOOK
|
||||
|| (
|
||||
inputField.privateImeOptions != null
|
||||
&& inputField.privateImeOptions.equals("io.github.sspanak.tt9.addword=true")
|
||||
)
|
||||
) {
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
allowedModes.add(InputMode.MODE_ABC);
|
||||
return allowedModes;
|
||||
}
|
||||
|
||||
switch (inputField.inputType & InputType.TYPE_MASK_CLASS) {
|
||||
case InputType.TYPE_CLASS_NUMBER:
|
||||
case InputType.TYPE_CLASS_DATETIME:
|
||||
// Numbers and dates default to the symbols keyboard, with
|
||||
// no extra features.
|
||||
case InputType.TYPE_CLASS_PHONE:
|
||||
// Phones will also default to the symbols keyboard, though
|
||||
// often you will want to have a dedicated phone keyboard.
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
return allowedModes;
|
||||
|
||||
case InputType.TYPE_CLASS_TEXT:
|
||||
// This is general text editing. We will default to the
|
||||
// normal alphabetic keyboard, and assume that we should
|
||||
// be doing predictive text (showing candidates as the
|
||||
// user types).
|
||||
if (!isPasswordField(inputField) && !isFilterField(inputField)) {
|
||||
allowedModes.add(InputMode.MODE_PREDICTIVE);
|
||||
}
|
||||
|
||||
// ↓ fallthrough to add ABC and 123 modes ↓
|
||||
|
||||
default:
|
||||
// For all unknown input types, default to the alphabetic
|
||||
// keyboard with no special features.
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
allowedModes.add(InputMode.MODE_ABC);
|
||||
|
||||
return allowedModes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to update the shift state of our keyboard based on the initial
|
||||
* editor state.
|
||||
*/
|
||||
public static int determineTextCase(InputConnection inputConnection, EditorInfo inputField) {
|
||||
if (inputField == null || inputConnection == null || inputField.inputType == InputType.TYPE_NULL) {
|
||||
return InputMode.CASE_UNDEFINED;
|
||||
}
|
||||
|
||||
if (isSpecializedTextField(inputField)) {
|
||||
return InputMode.CASE_LOWER;
|
||||
}
|
||||
|
||||
if (isPersonNameField(inputField)) {
|
||||
return InputMode.CASE_CAPITALIZE;
|
||||
}
|
||||
|
||||
switch (inputField.inputType & InputType.TYPE_MASK_FLAGS) {
|
||||
case InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS:
|
||||
return InputMode.CASE_UPPER;
|
||||
case InputType.TYPE_TEXT_FLAG_CAP_WORDS:
|
||||
return InputMode.CASE_CAPITALIZE;
|
||||
}
|
||||
|
||||
return InputMode.CASE_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getTextBeforeCursor
|
||||
* A simplified helper that return up to 50 characters before the cursor and "just works".
|
||||
*/
|
||||
public static String getTextBeforeCursor(InputConnection inputConnection) {
|
||||
if (inputConnection == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence before = inputConnection.getTextBeforeCursor(50, 0);
|
||||
return before != null ? before.toString() : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getTextBeforeCursor
|
||||
* A simplified helper that return up to 50 characters after the cursor and "just works".
|
||||
*/
|
||||
public static String getTextAfterCursor(InputConnection inputConnection) {
|
||||
if (inputConnection == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence before = inputConnection.getTextAfterCursor(50, 0);
|
||||
return before != null ? before.toString() : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getSurroundingWord
|
||||
* Returns the word next or around the cursor. Scanning length is up to 50 chars in each direction.
|
||||
*/
|
||||
public static String getSurroundingWord(InputConnection currentInputConnection) {
|
||||
Matcher before = beforeCursorWordRegex.matcher(getTextBeforeCursor(currentInputConnection));
|
||||
Matcher after = afterCursorWordRegex.matcher(getTextAfterCursor(currentInputConnection));
|
||||
|
||||
return (before.find() ? before.group(1) : "") + (after.find() ? after.group(1) : "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deletePrecedingSpace
|
||||
* Deletes the preceding space before the given word. The word must be before the cursor.
|
||||
* No action is taken when there is double space or when it's the beginning of the text field.
|
||||
*/
|
||||
public static void deletePrecedingSpace(InputConnection inputConnection, String word) {
|
||||
if (inputConnection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String searchText = " " + word;
|
||||
|
||||
inputConnection.beginBatchEdit();
|
||||
CharSequence beforeText = inputConnection.getTextBeforeCursor(searchText.length() + 1, 0);
|
||||
if (
|
||||
beforeText == null
|
||||
|| beforeText.length() < searchText.length() + 1
|
||||
|| beforeText.charAt(1) != ' ' // preceding char must be " "
|
||||
|| beforeText.charAt(0) == ' ' // but do nothing when there is double space
|
||||
) {
|
||||
inputConnection.endBatchEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
inputConnection.deleteSurroundingText(searchText.length(), 0);
|
||||
inputConnection.commitText(word, 1);
|
||||
|
||||
inputConnection.endBatchEdit();
|
||||
}
|
||||
}
|
||||
106
src/io/github/sspanak/tt9/ime/helpers/InputType.java
Normal file
106
src/io/github/sspanak/tt9/ime/helpers/InputType.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package io.github.sspanak.tt9.ime.helpers;
|
||||
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
|
||||
public class InputType {
|
||||
private final InputConnection connection;
|
||||
private final EditorInfo field;
|
||||
|
||||
|
||||
public InputType(InputConnection inputConnection, EditorInfo inputField) {
|
||||
connection = inputConnection;
|
||||
field = inputField;
|
||||
}
|
||||
|
||||
|
||||
public boolean isValid() {
|
||||
return field != null && connection != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isLimited
|
||||
* Special or limited input type means the input connection is not rich,
|
||||
* or it can not process or show things like candidate text, nor retrieve the current text.
|
||||
*
|
||||
* https://developer.android.com/reference/android/text/InputType#TYPE_NULL
|
||||
*/
|
||||
public boolean isLimited() {
|
||||
return field != null && field.inputType == android.text.InputType.TYPE_NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isDialer
|
||||
* Dialer fields seem to take care of numbers and backspace on their own,
|
||||
* so we need to be aware of them.
|
||||
*
|
||||
* NOTE: A Dialer field is not the same as a Phone field in a phone book.
|
||||
*/
|
||||
public boolean isDialer() {
|
||||
return
|
||||
field != null
|
||||
&& field.inputType == android.text.InputType.TYPE_CLASS_PHONE
|
||||
&& field.packageName.equals("com.android.dialer");
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmail() {
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int variation = field.inputType & android.text.InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return
|
||||
variation == android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||
|| variation == android.text.InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isFilter
|
||||
* handle filter list cases... do not hijack DPAD center and make sure back's go through proper
|
||||
*/
|
||||
public boolean isFilter() {
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int inputType = field.inputType & android.text.InputType.TYPE_MASK_CLASS;
|
||||
int inputVariation = field.inputType & android.text.InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return inputType == android.text.InputType.TYPE_CLASS_TEXT && inputVariation == android.text.InputType.TYPE_TEXT_VARIATION_FILTER;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPassword() {
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int variation = field.inputType & android.text.InputType.TYPE_MASK_VARIATION;
|
||||
|
||||
return
|
||||
variation == android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
|| variation == android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
|| variation == android.text.InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
||||
}
|
||||
|
||||
|
||||
boolean isPersonName() {
|
||||
return field != null && (field.inputType & android.text.InputType.TYPE_MASK_VARIATION) == android.text.InputType.TYPE_TEXT_VARIATION_PERSON_NAME;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSpecialized() {
|
||||
return isEmail() || isPassword() || isUri();
|
||||
}
|
||||
|
||||
|
||||
private boolean isUri() {
|
||||
return field != null && (field.inputType & android.text.InputType.TYPE_MASK_VARIATION) == android.text.InputType.TYPE_TEXT_VARIATION_URI;
|
||||
}
|
||||
}
|
||||
300
src/io/github/sspanak/tt9/ime/helpers/TextField.java
Normal file
300
src/io/github/sspanak/tt9/ime/helpers/TextField.java
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
package io.github.sspanak.tt9.ime.helpers;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
|
||||
public class TextField {
|
||||
private static final Pattern beforeCursorWordRegex = Pattern.compile("(\\w+)(?!\n)$");
|
||||
private static final Pattern afterCursorWordRegex = Pattern.compile("^(?<!\n)(\\w+)");
|
||||
|
||||
public final InputConnection connection;
|
||||
public final EditorInfo field;
|
||||
|
||||
public TextField(InputConnection inputConnection, EditorInfo inputField) {
|
||||
connection = inputConnection;
|
||||
field = inputField;
|
||||
}
|
||||
|
||||
|
||||
public boolean isThereText() {
|
||||
if (connection == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtractedText extractedText = connection.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
return extractedText != null && extractedText.text.length() > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* isThereSpaceAhead
|
||||
* Checks whether there is a space after the cursor.
|
||||
*/
|
||||
public boolean isThereSpaceAhead() {
|
||||
CharSequence after = connection != null ? connection.getTextAfterCursor(1, 0) : null;
|
||||
return after != null && after.equals(" ");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* determineInputModes
|
||||
* Determine the typing mode based on the input field being edited. Returns an ArrayList of the allowed modes.
|
||||
*
|
||||
* @return ArrayList<SettingsStore.MODE_ABC | SettingsStore.MODE_123 | SettingsStore.MODE_PREDICTIVE>
|
||||
*/
|
||||
public ArrayList<Integer> determineInputModes(InputType inputType) {
|
||||
final int INPUT_TYPE_SHARP_007H_PHONE_BOOK = 65633;
|
||||
|
||||
ArrayList<Integer> allowedModes = new ArrayList<>();
|
||||
|
||||
if (field == null) {
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
return allowedModes;
|
||||
}
|
||||
|
||||
if (
|
||||
field.inputType == INPUT_TYPE_SHARP_007H_PHONE_BOOK
|
||||
|| (
|
||||
field.privateImeOptions != null
|
||||
&& field.privateImeOptions.equals("io.github.sspanak.tt9.addword=true")
|
||||
)
|
||||
) {
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
allowedModes.add(InputMode.MODE_ABC);
|
||||
return allowedModes;
|
||||
}
|
||||
|
||||
switch (field.inputType & android.text.InputType.TYPE_MASK_CLASS) {
|
||||
case android.text.InputType.TYPE_CLASS_NUMBER:
|
||||
case android.text.InputType.TYPE_CLASS_DATETIME:
|
||||
// Numbers and dates default to the symbols keyboard, with
|
||||
// no extra features.
|
||||
case android.text.InputType.TYPE_CLASS_PHONE:
|
||||
// Phones will also default to the symbols keyboard, though
|
||||
// often you will want to have a dedicated phone keyboard.
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
return allowedModes;
|
||||
|
||||
case android.text.InputType.TYPE_CLASS_TEXT:
|
||||
// This is general text editing. We will default to the
|
||||
// normal alphabetic keyboard, and assume that we should
|
||||
// be doing predictive text (showing candidates as the
|
||||
// user types).
|
||||
if (!inputType.isPassword() && !inputType.isFilter()) {
|
||||
allowedModes.add(InputMode.MODE_PREDICTIVE);
|
||||
}
|
||||
|
||||
// ↓ fallthrough to add ABC and 123 modes ↓
|
||||
|
||||
default:
|
||||
// For all unknown input types, default to the alphabetic
|
||||
// keyboard with no special features.
|
||||
allowedModes.add(InputMode.MODE_123);
|
||||
allowedModes.add(InputMode.MODE_ABC);
|
||||
|
||||
return allowedModes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to update the shift state of our keyboard based on the initial
|
||||
* editor state.
|
||||
*/
|
||||
public int determineTextCase(InputType inputType) {
|
||||
if (connection == null || field == null || field.inputType == android.text.InputType.TYPE_NULL) {
|
||||
return InputMode.CASE_UNDEFINED;
|
||||
}
|
||||
|
||||
if (inputType.isSpecialized()) {
|
||||
return InputMode.CASE_LOWER;
|
||||
}
|
||||
|
||||
if (inputType.isPersonName()) {
|
||||
return InputMode.CASE_CAPITALIZE;
|
||||
}
|
||||
|
||||
switch (field.inputType & android.text.InputType.TYPE_MASK_FLAGS) {
|
||||
case android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS:
|
||||
return InputMode.CASE_UPPER;
|
||||
case android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS:
|
||||
return InputMode.CASE_CAPITALIZE;
|
||||
}
|
||||
|
||||
return InputMode.CASE_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getTextBeforeCursor
|
||||
* A simplified helper that return up to 50 characters before the cursor and "just works".
|
||||
*/
|
||||
public String getTextBeforeCursor() {
|
||||
if (connection == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence before = connection.getTextBeforeCursor(50, 0);
|
||||
return before != null ? before.toString() : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getTextBeforeCursor
|
||||
* A simplified helper that return up to 50 characters after the cursor and "just works".
|
||||
*/
|
||||
public String getTextAfterCursor() {
|
||||
if (connection == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence before = connection.getTextAfterCursor(50, 0);
|
||||
return before != null ? before.toString() : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getSurroundingWord
|
||||
* Returns the word next or around the cursor. Scanning length is up to 50 chars in each direction.
|
||||
*/
|
||||
public String getSurroundingWord() {
|
||||
Matcher before = beforeCursorWordRegex.matcher(getTextBeforeCursor());
|
||||
Matcher after = afterCursorWordRegex.matcher(getTextAfterCursor());
|
||||
|
||||
return (before.find() ? before.group(1) : "") + (after.find() ? after.group(1) : "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deletePrecedingSpace
|
||||
* Deletes the preceding space before the given word. The word must be before the cursor.
|
||||
* No action is taken when there is double space or when it's the beginning of the text field.
|
||||
*/
|
||||
public void deletePrecedingSpace(String word) {
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String searchText = " " + word;
|
||||
|
||||
connection.beginBatchEdit();
|
||||
CharSequence beforeText = connection.getTextBeforeCursor(searchText.length() + 1, 0);
|
||||
if (
|
||||
beforeText == null
|
||||
|| beforeText.length() < searchText.length() + 1
|
||||
|| beforeText.charAt(1) != ' ' // preceding char must be " "
|
||||
|| beforeText.charAt(0) == ' ' // but do nothing when there is double space
|
||||
) {
|
||||
connection.endBatchEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
connection.deleteSurroundingText(searchText.length(), 0);
|
||||
connection.commitText(word, 1);
|
||||
|
||||
connection.endBatchEdit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setText
|
||||
* A fail-safe setter that appends text to the field, ignoring NULL input.
|
||||
*/
|
||||
public void setText(String text) {
|
||||
if (text != null && connection != null) {
|
||||
connection.commitText(text, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setComposingText
|
||||
* A fail-safe setter for composing text, which ignores NULL input.
|
||||
*/
|
||||
public void setComposingText(CharSequence text, int position) {
|
||||
if (text != null && connection != null) {
|
||||
connection.setComposingText(text, position);
|
||||
}
|
||||
}
|
||||
|
||||
public void setComposingText(CharSequence text) { setComposingText(text, 1); }
|
||||
|
||||
|
||||
/**
|
||||
* setComposingTextWithHighlightedStem
|
||||
*
|
||||
* Sets the composing text, but makes the "stem" substring bold. If "highlightMore" is true,
|
||||
* the "stem" part will be in bold and italic.
|
||||
*/
|
||||
public void setComposingTextWithHighlightedStem(CharSequence word, String stem, boolean highlightMore) {
|
||||
setComposingText(
|
||||
stem.length() > 0 ? highlightText(word, 0, stem.length(), highlightMore) : word
|
||||
);
|
||||
}
|
||||
|
||||
public void setComposingTextWithHighlightedStem(CharSequence word, InputMode inputMode) {
|
||||
setComposingTextWithHighlightedStem(word, inputMode.getWordStem(), inputMode.isStemFilterFuzzy());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* finishComposingText
|
||||
* Finish composing text or do nothing if the text field is invalid.
|
||||
*/
|
||||
public void finishComposingText() {
|
||||
if (connection != null) {
|
||||
connection.finishComposingText();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* highlightText
|
||||
* Makes the characters from "start" to "end" bold. If "highlightMore" is true,
|
||||
* the text will be in bold and italic.
|
||||
*/
|
||||
private CharSequence highlightText(CharSequence word, int start, int end, boolean highlightMore) {
|
||||
if (end < start || start < 0) {
|
||||
Logger.w("tt9.util.highlightComposingText", "Cannot highlight invalid composing text range: [" + start + ", " + end + "]");
|
||||
return word;
|
||||
}
|
||||
|
||||
SpannableString styledWord = new SpannableString(word);
|
||||
|
||||
// default underline style
|
||||
styledWord.setSpan(new UnderlineSpan(), 0, word.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
// highlight the requested range
|
||||
styledWord.setSpan(
|
||||
new StyleSpan(Typeface.BOLD),
|
||||
start,
|
||||
Math.min(word.length(), end),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
);
|
||||
|
||||
if (highlightMore) {
|
||||
styledWord.setSpan(
|
||||
new StyleSpan(Typeface.BOLD_ITALIC),
|
||||
start,
|
||||
Math.min(word.length(), end),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
);
|
||||
}
|
||||
|
||||
return styledWord;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package io.github.sspanak.tt9.ime.helpers;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
|
||||
public class TextHelper {
|
||||
public static CharSequence highlightComposingText(CharSequence word, int start, int end, boolean highlightMore) {
|
||||
if (end < start || start < 0) {
|
||||
Logger.w("tt9.util.highlightComposingText", "Cannot highlight invalid composing text range: [" + start + ", " + end + "]");
|
||||
return word;
|
||||
}
|
||||
|
||||
SpannableString styledWord = new SpannableString(word);
|
||||
|
||||
// default underline style
|
||||
styledWord.setSpan(new UnderlineSpan(), 0, word.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
// highlight the requested range
|
||||
styledWord.setSpan(
|
||||
new StyleSpan(Typeface.BOLD),
|
||||
start,
|
||||
Math.min(word.length(), end),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
);
|
||||
|
||||
if (highlightMore) {
|
||||
styledWord.setSpan(
|
||||
new StyleSpan(Typeface.BOLD_ITALIC),
|
||||
start,
|
||||
Math.min(word.length(), end),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
);
|
||||
}
|
||||
|
||||
return styledWord;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package io.github.sspanak.tt9.ime.modes;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputType;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
|
|
@ -76,8 +76,8 @@ abstract public class InputMode {
|
|||
|
||||
// Interaction with the IME. Return "true" if it should perform the respective action.
|
||||
public boolean shouldAcceptCurrentSuggestion(Language language, int key, boolean hold, boolean repeat) { return false; }
|
||||
public boolean shouldAddAutoSpace(InputConnection inputConnection, EditorInfo inputField, boolean isWordAcceptedManually, int incomingKey, boolean hold, boolean repeat) { return false; }
|
||||
public boolean shouldDeletePrecedingSpace(EditorInfo inputField) { return false; }
|
||||
public boolean shouldAddAutoSpace(InputType inputType, TextField textField, boolean isWordAcceptedManually, int incomingKey, boolean hold, boolean repeat) { return false; }
|
||||
public boolean shouldDeletePrecedingSpace(InputType inputType) { return false; }
|
||||
public boolean shouldSelectNextSuggestion() { return false; }
|
||||
public boolean shouldTrackNumPress() { return true; }
|
||||
public boolean shouldTrackUpDown() { return false; }
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package io.github.sspanak.tt9.ime.modes;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
|
@ -12,7 +10,8 @@ import java.util.regex.Pattern;
|
|||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.db.DictionaryDb;
|
||||
import io.github.sspanak.tt9.ime.EmptyDatabaseWarning;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputFieldHelper;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputType;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.Characters;
|
||||
|
|
@ -516,15 +515,15 @@ public class ModePredictive extends InputMode {
|
|||
* See the helper functions for the list of rules.
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldAddAutoSpace(InputConnection inputConnection, EditorInfo inputField, boolean isWordAcceptedManually, int incomingKey, boolean hold, boolean repeat) {
|
||||
public boolean shouldAddAutoSpace(InputType inputType, TextField textField, boolean isWordAcceptedManually, int incomingKey, boolean hold, boolean repeat) {
|
||||
return
|
||||
settings.getAutoSpace()
|
||||
&& !hold
|
||||
&& (
|
||||
shouldAddAutoSpaceAfterPunctuation(inputField, incomingKey, repeat)
|
||||
|| shouldAddAutoSpaceAfterWord(inputField, isWordAcceptedManually)
|
||||
shouldAddAutoSpaceAfterPunctuation(inputType, incomingKey, repeat)
|
||||
|| shouldAddAutoSpaceAfterWord(inputType, isWordAcceptedManually)
|
||||
)
|
||||
&& !InputFieldHelper.isThereSpaceAhead(inputConnection);
|
||||
&& !textField.isThereSpaceAhead();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -534,7 +533,7 @@ public class ModePredictive extends InputMode {
|
|||
* The rules are similar to the ones in the standard Android keyboard (with some exceptions,
|
||||
* because we are not using a QWERTY keyboard here).
|
||||
*/
|
||||
private boolean shouldAddAutoSpaceAfterPunctuation(EditorInfo inputField, int incomingKey, boolean repeat) {
|
||||
private boolean shouldAddAutoSpaceAfterPunctuation(InputType inputType, int incomingKey, boolean repeat) {
|
||||
return
|
||||
(incomingKey != 0 || repeat)
|
||||
&& (
|
||||
|
|
@ -548,7 +547,7 @@ public class ModePredictive extends InputMode {
|
|||
|| lastAcceptedWord.endsWith("]")
|
||||
|| lastAcceptedWord.endsWith("%")
|
||||
)
|
||||
&& !InputFieldHelper.isSpecializedTextField(inputField);
|
||||
&& !inputType.isSpecialized();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -557,7 +556,7 @@ public class ModePredictive extends InputMode {
|
|||
* Similar to "shouldAddAutoSpaceAfterPunctuation()", but determines whether to add a space after
|
||||
* words.
|
||||
*/
|
||||
private boolean shouldAddAutoSpaceAfterWord(EditorInfo inputField, boolean isWordAcceptedManually) {
|
||||
private boolean shouldAddAutoSpaceAfterWord(InputType inputType, boolean isWordAcceptedManually) {
|
||||
return
|
||||
// Do not add space when auto-accepting words, because it feels very confusing when typing.
|
||||
isWordAcceptedManually
|
||||
|
|
@ -565,7 +564,7 @@ public class ModePredictive extends InputMode {
|
|||
&& !lastAcceptedSequence.equals("0")
|
||||
// Emoji
|
||||
&& !lastAcceptedSequence.startsWith("1")
|
||||
&& !InputFieldHelper.isSpecializedTextField(inputField);
|
||||
&& !inputType.isSpecialized();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -575,7 +574,7 @@ public class ModePredictive extends InputMode {
|
|||
* This allows automatic conversion from: "words ." to: "words."
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldDeletePrecedingSpace(EditorInfo inputField) {
|
||||
public boolean shouldDeletePrecedingSpace(InputType inputType) {
|
||||
return
|
||||
settings.getAutoSpace()
|
||||
&& (
|
||||
|
|
@ -590,7 +589,7 @@ public class ModePredictive extends InputMode {
|
|||
|| lastAcceptedWord.equals("'")
|
||||
|| lastAcceptedWord.equals("@")
|
||||
)
|
||||
&& !InputFieldHelper.isSpecializedTextField(inputField);
|
||||
&& !inputType.isSpecialized();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue