improved support for phone, decimal and signed integer numeric fields
This commit is contained in:
parent
5ece90cd21
commit
81df61b900
6 changed files with 125 additions and 44 deletions
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue