1
0
Fork 0

Fixed unusable hotkeys and typing issues in very specific cases (#258)

* fixed '*', '#', "." and "," not working properly in 123 mode

* fixed typing "+" by holding 0-key not working in Dialer fields

* fixed Predictive mode not allowing the same key for typing and as a hotkey, when being pressed and held, respectively.

* removed EDITING_STRICT_NUMERIC, EDITING_DIALER and deprecated mEditing

* added ModeDialer
This commit is contained in:
Dimo Karaivanov 2023-05-13 08:51:55 +03:00 committed by GitHub
parent 8949c65f4f
commit 008590bdf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 142 additions and 71 deletions

View file

@ -177,6 +177,10 @@ abstract class KeyPadHandler extends InputMethodService {
return onNumber(Key.codeToNumber(settings, keyCode), true, 0);
}
if (Key.isPoundOrStar(keyCode) && onOtherKey(keyCode)) {
return true;
}
ignoreNextKeyUp = 0;
return super.onKeyLongPress(keyCode, event);
}
@ -234,7 +238,9 @@ abstract class KeyPadHandler extends InputMethodService {
case KeyEvent.KEYCODE_DPAD_RIGHT: return onRight(keyRepeatCounter > 0);
case KeyEvent.KEYCODE_STAR:
case KeyEvent.KEYCODE_POUND:
return onOtherKey(keyCode);
if (onOtherKey(keyCode)) {
return true;
}
}
return super.onKeyUp(keyCode, event);

View file

@ -14,7 +14,6 @@ import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.db.DictionaryDb;
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
import io.github.sspanak.tt9.ime.helpers.InputType;
import io.github.sspanak.tt9.ime.helpers.Key;
import io.github.sspanak.tt9.ime.helpers.TextField;
import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.languages.Language;
@ -34,9 +33,7 @@ public class TraditionalT9 extends KeyPadHandler {
// editing mode
protected static final int NON_EDIT = 0;
protected static final int EDITING = 1;
protected static final int EDITING_STRICT_NUMERIC = 3;
protected static final int EDITING_DIALER = 4; // see: https://github.com/sspanak/tt9/issues/46
protected int mEditing = NON_EDIT;
@Deprecated protected int mEditing = NON_EDIT; // @todo: migrate to "isActive"
// input mode
private ArrayList<Integer> allowedInputModes = new ArrayList<>();
@ -185,7 +182,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 || !textField.isThereText()) {
if (mInputMode.isDialer() || !textField.isThereText()) {
Logger.d("onBackspace", "backspace ignored");
mInputMode.reset();
return false;
@ -316,21 +313,20 @@ public class TraditionalT9 extends KeyPadHandler {
public boolean onOtherKey(int keyCode) {
if (
keyCode <= 0 ||
(mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) && !Key.isNumber(keyCode)
) {
return false;
String acceptedWord = acceptIncompleteSuggestion();
if (mInputMode.onOtherKey(keyCode)) {
autoCorrectSpace(acceptedWord, false);
getSuggestions();
resetKeyRepeat();
return true;
}
autoCorrectSpace(acceptIncompleteSuggestion(), false);
sendDownUpKeyEvents(keyCode);
return true;
return acceptedWord.length() > 0;
}
public boolean onText(String text) {
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER || text.length() == 0) {
if (mInputMode.isNumeric() || text.length() == 0) {
return false;
}
@ -346,7 +342,7 @@ public class TraditionalT9 extends KeyPadHandler {
public boolean onKeyAddWord() {
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
if (!isInputViewShown() || mInputMode.isNumeric()) {
return false;
}
@ -376,14 +372,19 @@ public class TraditionalT9 extends KeyPadHandler {
public boolean onKeyNextInputMode() {
nextInputMode();
if (allowedInputModes.size() == 1) {
return false;
}
mainView.render();
forceShowWindowIfHidden();
return (mEditing != EDITING_STRICT_NUMERIC && mEditing != EDITING_DIALER);
return true;
}
public boolean onKeyShowSettings() {
if (mEditing == EDITING_DIALER) {
if (!isInputViewShown()) {
return false;
}
@ -393,11 +394,11 @@ public class TraditionalT9 extends KeyPadHandler {
protected boolean shouldTrackUpDown() {
return mEditing != EDITING_STRICT_NUMERIC && !isSuggestionViewHidden() && mInputMode.shouldTrackUpDown();
return !isSuggestionViewHidden() && mInputMode.shouldTrackUpDown();
}
protected boolean shouldTrackLeftRight() {
return mEditing != EDITING_STRICT_NUMERIC && !isSuggestionViewHidden() && mInputMode.shouldTrackLeftRight();
return !isSuggestionViewHidden() && mInputMode.shouldTrackLeftRight();
}
@ -474,7 +475,8 @@ public class TraditionalT9 extends KeyPadHandler {
// key code "suggestions" take priority over words
if (mInputMode.getKeyCode() > 0) {
sendDownUpKeyEvents(mInputMode.getKeyCode());
mInputMode.onAcceptSuggestion(null);
mInputMode.onAcceptSuggestion("");
return;
}
// display the list of suggestions
@ -522,7 +524,9 @@ public class TraditionalT9 extends KeyPadHandler {
private void nextInputMode() {
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
if (mInputMode.isDialer()) {
return;
} else if (allowedInputModes.size() == 1 && allowedInputModes.contains(InputMode.MODE_123)) {
mInputMode = !mInputMode.is123() ? InputMode.getInstance(settings, mLanguage, InputMode.MODE_123) : mInputMode;
}
// when typing a word or viewing scrolling the suggestions, only change the case
@ -562,7 +566,7 @@ public class TraditionalT9 extends KeyPadHandler {
private boolean nextLang() {
if (mInputMode.is123() || mEnabledLanguages.size() < 2) {
if (mInputMode.isNumeric() || mEnabledLanguages.size() < 2) {
return false;
}
@ -597,20 +601,16 @@ public class TraditionalT9 extends KeyPadHandler {
int lastInputModeId = settings.getInputMode();
if (allowedInputModes.contains(lastInputModeId)) {
mInputMode = InputMode.getInstance(settings, mLanguage, lastInputModeId);
} else if (allowedInputModes.size() == 1 && allowedInputModes.get(0) == InputMode.MODE_DIALER) {
mInputMode = InputMode.getInstance(settings, mLanguage, InputMode.MODE_DIALER);
} else if (allowedInputModes.contains(InputMode.MODE_ABC)) {
mInputMode = InputMode.getInstance(settings, mLanguage, InputMode.MODE_ABC);
} else {
mInputMode = InputMode.getInstance(settings, mLanguage, allowedInputModes.get(0));
}
if (inputType.isDialer()) {
mEditing = EDITING_DIALER;
} else if (mInputMode.is123() && allowedInputModes.size() == 1) {
mEditing = EDITING_STRICT_NUMERIC;
} else {
mEditing = EDITING;
}
}
private void autoCorrectSpace(String currentWord, boolean isWordAcceptedManually) {
@ -721,8 +721,7 @@ public class TraditionalT9 extends KeyPadHandler {
*/
protected void forceShowWindowIfHidden() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& mEditing != EDITING_STRICT_NUMERIC
&& mEditing != EDITING_DIALER
&& !mInputMode.isDialer()
&& !isInputViewShown()
) {
requestShowSelf(InputMethodManager.SHOW_IMPLICIT);
@ -732,7 +731,7 @@ public class TraditionalT9 extends KeyPadHandler {
@Override
protected boolean shouldBeVisible() {
return mEditing != EDITING_DIALER && mEditing != NON_EDIT;
return !mInputMode.isDialer() && mEditing != NON_EDIT;
}

View file

@ -25,7 +25,7 @@ public class InputType {
* 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.
*
* <a href="https://developer.android.com/reference/android/text/InputType#TYPE_NULL">...</a>
* More info: <a href="https://developer.android.com/reference/android/text/InputType#TYPE_NULL">android docs</a>.
*/
public boolean isLimited() {
return field != null && field.inputType == android.text.InputType.TYPE_NULL;
@ -37,7 +37,11 @@ public class InputType {
* 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.
* 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
* field in any app or a webpage, intended for typing phone numbers.
*
* More info: <a href="https://github.com/sspanak/tt9/issues/46">in this Github issue</a>.
*/
public boolean isDialer() {
if (field == null) {

View file

@ -21,6 +21,17 @@ public class Key {
}
public static boolean isPoundOrStar(int keyCode) {
return keyCode == KeyEvent.KEYCODE_POUND || keyCode == KeyEvent.KEYCODE_STAR;
}
public static boolean isDecimalSeparator(int keyCode) {
return
keyCode == KeyEvent.KEYCODE_COMMA
|| keyCode == KeyEvent.KEYCODE_NUMPAD_DOT
|| keyCode == KeyEvent.KEYCODE_PERIOD;
}
public static boolean isOK(int keyCode) {
return
keyCode == KeyEvent.KEYCODE_DPAD_CENTER

View file

@ -90,14 +90,19 @@ public class TextField {
return allowedModes;
}
// Dialer field, not to be confused with Phone text field.
// It only accepts 0-9, "#" and "*".
if (inputType.isDialer()) {
allowedModes.add(InputMode.MODE_DIALER);
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.
// Numbers, dates and phone numbers default to the numeric keyboard,
// with no extra features.
allowedModes.add(InputMode.MODE_123);
return allowedModes;

View file

@ -1,5 +1,7 @@
package io.github.sspanak.tt9.ime.modes;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import io.github.sspanak.tt9.Logger;
@ -13,6 +15,7 @@ abstract public class InputMode {
public static final int MODE_PREDICTIVE = 0;
public static final int MODE_ABC = 1;
public static final int MODE_123 = 2;
public static final int MODE_DIALER = 4;
// text case
public static final int CASE_UNDEFINED = -1;
@ -37,6 +40,8 @@ abstract public class InputMode {
return new ModePredictive(settings, language);
case MODE_ABC:
return new ModeABC(language);
case MODE_DIALER:
return new ModeDialer();
default:
Logger.w("tt9/InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode);
case MODE_123:
@ -47,18 +52,19 @@ abstract public class InputMode {
// Key handlers. Return "true" when handling the key or "false", when is nothing to do.
public boolean onBackspace() { return false; }
abstract public boolean onNumber(int number, boolean hold, int repeat);
abstract public boolean onOtherKey(int key);
// Suggestions
public void onAcceptSuggestion(String suggestion) {}
public void onAcceptSuggestion(@NonNull String suggestion) {}
/**
* loadSuggestions
* Loads the suggestions based on the current state, with optional "currentWord" filter.
* Once loading is finished the respective InputMode child will call "notification", notifying it
* Once loading is finished the respective InputMode child will call "onLoad", notifying it
* the suggestions are available using "getSuggestions()".
*/
public void loadSuggestions(Runnable notification, String currentWord) {
notification.run();
public void loadSuggestions(Runnable onLoad, String currentWord) {
onLoad.run();
}
public ArrayList<String> getSuggestions() {
@ -71,9 +77,10 @@ abstract public class InputMode {
}
// Mode identifiers
public boolean isPredictive() { return false; }
public boolean isABC() { return false; }
public boolean is123() { return false; }
public boolean isDialer() { return false; }
public boolean isNumeric() { return false; }
// Utility
abstract public int getId();

View file

@ -1,5 +1,7 @@
package io.github.sspanak.tt9.ime.modes;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import io.github.sspanak.tt9.ime.helpers.Key;
@ -14,19 +16,23 @@ public class Mode123 extends InputMode {
@Override
public boolean onNumber(int number, boolean hold, int repeat) {
reset();
if (number == 0 && hold) {
autoAcceptTimeout = 0;
suggestions.add("+");
} else {
keyCode = Key.numberToCode(number);
}
keyCode = (number == 0 && hold) ? KeyEvent.KEYCODE_PLUS : Key.numberToCode(number);
return true;
}
@Override
public boolean onOtherKey(int key) {
reset();
if (Key.isDecimalSeparator(key) || Key.isPoundOrStar(key)) {
keyCode = key;
return true;
}
@Override final public boolean is123() { return true; }
return false;
}
@Override public boolean is123() { return true; }
@Override final public boolean isNumeric() { return true; }
@Override public int getSequenceLength() { return 0; }
@NonNull

View file

@ -31,11 +31,25 @@ public class ModeABC extends InputMode {
}
@Override
public boolean onOtherKey(int key) {
reset();
if (key > 0) {
keyCode = key;
return true;
}
return false;
}
@Override
protected String adjustSuggestionTextCase(String word, int newTextCase) {
return newTextCase == CASE_UPPER ? word.toUpperCase(language.getLocale()) : word.toLowerCase(language.getLocale());
}
@Override
public void changeLanguage(Language language) {
super.changeLanguage(language);

View file

@ -0,0 +1,15 @@
package io.github.sspanak.tt9.ime.modes;
import io.github.sspanak.tt9.ime.helpers.Key;
// see: InputType.isDialer()
public class ModeDialer extends Mode123 {
@Override public int getId() { return MODE_DIALER; }
@Override public boolean is123() { return false; }
@Override final public boolean isDialer() { return true; }
@Override
public boolean onOtherKey(int key) {
return !Key.isDecimalSeparator(key) && super.onOtherKey(key);
}
}

View file

@ -86,6 +86,20 @@ public class ModePredictive extends InputMode {
}
@Override
public boolean onOtherKey(int key) {
reset();
if (key > 0) {
disablePredictions = true;
keyCode = key;
return true;
}
return false;
}
@Override
public void changeLanguage(Language language) {
super.changeLanguage(language);
@ -191,13 +205,13 @@ public class ModePredictive extends InputMode {
* See: Predictions.generatePossibleCompletions()
*/
@Override
public void loadSuggestions(Runnable handler, String currentWord) {
public void loadSuggestions(Runnable onLoad, String currentWord) {
if (disablePredictions) {
super.loadSuggestions(handler, currentWord);
super.loadSuggestions(onLoad, currentWord);
return;
}
onSuggestionsUpdated = handler;
onSuggestionsUpdated = onLoad;
predictions
.setDigitSequence(digitSequence)
.setIsStemFuzzy(isStemFuzzy)
@ -227,7 +241,7 @@ public class ModePredictive extends InputMode {
* Bring this word up in the suggestions list next time.
*/
@Override
public void onAcceptSuggestion(String currentWord) {
public void onAcceptSuggestion(@NonNull String currentWord) {
lastAcceptedWord = currentWord;
lastAcceptedSequence = digitSequence;
reset();
@ -314,7 +328,6 @@ public class ModePredictive extends InputMode {
@Override public boolean shouldTrackUpDown() { return true; }
@Override public boolean shouldTrackLeftRight() { return true; }
@Override final public boolean isPredictive() { return true; }
@Override public int getSequenceLength() { return digitSequence.length(); }
@NonNull

View file

@ -102,18 +102,9 @@ public class Hotkeys {
*
* NOTE: Some TT9 functions do not support all keys. Here you just list all possible options.
* Actual validation and assigning happens in SectionKeymap.populate().
*
* NOTE 2: Holding is deliberately skipped for most of the keys.
* It's because handling holding requires short press event to be consumed in
* KeyPadHandler, as well.
*
* From user perspective, when holding is assigned to a function,
* short press will also stop performing its default system action, which may be confusing.
* And in order to avoid lengthy explanations in the documentation (that no one reads),
* the problem is avoided by simply not causing it.
*/
private void generateList() {
add(KeyEvent.KEYCODE_CALL, R.string.key_call, false);
add(KeyEvent.KEYCODE_CALL, R.string.key_call, true);
addIfDeviceHasKey(KeyEvent.KEYCODE_BACK, R.string.key_back, false);
addIfDeviceHasKey(KeyEvent.KEYCODE_CLEAR, R.string.key_clear, false);
@ -122,7 +113,7 @@ public class Hotkeys {
addIfDeviceHasKey(KeyEvent.KEYCODE_F2, "F2", true);
addIfDeviceHasKey(KeyEvent.KEYCODE_F3, "F3", true);
addIfDeviceHasKey(KeyEvent.KEYCODE_F4, "F4", true);
addIfDeviceHasKey(KeyEvent.KEYCODE_MENU, R.string.key_menu, false);
addIfDeviceHasKey(KeyEvent.KEYCODE_MENU, R.string.key_menu, true);
addIfDeviceHasKey(KeyEvent.KEYCODE_SOFT_LEFT, R.string.key_soft_left, false);
addIfDeviceHasKey(KeyEvent.KEYCODE_SOFT_RIGHT, R.string.key_soft_right, false);

View file

@ -29,8 +29,8 @@ public class SoftPunctuationKey extends SoftKey {
preventRepeat();
int keyId = getId();
if (keyId == R.id.soft_key_punctuation_1) return tt9.onText(",");
if (keyId == R.id.soft_key_punctuation_2) return tt9.onText(".");
if (keyId == R.id.soft_key_punctuation_1) return tt9.onOtherKey(KeyEvent.KEYCODE_COMMA);
if (keyId == R.id.soft_key_punctuation_2) return tt9.onOtherKey(KeyEvent.KEYCODE_PERIOD);
return false;
}