1
0
Fork 0

improved support for phone, decimal and signed integer numeric fields

This commit is contained in:
Dimo Karaivanov 2023-08-28 12:19:17 +03:00
parent 5ece90cd21
commit 81df61b900
6 changed files with 125 additions and 44 deletions

View file

@ -63,8 +63,20 @@ public class TraditionalT9 extends KeyPadHandler {
return settings; return settings;
} }
public int getInputMode() { public boolean isInputModeNumeric() {
return mInputMode != null ? mInputMode.getId() : InputMode.MODE_UNDEFINED; return mInputMode != null && mInputMode.is123();
}
public boolean isNumericModeStrict() {
return mInputMode != null && mInputMode.is123() && inputType.isNumeric() && !inputType.isPhoneNumber();
}
public boolean isNumericModeSigned() {
return mInputMode != null && mInputMode.is123() && inputType.isSignedNumber();
}
public boolean isInputModePhone() {
return mInputMode != null && mInputMode.is123() && inputType.isPhoneNumber();
} }
public int getTextCase() { public int getTextCase() {
@ -96,7 +108,7 @@ public class TraditionalT9 extends KeyPadHandler {
private void determineInputMode() { private void determineInputMode() {
allowedInputModes = textField.determineInputModes(inputType); allowedInputModes = textField.determineInputModes(inputType);
int validModeId = InputModeValidator.validateMode(settings.getInputMode(), allowedInputModes); int validModeId = InputModeValidator.validateMode(settings.getInputMode(), allowedInputModes);
mInputMode = InputMode.getInstance(settings, mLanguage, validModeId); mInputMode = InputMode.getInstance(settings, mLanguage, inputType, validModeId);
} }
@ -669,7 +681,7 @@ public class TraditionalT9 extends KeyPadHandler {
if (mInputMode.isPassthrough()) { if (mInputMode.isPassthrough()) {
return; return;
} else if (allowedInputModes.size() == 1 && allowedInputModes.contains(InputMode.MODE_123)) { } else if (allowedInputModes.size() == 1 && allowedInputModes.contains(InputMode.MODE_123)) {
mInputMode = !mInputMode.is123() ? InputMode.getInstance(settings, mLanguage, InputMode.MODE_123) : mInputMode; mInputMode = !mInputMode.is123() ? InputMode.getInstance(settings, mLanguage, inputType, InputMode.MODE_123) : mInputMode;
} }
// when typing a word or viewing scrolling the suggestions, only change the case // when typing a word or viewing scrolling the suggestions, only change the case
else if (!isSuggestionViewHidden()) { else if (!isSuggestionViewHidden()) {
@ -693,7 +705,7 @@ public class TraditionalT9 extends KeyPadHandler {
mInputMode.nextTextCase(); mInputMode.nextTextCase();
} else { } else {
int nextModeIndex = (allowedInputModes.indexOf(mInputMode.getId()) + 1) % allowedInputModes.size(); int nextModeIndex = (allowedInputModes.indexOf(mInputMode.getId()) + 1) % allowedInputModes.size();
mInputMode = InputMode.getInstance(settings, mLanguage, allowedInputModes.get(nextModeIndex)); mInputMode = InputMode.getInstance(settings, mLanguage, inputType, allowedInputModes.get(nextModeIndex));
mInputMode.setTextFieldCase(textField.determineTextCase(inputType)); mInputMode.setTextFieldCase(textField.determineTextCase(inputType));
mInputMode.determineNextWordTextCase(textField.isThereText(), textField.getTextBeforeCursor()); mInputMode.determineNextWordTextCase(textField.isThereText(), textField.getTextBeforeCursor());

View file

@ -24,7 +24,7 @@ public class InputType {
* isLimited * isLimited
* Special or limited input type means the input connection is not rich, * 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. * or it can not process or show things like candidate text, nor retrieve the current text.
* * <p>
* More info: <a href="https://developer.android.com/reference/android/text/InputType#TYPE_NULL">android docs</a>. * More info: <a href="https://developer.android.com/reference/android/text/InputType#TYPE_NULL">android docs</a>.
*/ */
public boolean isLimited() { public boolean isLimited() {
@ -36,24 +36,44 @@ public class InputType {
* isSpecialNumeric * isSpecialNumeric
* Calculator and Dialer fields seem to take care of numbers and backspace on their own, * Calculator and Dialer fields seem to take care of numbers and backspace on their own,
* so we need to be aware of them. * so we need to be aware of them.
* * <p>
* NOTE: A Dialer field is not the same as Phone field. Dialer is where you * NOTE: A Dialer field is not the same as Phone field. Dialer is where you
* actually dial and call a phone number. While the Phone field is a text * actually dial and call a phone number. While the Phone field is a text
* field in any app or a webpage, intended for typing phone numbers. * field in any app or a webpage, intended for typing phone numbers.
* * <p>
* More info: <a href="https://github.com/sspanak/tt9/issues/46">in this Github issue</a> * More info: <a href="https://github.com/sspanak/tt9/issues/46">in this Github issue</a>
* and <a href="https://github.com/sspanak/tt9/pull/326">the PR about calculators</a>. * and <a href="https://github.com/sspanak/tt9/pull/326">the PR about calculators</a>.
*/ */
public boolean isSpecialNumeric() { public boolean isSpecialNumeric() {
if (field == null) {
return false;
}
int inputType = field.inputType & android.text.InputType.TYPE_MASK_CLASS;
return return
inputType == android.text.InputType.TYPE_CLASS_PHONE && field.packageName.equals("com.android.dialer") isPhoneNumber() && field.packageName.equals("com.android.dialer")
|| inputType == android.text.InputType.TYPE_CLASS_NUMBER && field.packageName.contains("com.android.calculator"); || isNumeric() && field.packageName.contains("com.android.calculator");
}
public boolean isPhoneNumber() {
return
field != null
&& (field.inputType & android.text.InputType.TYPE_MASK_CLASS) == android.text.InputType.TYPE_CLASS_PHONE;
}
public boolean isNumeric() {
return
field != null
&& (field.inputType & android.text.InputType.TYPE_MASK_CLASS) == android.text.InputType.TYPE_CLASS_NUMBER;
}
public boolean isDecimal() {
return
isNumeric()
&& (field.inputType & android.text.InputType.TYPE_MASK_FLAGS) == android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL;
}
public boolean isSignedNumber() {
return
isNumeric()
&& (field.inputType & android.text.InputType.TYPE_MASK_FLAGS) == android.text.InputType.TYPE_NUMBER_FLAG_SIGNED;
} }

View file

@ -12,7 +12,6 @@ import io.github.sspanak.tt9.preferences.SettingsStore;
abstract public class InputMode { abstract public class InputMode {
// typing mode // typing mode
public static final int MODE_UNDEFINED = -1;
public static final int MODE_PREDICTIVE = 0; public static final int MODE_PREDICTIVE = 0;
public static final int MODE_ABC = 1; public static final int MODE_ABC = 1;
public static final int MODE_123 = 2; public static final int MODE_123 = 2;
@ -34,7 +33,7 @@ abstract public class InputMode {
protected final ArrayList<String> suggestions = new ArrayList<>(); protected final ArrayList<String> suggestions = new ArrayList<>();
public static InputMode getInstance(SettingsStore settings, Language language, int mode) { public static InputMode getInstance(SettingsStore settings, Language language, InputType inputType, int mode) {
switch (mode) { switch (mode) {
case MODE_PREDICTIVE: case MODE_PREDICTIVE:
return new ModePredictive(settings, language); return new ModePredictive(settings, language);
@ -45,7 +44,7 @@ abstract public class InputMode {
default: default:
Logger.w("InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode); Logger.w("InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode);
case MODE_123: case MODE_123:
return new Mode123(); return new Mode123(inputType);
} }
} }

View file

@ -3,8 +3,10 @@ package io.github.sspanak.tt9.ime.modes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import io.github.sspanak.tt9.ime.helpers.InputType;
import io.github.sspanak.tt9.languages.Characters; import io.github.sspanak.tt9.languages.Characters;
public class Mode123 extends ModePassthrough { public class Mode123 extends ModePassthrough {
@ -18,7 +20,47 @@ public class Mode123 extends ModePassthrough {
private final ArrayList<ArrayList<String>> KEY_CHARACTERS = new ArrayList<>(); private final ArrayList<ArrayList<String>> KEY_CHARACTERS = new ArrayList<>();
public Mode123() {
public Mode123(InputType inputType) {
if (inputType.isPhoneNumber()) {
getPhoneSpecialCharacters();
} else if (inputType.isNumeric()) {
getNumberSpecialCharacters(inputType.isDecimal(), inputType.isSignedNumber());
} else {
getDefaultSpecialCharacters();
}
}
/**
* getPhoneSpecialCharacters
* Special characters for phone number fields, including both characters for conveniently typing a phone number: "()-",
* as well as command characters such as "," = "slight pause" and ";" = "wait" used in Japan and some other countries.
*/
private void getPhoneSpecialCharacters() {
KEY_CHARACTERS.add(new ArrayList<>(Arrays.asList("+", " ")));
KEY_CHARACTERS.add(new ArrayList<>(Arrays.asList("-", "(", ")", ".", ";", ",")));
}
/**
* getNumberSpecialCharacters
* Special characters for all kinds of numeric fields: integer, decimal with +/- included as necessary.
*/
private void getNumberSpecialCharacters(boolean decimal, boolean signed) {
KEY_CHARACTERS.add(signed ? new ArrayList<>(Arrays.asList("-", "+")) : new ArrayList<>());
if (decimal) {
KEY_CHARACTERS.add(new ArrayList<>(Arrays.asList(".", ",")));
}
}
/**
* getDefaultSpecialCharacters
* Special characters for when the user has selected 123 mode in a text field. In this case, we just
* use the default list, but reorder it a bit for convenience.
*/
private void getDefaultSpecialCharacters() {
// 0-key // 0-key
KEY_CHARACTERS.add(new ArrayList<>(Collections.singletonList("+"))); KEY_CHARACTERS.add(new ArrayList<>(Collections.singletonList("+")));
for (String character : Characters.Special) { for (String character : Characters.Special) {
@ -36,10 +78,11 @@ public class Mode123 extends ModePassthrough {
} }
} }
@Override public boolean onNumber(int number, boolean hold, int repeat) { @Override public boolean onNumber(int number, boolean hold, int repeat) {
reset(); reset();
if (hold && number < KEY_CHARACTERS.size()) { if (hold && number < KEY_CHARACTERS.size() && KEY_CHARACTERS.get(number).size() > 0) {
suggestions.addAll(KEY_CHARACTERS.get(number)); suggestions.addAll(KEY_CHARACTERS.get(number));
} else { } else {
autoAcceptTimeout = 0; autoAcceptTimeout = 0;
@ -49,6 +92,7 @@ public class Mode123 extends ModePassthrough {
return true; return true;
} }
/** /**
* shouldIgnoreText * shouldIgnoreText
* Since this is a numeric mode, we allow typing only numbers and: * Since this is a numeric mode, we allow typing only numbers and:

View file

@ -64,8 +64,13 @@ public class SoftNumberKey extends SoftKey {
int number = getNumber(getId()); int number = getNumber(getId());
// 0
if (number == 0) { if (number == 0) {
if (tt9.getInputMode() == InputMode.MODE_123) { if (tt9.isNumericModeSigned()) {
return "+/-";
} else if (tt9.isNumericModeStrict()) {
return null;
} else if (tt9.isInputModeNumeric()) {
return "+"; return "+";
} else { } else {
COMPLEX_LABEL_SUB_TITLE_SIZE = 1; COMPLEX_LABEL_SUB_TITLE_SIZE = 1;
@ -75,11 +80,11 @@ public class SoftNumberKey extends SoftKey {
// 1 // 1
if (number == 1) { if (number == 1) {
return ",:-)"; return tt9.isNumericModeStrict() ? null : ",:-)";
} }
// no other special labels in 123 mode // no other special labels in 123 mode
if (tt9.getInputMode() == InputMode.MODE_123) { if (tt9.isInputModeNumeric()) {
return null; return null;
} }

View file

@ -4,7 +4,6 @@ import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.ime.modes.InputMode;
public class SoftPunctuationKey extends SoftKey { public class SoftPunctuationKey extends SoftKey {
public SoftPunctuationKey(Context context) { public SoftPunctuationKey(Context context) {
@ -21,37 +20,39 @@ public class SoftPunctuationKey extends SoftKey {
@Override @Override
protected boolean handleRelease() { protected boolean handleRelease() {
if (!validateTT9Handler()) { return tt9.onText(getKeyChar());
return false;
}
int keyId = getId();
if (tt9.getInputMode() == InputMode.MODE_123) {
if (keyId == R.id.soft_key_punctuation_1) return tt9.onText("*");
if (keyId == R.id.soft_key_punctuation_2) return tt9.onText("#");
} else {
if (keyId == R.id.soft_key_punctuation_1) return tt9.onText("!");
if (keyId == R.id.soft_key_punctuation_2) return tt9.onText("?");
}
return true;
} }
@Override @Override
protected String getTitle() { protected String getTitle() {
if (tt9 == null) { String keyChar = getKeyChar();
return "PUNC"; switch (keyChar) {
case "":
return "PUNC";
case "*":
return "";
default:
return keyChar;
}
}
private String getKeyChar() {
if (!validateTT9Handler()) {
return "";
} }
int keyId = getId(); int keyId = getId();
if (tt9.getInputMode() == InputMode.MODE_123) { if (tt9.isInputModePhone()) {
if (keyId == R.id.soft_key_punctuation_1) return ""; if (keyId == R.id.soft_key_punctuation_1) return "*";
if (keyId == R.id.soft_key_punctuation_2) return "#"; if (keyId == R.id.soft_key_punctuation_2) return "#";
} else if (tt9.isInputModeNumeric()) {
if (keyId == R.id.soft_key_punctuation_1) return ",";
if (keyId == R.id.soft_key_punctuation_2) return ".";
} else { } else {
if (keyId == R.id.soft_key_punctuation_1) return "!"; if (keyId == R.id.soft_key_punctuation_1) return "!";
if (keyId == R.id.soft_key_punctuation_2) return "?"; if (keyId == R.id.soft_key_punctuation_2) return "?";
} }
return "PUNC"; return "";
} }
} }