Touchscreen support + small improvements
* Soft-Keyboard numpad * no more SoftKeyHandler, the main view is in its own package * settings are no longer passed unnecessarilly here and there * fixed numeric mode not working in some cases * simplified suggestion handling * fixed crashing when changing the phone orientation
This commit is contained in:
parent
7f6cd6110d
commit
4e5416f6b4
36 changed files with 1142 additions and 368 deletions
|
|
@ -143,36 +143,17 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
isBackspaceHandled = false;
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In numeric fields, we do not want to handle anything, but "backspace"
|
||||
if (mEditing == EDITING_STRICT_NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// holding "0" is important in all cases
|
||||
if (keyCode == KeyEvent.KEYCODE_0) {
|
||||
event.startTracking();
|
||||
return true;
|
||||
}
|
||||
|
||||
// In dialer fields we just want passthrough, but we do handle holding "0",
|
||||
// to convert it to "+".
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// start tracking key hold
|
||||
if (shouldTrackNumPress() || Key.isHotkey(settings, -keyCode)) {
|
||||
if (Key.isNumber(keyCode) || Key.isHotkey(settings, -keyCode)) {
|
||||
event.startTracking();
|
||||
}
|
||||
|
||||
return Key.isHotkey(settings, keyCode) || Key.isHotkey(settings, -keyCode)
|
||||
return
|
||||
Key.isNumber(keyCode)
|
||||
|| Key.isOK(keyCode)
|
||||
|| Key.isHotkey(settings, keyCode) || Key.isHotkey(settings, -keyCode)
|
||||
|| keyCode == KeyEvent.KEYCODE_STAR
|
||||
|| keyCode == KeyEvent.KEYCODE_POUND
|
||||
|| (Key.isNumber(keyCode) && shouldTrackNumPress())
|
||||
|| ((keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) && shouldTrackUpDown())
|
||||
|| ((keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) && shouldTrackLeftRight());
|
||||
}
|
||||
|
|
@ -217,12 +198,15 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount());
|
||||
|
||||
if (keyCode == ignoreNextKeyUp) {
|
||||
// Logger.d("onKeyUp", "Ignored: " + keyCode);
|
||||
ignoreNextKeyUp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// repeat handling
|
||||
keyRepeatCounter = (lastKeyCode == keyCode) ? keyRepeatCounter + 1 : 0;
|
||||
lastKeyCode = keyCode;
|
||||
|
||||
|
|
@ -231,46 +215,31 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
lastNumKeyCode = keyCode;
|
||||
}
|
||||
|
||||
// Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount());
|
||||
|
||||
// backspace is handled in onKeyDown only, so we ignore it here
|
||||
if (isBackspaceHandled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return onOK();
|
||||
}
|
||||
|
||||
// in numeric fields, we just handle backspace and let the rest go as-is.
|
||||
if (mEditing == EDITING_STRICT_NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_0) {
|
||||
return onNumber(Key.codeToNumber(settings, keyCode), false, numKeyRepeatCounter);
|
||||
}
|
||||
|
||||
// dialer fields are similar to pure numeric fields, but for user convenience, holding "0"
|
||||
// is converted to "+"
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handleHotkey(keyCode, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Key.isNumber(keyCode)) {
|
||||
return onNumber(Key.codeToNumber(settings, keyCode), false, numKeyRepeatCounter);
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return onOK();
|
||||
}
|
||||
|
||||
if (handleHotkey(keyCode, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_DPAD_UP: return onUp();
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN: return onDown();
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT: return onLeft();
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT: return onRight(keyRepeatCounter > 0);
|
||||
case KeyEvent.KEYCODE_STAR: return onStar();
|
||||
case KeyEvent.KEYCODE_POUND: return onPound();
|
||||
case KeyEvent.KEYCODE_STAR:
|
||||
case KeyEvent.KEYCODE_POUND:
|
||||
return onOtherKey(keyCode);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -324,8 +293,7 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
abstract protected boolean onLeft();
|
||||
abstract protected boolean onRight(boolean repeat);
|
||||
abstract protected boolean onNumber(int key, boolean hold, int repeat);
|
||||
abstract protected boolean onStar();
|
||||
abstract protected boolean onPound();
|
||||
abstract protected boolean onOtherKey(int keyCode);
|
||||
|
||||
// customized key handlers
|
||||
abstract protected boolean onKeyAddWord();
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
package io.github.sspanak.tt9.ime;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ui.UI;
|
||||
|
||||
class SoftKeyHandler implements View.OnTouchListener {
|
||||
private static final int[] buttons = { R.id.main_left, R.id.main_mid, R.id.main_right };
|
||||
private final TraditionalT9 tt9;
|
||||
private View view = null;
|
||||
|
||||
private long lastBackspaceCall = 0;
|
||||
|
||||
public SoftKeyHandler(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
|
||||
getView();
|
||||
}
|
||||
|
||||
|
||||
View getView() {
|
||||
if (view == null) {
|
||||
view = View.inflate(tt9.getApplicationContext(), R.layout.mainview, null);
|
||||
|
||||
for (int buttonId : buttons) {
|
||||
view.findViewById(buttonId).setOnTouchListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
void show() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hide() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setSoftKeysVisibility(boolean visible) {
|
||||
if (view != null) {
|
||||
view.findViewById(R.id.main_soft_keys).setVisibility(visible ? LinearLayout.VISIBLE : LinearLayout.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** setDarkTheme
|
||||
* Changes the main view colors according to the theme.
|
||||
*
|
||||
* We need to do this manually, instead of relying on the Context to resolve the appropriate colors,
|
||||
* because this View is part of the main service View. And service Views are always locked to the
|
||||
* system context and theme.
|
||||
*
|
||||
* More info:
|
||||
* https://stackoverflow.com/questions/72382886/system-applies-night-mode-to-views-added-in-service-type-application-overlay
|
||||
*/
|
||||
void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.findViewById(R.id.main_soft_keys).setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_background_dark : R.drawable.button_background
|
||||
));
|
||||
|
||||
// text
|
||||
int textColor = ContextCompat.getColor(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.color.dark_button_text : R.color.button_text
|
||||
);
|
||||
|
||||
for (int buttonId : buttons) {
|
||||
Button button = view.findViewById(buttonId);
|
||||
button.setTextColor(textColor);
|
||||
}
|
||||
|
||||
// separators
|
||||
Drawable separatorColor = ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_separator_dark : R.drawable.button_separator
|
||||
);
|
||||
|
||||
view.findViewById(R.id.main_separator_left).setBackground(separatorColor);
|
||||
view.findViewById(R.id.main_separator_right).setBackground(separatorColor);
|
||||
}
|
||||
|
||||
|
||||
private boolean handleBackspaceHold() {
|
||||
if (System.currentTimeMillis() - lastBackspaceCall < tt9.settings.getSoftKeyRepeatDelay()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean handled = tt9.onBackspace();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
lastBackspaceCall = lastBackspaceCall == 0 ? tt9.settings.getSoftKeyInitialDelay() + now : now;
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
private boolean handleBackspaceUp() {
|
||||
lastBackspaceCall = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
int action = event.getAction();
|
||||
int buttonId = view.getId();
|
||||
|
||||
if (buttonId == R.id.main_left && action == MotionEvent.ACTION_UP) {
|
||||
UI.showSettingsScreen(tt9);
|
||||
return view.performClick();
|
||||
}
|
||||
|
||||
if (buttonId == R.id.main_mid && action == MotionEvent.ACTION_UP) {
|
||||
tt9.onOK();
|
||||
return view.performClick();
|
||||
}
|
||||
|
||||
if (buttonId == R.id.main_right) {
|
||||
if (action == MotionEvent.AXIS_PRESSURE) {
|
||||
return handleBackspaceHold();
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
return handleBackspaceUp();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,13 +17,16 @@ 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;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.ui.bottom.StatusBar;
|
||||
import io.github.sspanak.tt9.ui.bottom.SuggestionsBar;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
import io.github.sspanak.tt9.ui.UI;
|
||||
import io.github.sspanak.tt9.ui.main.MainView;
|
||||
import io.github.sspanak.tt9.ui.tray.StatusBar;
|
||||
import io.github.sspanak.tt9.ui.tray.SuggestionsBar;
|
||||
|
||||
public class TraditionalT9 extends KeyPadHandler {
|
||||
// internal settings/data
|
||||
|
|
@ -40,7 +43,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected Language mLanguage;
|
||||
|
||||
// soft key view
|
||||
private SoftKeyHandler softKeyHandler = null;
|
||||
private MainView mainView = null;
|
||||
private StatusBar statusBar = null;
|
||||
private SuggestionsBar suggestionBar = null;
|
||||
|
||||
|
|
@ -50,6 +53,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
return self.getApplicationContext();
|
||||
}
|
||||
|
||||
public SettingsStore getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
private void loadSettings() {
|
||||
mLanguage = LanguageCollection.getLanguage(settings.getInputLanguage());
|
||||
|
|
@ -80,16 +87,9 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
DictionaryDb.init(this);
|
||||
DictionaryDb.normalizeWordFrequencies(settings);
|
||||
|
||||
if (softKeyHandler == null) {
|
||||
softKeyHandler = new SoftKeyHandler(this);
|
||||
}
|
||||
|
||||
if (statusBar == null) {
|
||||
statusBar = new StatusBar(softKeyHandler.getView());
|
||||
}
|
||||
|
||||
if (suggestionBar == null) {
|
||||
suggestionBar = new SuggestionsBar(settings, softKeyHandler.getView());
|
||||
if (mainView == null) {
|
||||
mainView = new MainView(this);
|
||||
initTray();
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
|
@ -115,17 +115,28 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private void initUi() {
|
||||
statusBar
|
||||
.setText(mInputMode != null ? mInputMode.toString() : "")
|
||||
.setDarkTheme(settings.getDarkTheme());
|
||||
private void initTray() {
|
||||
setInputView(mainView.getView());
|
||||
statusBar = new StatusBar(mainView.getView());
|
||||
suggestionBar = new SuggestionsBar(this, mainView.getView());
|
||||
}
|
||||
|
||||
clearSuggestions();
|
||||
|
||||
private void setDarkTheme() {
|
||||
mainView.setDarkTheme(settings.getDarkTheme());
|
||||
statusBar.setDarkTheme(settings.getDarkTheme());
|
||||
suggestionBar.setDarkTheme(settings.getDarkTheme());
|
||||
}
|
||||
|
||||
softKeyHandler.setDarkTheme(settings.getDarkTheme());
|
||||
softKeyHandler.setSoftKeysVisibility(settings.getShowSoftKeys());
|
||||
softKeyHandler.show();
|
||||
|
||||
private void initUi() {
|
||||
if (mainView.createView()) {
|
||||
initTray();
|
||||
}
|
||||
clearSuggestions();
|
||||
statusBar.setText(mInputMode != null ? mInputMode.toString() : "");
|
||||
setDarkTheme();
|
||||
mainView.render();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -163,8 +174,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected void onStop() {
|
||||
onFinishTyping();
|
||||
clearSuggestions();
|
||||
|
||||
softKeyHandler.hide();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -193,10 +202,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
public boolean onOK() {
|
||||
if (!textField.isThereText()) {
|
||||
if (!isInputViewShown() && !textField.isThereText()) {
|
||||
forceShowWindowIfHidden();
|
||||
return true;
|
||||
} else if (isSuggestionViewHidden() && currentInputConnection != null) {
|
||||
} else if (isSuggestionViewHidden()) {
|
||||
return performOKAction();
|
||||
}
|
||||
|
||||
|
|
@ -275,12 +284,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
String currentWord = getComposingText();
|
||||
|
||||
// Automatically accept the current word, when the next one is a space or whatnot,
|
||||
// Automatically accept the current word, when the next one is a space or punctuation,
|
||||
// instead of requiring "OK" before that.
|
||||
if (mInputMode.shouldAcceptCurrentSuggestion(key, hold, repeat > 0)) {
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
commitCurrentSuggestion(false);
|
||||
autoCorrectSpace(currentWord, false, key, hold, repeat > 0);
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, key, hold, repeat > 0);
|
||||
currentWord = "";
|
||||
}
|
||||
|
||||
|
|
@ -296,17 +303,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
if (mInputMode.shouldSelectNextSuggestion() && !isSuggestionViewHidden()) {
|
||||
nextSuggestion();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mInputMode.getWord() != null) {
|
||||
currentWord = mInputMode.getWord();
|
||||
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
textField.setText(currentWord);
|
||||
clearSuggestions();
|
||||
autoCorrectSpace(currentWord, true, key, hold, repeat > 0);
|
||||
resetKeyRepeat();
|
||||
} else {
|
||||
getSuggestions();
|
||||
}
|
||||
|
|
@ -315,19 +311,33 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onPound() {
|
||||
textField.setText("#");
|
||||
public boolean onOtherKey(int keyCode) {
|
||||
if (
|
||||
keyCode <= 0 ||
|
||||
(mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) && !Key.isNumber(keyCode)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, -1, false, false);
|
||||
sendDownUpKeyEvents(keyCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected boolean onStar() {
|
||||
textField.setText("*");
|
||||
public boolean onText(String text) {
|
||||
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER || text.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, -1, false, false);
|
||||
textField.setText(text);
|
||||
autoCorrectSpace(text, false, -1, false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected boolean onKeyAddWord() {
|
||||
public boolean onKeyAddWord() {
|
||||
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -337,7 +347,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onKeyNextLanguage() {
|
||||
public boolean onKeyNextLanguage() {
|
||||
if (nextLang()) {
|
||||
commitCurrentSuggestion(false);
|
||||
mInputMode.changeLanguage(mLanguage);
|
||||
|
|
@ -345,6 +355,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
resetKeyRepeat();
|
||||
clearSuggestions();
|
||||
statusBar.setText(mInputMode.toString());
|
||||
mainView.render();
|
||||
forceShowWindowIfHidden();
|
||||
|
||||
return true;
|
||||
|
|
@ -354,14 +365,15 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onKeyNextInputMode() {
|
||||
public boolean onKeyNextInputMode() {
|
||||
nextInputMode();
|
||||
mainView.render();
|
||||
forceShowWindowIfHidden();
|
||||
return (mEditing != EDITING_STRICT_NUMERIC && mEditing != EDITING_DIALER);
|
||||
}
|
||||
|
||||
|
||||
protected boolean onKeyShowSettings() {
|
||||
public boolean onKeyShowSettings() {
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -414,6 +426,15 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private String acceptIncompleteSuggestion() {
|
||||
String currentWord = getComposingText();
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
commitCurrentSuggestion(false);
|
||||
|
||||
return currentWord;
|
||||
}
|
||||
|
||||
|
||||
private void commitCurrentSuggestion() {
|
||||
commitCurrentSuggestion(true);
|
||||
}
|
||||
|
|
@ -448,9 +469,22 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private void handleSuggestions() {
|
||||
// key code "suggestions" take priority over words
|
||||
if (mInputMode.getKeyCode() > 0) {
|
||||
sendDownUpKeyEvents(mInputMode.getKeyCode());
|
||||
mInputMode.onAcceptSuggestion(null);
|
||||
}
|
||||
|
||||
// display the list of suggestions
|
||||
setSuggestions(mInputMode.getSuggestions());
|
||||
|
||||
// Put the first suggestion in the text field,
|
||||
// flush the first suggestion immediately, if the InputMode has requested it
|
||||
if (mInputMode.getAutoAcceptTimeout() == 0) {
|
||||
onOK();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, put the first suggestion in the text field,
|
||||
// but cut it off to the length of the sequence (how many keys were pressed),
|
||||
// for a more intuitive experience.
|
||||
String word = suggestionBar.getCurrentSuggestion();
|
||||
|
|
@ -615,6 +649,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private boolean performOKAction() {
|
||||
if (currentInputConnection == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int action = textField.getAction();
|
||||
switch (action) {
|
||||
case EditorInfo.IME_ACTION_NONE:
|
||||
|
|
@ -684,7 +722,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
* Generates the actual UI of TT9.
|
||||
*/
|
||||
protected View createSoftKeyView() {
|
||||
return softKeyHandler.getView();
|
||||
mainView.forceCreateView();
|
||||
initTray();
|
||||
setDarkTheme();
|
||||
return mainView.getView();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -65,4 +65,12 @@ public class Key {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int numberToCode(int number) {
|
||||
if (number >= 0 && number <= 9) {
|
||||
return KeyEvent.KEYCODE_0 + number;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ abstract public class InputMode {
|
|||
protected int textFieldTextCase = CASE_UNDEFINED;
|
||||
|
||||
// data
|
||||
protected int autoAcceptTimeout = -1;
|
||||
protected Language language;
|
||||
protected ArrayList<String> suggestions = new ArrayList<>();
|
||||
protected String word = null;
|
||||
protected int keyCode = 0;
|
||||
|
||||
|
||||
public static InputMode getInstance(SettingsStore settings, Language language, int mode) {
|
||||
|
|
@ -47,7 +48,7 @@ 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 key, boolean hold, int repeat);
|
||||
abstract public boolean onNumber(int number, boolean hold, int repeat);
|
||||
|
||||
// Predictions
|
||||
public void onAcceptSuggestion(String suggestion) {}
|
||||
|
|
@ -63,9 +64,6 @@ abstract public class InputMode {
|
|||
return newSuggestions;
|
||||
}
|
||||
|
||||
// Word
|
||||
public String getWord() { return word; }
|
||||
|
||||
// Mode identifiers
|
||||
public boolean isPredictive() { return false; }
|
||||
public boolean isABC() { return false; }
|
||||
|
|
@ -74,6 +72,10 @@ abstract public class InputMode {
|
|||
// Utility
|
||||
abstract public int getId();
|
||||
abstract public int getSequenceLength(); // The number of key presses for the current word.
|
||||
public int getAutoAcceptTimeout() {
|
||||
return autoAcceptTimeout;
|
||||
}
|
||||
public int getKeyCode() { return keyCode; }
|
||||
public void changeLanguage(Language newLanguage) {
|
||||
if (newLanguage != null) {
|
||||
language = newLanguage;
|
||||
|
|
@ -90,8 +92,9 @@ abstract public class InputMode {
|
|||
public boolean shouldTrackLeftRight() { return false; }
|
||||
|
||||
public void reset() {
|
||||
suggestions = new ArrayList<>();
|
||||
word = null;
|
||||
autoAcceptTimeout = -1;
|
||||
keyCode = 0;
|
||||
suggestions.clear();
|
||||
}
|
||||
|
||||
// Text case
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package io.github.sspanak.tt9.ime.modes;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
|
||||
public class Mode123 extends InputMode {
|
||||
public int getId() { return MODE_123; }
|
||||
|
|
@ -11,21 +11,24 @@ public class Mode123 extends InputMode {
|
|||
allowedTextCases.add(CASE_LOWER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
reset();
|
||||
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
if (key != 0) {
|
||||
return false;
|
||||
if (number == 0 && hold) {
|
||||
autoAcceptTimeout = 0;
|
||||
suggestions.add("+");
|
||||
} else {
|
||||
keyCode = Key.numberToCode(number);
|
||||
}
|
||||
|
||||
suggestions = new ArrayList<>();
|
||||
word = hold ? "+" : "0";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
final public boolean is123() { return true; }
|
||||
public int getSequenceLength() { return 0; }
|
||||
public boolean shouldTrackNumPress() { return false; }
|
||||
@Override final public boolean is123() { return true; }
|
||||
@Override public int getSequenceLength() { return 0; }
|
||||
@Override public boolean shouldTrackNumPress() { return false; }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package io.github.sspanak.tt9.ime.modes;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
||||
public class ModeABC extends InputMode {
|
||||
|
|
@ -17,16 +15,16 @@ public class ModeABC extends InputMode {
|
|||
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
shouldSelectNextLetter = false;
|
||||
suggestions = language.getKeyCharacters(key);
|
||||
word = null;
|
||||
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
if (hold) {
|
||||
suggestions = new ArrayList<>();
|
||||
word = String.valueOf(key);
|
||||
reset();
|
||||
suggestions.add(String.valueOf(number));
|
||||
autoAcceptTimeout = 0;
|
||||
} else if (repeat > 0) {
|
||||
shouldSelectNextLetter = true;
|
||||
} else {
|
||||
reset();
|
||||
suggestions.addAll(language.getKeyCharacters(number));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -59,6 +57,12 @@ public class ModeABC extends InputMode {
|
|||
return shouldSelectNextLetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
shouldSelectNextLetter = false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import io.github.sspanak.tt9.ime.helpers.TextField;
|
|||
import io.github.sspanak.tt9.ime.modes.helpers.AutoSpace;
|
||||
import io.github.sspanak.tt9.ime.modes.helpers.AutoTextCase;
|
||||
import io.github.sspanak.tt9.ime.modes.helpers.Predictions;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
|
|
@ -69,40 +68,25 @@ public class ModePredictive extends InputMode {
|
|||
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
if (hold) {
|
||||
// hold to type any digit
|
||||
reset();
|
||||
word = String.valueOf(key);
|
||||
} else if (key == 0 && repeat > 0) {
|
||||
onDouble0();
|
||||
autoAcceptTimeout = 0;
|
||||
suggestions.add(String.valueOf(number));
|
||||
} else {
|
||||
// words
|
||||
super.reset();
|
||||
digitSequence += key;
|
||||
digitSequence += number;
|
||||
if (number == 0 && repeat > 0) {
|
||||
autoAcceptTimeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onDouble0
|
||||
* Double "0" is a shortcut for the preferred character.
|
||||
*/
|
||||
private void onDouble0() {
|
||||
try {
|
||||
reset();
|
||||
word = settings.getDoubleZeroChar();
|
||||
digitSequence = language.getDigitSequenceForWord(word);
|
||||
} catch (InvalidLanguageCharactersException e) {
|
||||
Logger.w("tt9/onDouble0", "Failed getting the sequence for word: '" + word + "'. Performing standard 0-key action.");
|
||||
reset();
|
||||
digitSequence = "0";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void changeLanguage(Language language) {
|
||||
super.changeLanguage(language);
|
||||
|
|
@ -217,7 +201,6 @@ public class ModePredictive extends InputMode {
|
|||
.setWordsChangedHandler(handleSuggestions);
|
||||
|
||||
handleSuggestionsExternal = handler;
|
||||
super.reset();
|
||||
|
||||
return predictions.load();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class Predictions {
|
|||
private Handler wordsChangedHandler;
|
||||
|
||||
// data
|
||||
private ArrayList<String> words = new ArrayList<>();
|
||||
private final ArrayList<String> words = new ArrayList<>();
|
||||
|
||||
// punctuation/emoji
|
||||
private final Pattern containsOnly1Regex = Pattern.compile("^1+$");
|
||||
|
|
@ -127,7 +127,7 @@ public class Predictions {
|
|||
*/
|
||||
public boolean load() {
|
||||
if (digitSequence == null || digitSequence.length() == 0) {
|
||||
words = new ArrayList<>();
|
||||
words.clear();
|
||||
onWordsChanged();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -155,16 +155,27 @@ public class Predictions {
|
|||
* Returns "false", when there are no static options for the current digitSequence.
|
||||
*/
|
||||
private boolean loadStatic() {
|
||||
// whitespace/special/math characters
|
||||
if (digitSequence.equals("0")) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
words = language.getKeyCharacters(0, false);
|
||||
} else if (containsOnly1Regex.matcher(digitSequence).matches()) {
|
||||
words.addAll(language.getKeyCharacters(0, false));
|
||||
}
|
||||
// "00" is a shortcut for the preferred character
|
||||
else if (digitSequence.equals("00")) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
words.add(settings.getDoubleZeroChar());
|
||||
}
|
||||
// emoji
|
||||
else if (containsOnly1Regex.matcher(digitSequence).matches()) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
if (digitSequence.length() == 1) {
|
||||
words = language.getKeyCharacters(1, false);
|
||||
words.addAll(language.getKeyCharacters(1, false));
|
||||
} else {
|
||||
digitSequence = digitSequence.length() <= maxEmojiSequence.length() ? digitSequence : maxEmojiSequence;
|
||||
words = Characters.getEmoji(digitSequence.length() - 2);
|
||||
words.addAll(Characters.getEmoji(digitSequence.length() - 2));
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -205,10 +205,9 @@ public class SettingsStore {
|
|||
|
||||
public boolean getDarkTheme() { return prefs.getBoolean("pref_dark_theme", true); }
|
||||
|
||||
|
||||
public boolean getShowSoftKeys() { return prefs.getBoolean("pref_show_soft_keys", true); }
|
||||
|
||||
|
||||
public boolean getShowSoftNumpad() { return getShowSoftKeys() && prefs.getBoolean("pref_show_soft_numpad", false); }
|
||||
|
||||
/************* typing settings *************/
|
||||
|
||||
|
|
|
|||
92
src/io/github/sspanak/tt9/ui/main/BaseMainLayout.java
Normal file
92
src/io/github/sspanak/tt9/ui/main/BaseMainLayout.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
abstract class BaseMainLayout {
|
||||
protected TraditionalT9 tt9;
|
||||
private final int xml;
|
||||
|
||||
protected View view = null;
|
||||
protected ArrayList<SoftKey> keys = new ArrayList<>();
|
||||
|
||||
public BaseMainLayout(TraditionalT9 tt9, int xml) {
|
||||
this.tt9 = tt9;
|
||||
this.xml = xml;
|
||||
}
|
||||
|
||||
|
||||
/** setDarkTheme
|
||||
* Changes the main view colors according to the theme.
|
||||
*
|
||||
* We need to do this manually, instead of relying on the Context to resolve the appropriate colors,
|
||||
* because this View is part of the main service View. And service Views are always locked to the
|
||||
* system context and theme.
|
||||
*
|
||||
* More info:
|
||||
* <a href="https://stackoverflow.com/questions/72382886/system-applies-night-mode-to-views-added-in-service-type-application-overlay">...</a>
|
||||
*/
|
||||
abstract public void setDarkTheme(boolean yes);
|
||||
|
||||
|
||||
/**
|
||||
* render
|
||||
* Do all the necessary stuff to display the View.
|
||||
*/
|
||||
abstract public void render();
|
||||
|
||||
|
||||
/**
|
||||
* getKeys
|
||||
* Returns a list of all the usable Soft Keys.
|
||||
*/
|
||||
abstract protected ArrayList<SoftKey> getKeys();
|
||||
|
||||
|
||||
public View getView() {
|
||||
if (view == null) {
|
||||
view = View.inflate(tt9.getApplicationContext(), xml, null);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableClickHandlers() {
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setTT9(tt9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected ArrayList<SoftKey> getKeysFromContainer(ViewGroup container) {
|
||||
ArrayList<SoftKey> keyList = new ArrayList<>();
|
||||
final int childrenCount = container != null ? container.getChildCount() : 0;
|
||||
|
||||
for (int i = 0; i < childrenCount; i++) {
|
||||
View child = container.getChildAt(i);
|
||||
if (child instanceof SoftKey) {
|
||||
keyList.add((SoftKey) child);
|
||||
}
|
||||
}
|
||||
|
||||
return keyList;
|
||||
}
|
||||
}
|
||||
64
src/io/github/sspanak/tt9/ui/main/MainLayoutNumpad.java
Normal file
64
src/io/github/sspanak/tt9/ui/main/MainLayoutNumpad.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
class MainLayoutNumpad extends BaseMainLayout {
|
||||
public MainLayoutNumpad(TraditionalT9 tt9) {
|
||||
super(tt9, R.layout.main_numpad);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.color.dark_numpad_background : R.color.numpad_background
|
||||
));
|
||||
|
||||
// text
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setDarkTheme(darkEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
getView();
|
||||
enableClickHandlers();
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.render();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<SoftKey> getKeys() {
|
||||
if (keys != null && keys.size() > 0) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
ViewGroup table = view.findViewById(R.id.main_soft_keys);
|
||||
int tableRowsCount = table.getChildCount();
|
||||
|
||||
for (int rowId = 0; rowId < tableRowsCount; rowId++) {
|
||||
View row = table.getChildAt(rowId);
|
||||
if (row instanceof ViewGroup) {
|
||||
keys.addAll(getKeysFromContainer((ViewGroup) row));
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
67
src/io/github/sspanak/tt9/ui/main/MainLayoutSmall.java
Normal file
67
src/io/github/sspanak/tt9/ui/main/MainLayoutSmall.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
class MainLayoutSmall extends BaseMainLayout {
|
||||
public MainLayoutSmall(TraditionalT9 tt9) {
|
||||
super(tt9, R.layout.main_small);
|
||||
}
|
||||
|
||||
private void setSoftKeysVisibility() {
|
||||
if (view != null) {
|
||||
view.findViewById(R.id.main_soft_keys).setVisibility(tt9.getSettings().getShowSoftKeys() ? LinearLayout.VISIBLE : LinearLayout.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
getView();
|
||||
enableClickHandlers();
|
||||
setSoftKeysVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.findViewById(R.id.main_soft_keys).setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_background_dark : R.drawable.button_background
|
||||
));
|
||||
|
||||
// text
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setDarkTheme(darkEnabled);
|
||||
}
|
||||
|
||||
// separators
|
||||
Drawable separatorColor = ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_separator_dark : R.drawable.button_separator
|
||||
);
|
||||
|
||||
view.findViewById(R.id.main_separator_left).setBackground(separatorColor);
|
||||
view.findViewById(R.id.main_separator_right).setBackground(separatorColor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ArrayList<SoftKey> getKeys() {
|
||||
if (view != null && (keys == null || keys.size() == 0)) {
|
||||
keys = getKeysFromContainer(view.findViewById(R.id.main_soft_keys));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
47
src/io/github/sspanak/tt9/ui/main/MainView.java
Normal file
47
src/io/github/sspanak/tt9/ui/main/MainView.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class MainView {
|
||||
private final TraditionalT9 tt9;
|
||||
private BaseMainLayout main;
|
||||
|
||||
public MainView(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
|
||||
forceCreateView();
|
||||
}
|
||||
|
||||
public boolean createView() {
|
||||
if (tt9.getSettings().getShowSoftNumpad() && !(main instanceof MainLayoutNumpad)) {
|
||||
main = new MainLayoutNumpad(tt9);
|
||||
main.render();
|
||||
return true;
|
||||
} else if (!tt9.getSettings().getShowSoftNumpad() && !(main instanceof MainLayoutSmall)) {
|
||||
main = new MainLayoutSmall(tt9);
|
||||
main.render();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void forceCreateView() {
|
||||
main = null;
|
||||
createView();
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return main.getView();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
main.render();
|
||||
}
|
||||
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
main.setDarkTheme(darkEnabled);
|
||||
}
|
||||
}
|
||||
75
src/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java
Normal file
75
src/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
public class SoftBackspaceKey extends SoftKey {
|
||||
private SettingsStore settings;
|
||||
long lastBackspaceCall = 0;
|
||||
|
||||
public SoftBackspaceKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftBackspaceKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftBackspaceKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private SettingsStore getSettings() {
|
||||
if (settings == null) {
|
||||
settings = new SettingsStore(getContext());
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public boolean onTouch(View view, MotionEvent event) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int action = event.getAction() & MotionEvent.ACTION_MASK;
|
||||
|
||||
if (action == MotionEvent.AXIS_PRESSURE) {
|
||||
handleHold();
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
handleUp();
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
// Fallback for phones that do not report AXIS_PRESSURE, when a key is being held
|
||||
handlePress(-1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleHold() {
|
||||
if (System.currentTimeMillis() - lastBackspaceCall < getSettings().getSoftKeyRepeatDelay()) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlePress(-1);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
lastBackspaceCall = lastBackspaceCall == 0 ? getSettings().getSoftKeyInitialDelay() + now : now;
|
||||
}
|
||||
|
||||
private void handleUp() {
|
||||
lastBackspaceCall = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected boolean handlePress(int b) {
|
||||
return tt9.onBackspace();
|
||||
}
|
||||
}
|
||||
126
src/io/github/sspanak/tt9/ui/main/keys/SoftKey.java
Normal file
126
src/io/github/sspanak/tt9/ui/main/keys/SoftKey.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class SoftKey extends androidx.appcompat.widget.AppCompatButton implements View.OnTouchListener {
|
||||
protected TraditionalT9 tt9;
|
||||
|
||||
public SoftKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setTT9(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
}
|
||||
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
int textColor = ContextCompat.getColor(
|
||||
getContext(),
|
||||
darkEnabled ? R.color.dark_button_text : R.color.button_text
|
||||
);
|
||||
setTextColor(textColor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
getRootView().setOnTouchListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
super.onTouchEvent(event);
|
||||
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
return handlePress(view.getId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyId == R.id.soft_key_add_word) return tt9.onKeyAddWord();
|
||||
if (keyId == R.id.soft_key_input_mode) return tt9.onKeyNextInputMode();
|
||||
if (keyId == R.id.soft_key_language) return tt9.onKeyNextLanguage();
|
||||
if (keyId == R.id.soft_key_ok) return tt9.onOK();
|
||||
if (keyId == R.id.soft_key_settings) return tt9.onKeyShowSettings();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name of the key, for example: "OK", "Backspace", "1", etc...
|
||||
*/
|
||||
protected String getKeyNameLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a String describing what the key does.
|
||||
* For example: "ABC" for 2-key; "⌫" for Backspace key, "⚙" for Settings key, and so on.
|
||||
*
|
||||
* The function label is optional.
|
||||
*/
|
||||
protected String getKeyFunctionLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* render
|
||||
* Sets the key label using "getKeyNameLabel()" and "getKeyFunctionLabel()" or if they both
|
||||
* return NULL, the XML "text" attribute will be preserved.
|
||||
*
|
||||
* If there is only name label, it will be centered and at normal font size.
|
||||
* If there is also a function label, it will be displayed below the name label and both will
|
||||
* have their font size adjusted to fit inside the key.
|
||||
*/
|
||||
public void render() {
|
||||
String name = getKeyNameLabel();
|
||||
String func = getKeyFunctionLabel();
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
} else if (func == null) {
|
||||
setText(name);
|
||||
return;
|
||||
}
|
||||
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder(name);
|
||||
sb.append('\n');
|
||||
sb.append(func);
|
||||
|
||||
sb.setSpan(new RelativeSizeSpan(0.55f), 0, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
sb.setSpan(new StyleSpan(Typeface.ITALIC), 0, 2, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
sb.setSpan(new RelativeSizeSpan(0.75f), 1, sb.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
setText(sb);
|
||||
}
|
||||
}
|
||||
99
src/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java
Normal file
99
src/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
|
||||
public class SoftNumberKey extends SoftKey {
|
||||
public SoftNumberKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftNumberKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftNumberKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int keyCode = Key.numberToCode(getNumber(keyId));
|
||||
if (keyCode < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tt9.onKeyDown(keyCode, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
|
||||
tt9.onKeyUp(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getNumber(int keyId) {
|
||||
if (keyId == R.id.soft_key_0) return 0;
|
||||
if (keyId == R.id.soft_key_1) return 1;
|
||||
if (keyId == R.id.soft_key_2) return 2;
|
||||
if (keyId == R.id.soft_key_3) return 3;
|
||||
if (keyId == R.id.soft_key_4) return 4;
|
||||
if (keyId == R.id.soft_key_5) return 5;
|
||||
if (keyId == R.id.soft_key_6) return 6;
|
||||
if (keyId == R.id.soft_key_7) return 7;
|
||||
if (keyId == R.id.soft_key_8) return 8;
|
||||
if (keyId == R.id.soft_key_9) return 9;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyNameLabel() {
|
||||
return String.valueOf(getNumber(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyFunctionLabel() {
|
||||
if (tt9 == null || tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int number = getNumber(getId());
|
||||
int textCase = tt9.getSettings().getTextCase();
|
||||
Language language = LanguageCollection.getLanguage(tt9.getSettings().getInputLanguage());
|
||||
|
||||
if (language == null) {
|
||||
Logger.d("SoftNumberKey.getLabel", "Cannot generate a label when the language is NULL.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (number == 0) {
|
||||
return "␣";
|
||||
}
|
||||
|
||||
if (number == 1) {
|
||||
return ",:-)";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ArrayList<String> chars = language.getKeyCharacters(number, false);
|
||||
for (int i = 0; i < 5 && i < chars.size(); i++) {
|
||||
sb.append(
|
||||
textCase == InputMode.CASE_UPPER ? chars.get(i).toUpperCase(language.getLocale()) : chars.get(i)
|
||||
);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
|
||||
public class SoftPunctuationKey extends SoftKey {
|
||||
public SoftPunctuationKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftPunctuationKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftPunctuationKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return tt9.onOtherKey(KeyEvent.KEYCODE_STAR);
|
||||
if (keyId == R.id.soft_key_punctuation_2) return tt9.onOtherKey(KeyEvent.KEYCODE_POUND);
|
||||
} 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
|
||||
protected String getKeyNameLabel() {
|
||||
int keyId = getId();
|
||||
|
||||
if (tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return "✱";
|
||||
if (keyId == R.id.soft_key_punctuation_2) return "#";
|
||||
} else {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return "!";
|
||||
if (keyId == R.id.soft_key_punctuation_2) return "?";
|
||||
}
|
||||
|
||||
return "PUNC";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
|
|
@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import java.util.List;
|
||||
|
||||
public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.ViewHolder> {
|
||||
private final SuggestionsBar suggestionsBar;
|
||||
private final int layout;
|
||||
private final int textViewResourceId;
|
||||
private final LayoutInflater mInflater;
|
||||
|
|
@ -23,7 +24,8 @@ public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.
|
|||
private int selectedIndex = 0;
|
||||
|
||||
|
||||
public SuggestionsAdapter(Context context, int layout, int textViewResourceId, List<String> suggestions) {
|
||||
public SuggestionsAdapter(Context context, SuggestionsBar suggestionBar, int layout, int textViewResourceId, List<String> suggestions) {
|
||||
this.suggestionsBar = suggestionBar;
|
||||
this.layout = layout;
|
||||
this.textViewResourceId = textViewResourceId;
|
||||
this.mInflater = LayoutInflater.from(context);
|
||||
|
|
@ -43,6 +45,7 @@ public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.
|
|||
holder.suggestionItem.setText(mSuggestions.get(position));
|
||||
holder.suggestionItem.setTextColor(colorDefault);
|
||||
holder.suggestionItem.setBackgroundColor(selectedIndex == position ? colorHighlight : Color.TRANSPARENT);
|
||||
holder.suggestionItem.setOnClickListener(v -> suggestionsBar.onItemClick(holder.getAdapterPosition()));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
|
@ -16,7 +16,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class SuggestionsBar {
|
||||
private final List<String> suggestions = new ArrayList<>();
|
||||
|
|
@ -24,14 +24,14 @@ public class SuggestionsBar {
|
|||
private boolean isDarkThemeEnabled = false;
|
||||
|
||||
private final RecyclerView mView;
|
||||
private final SettingsStore settings;
|
||||
private final TraditionalT9 tt9;
|
||||
private SuggestionsAdapter mSuggestionsAdapter;
|
||||
|
||||
|
||||
public SuggestionsBar(SettingsStore settings, View mainView) {
|
||||
public SuggestionsBar(TraditionalT9 tt9, View mainView) {
|
||||
super();
|
||||
|
||||
this.settings = settings;
|
||||
this.tt9 = tt9;
|
||||
|
||||
mView = mainView.findViewById(R.id.suggestions_bar);
|
||||
mView.setLayoutManager(new LinearLayoutManager(mainView.getContext(), RecyclerView.HORIZONTAL,false));
|
||||
|
|
@ -45,8 +45,8 @@ public class SuggestionsBar {
|
|||
private void configureAnimation() {
|
||||
DefaultItemAnimator animator = new DefaultItemAnimator();
|
||||
|
||||
int translateDuration = settings.getSuggestionTranslateAnimationDuration();
|
||||
int selectDuration = settings.getSuggestionSelectAnimationDuration();
|
||||
int translateDuration = tt9.getSettings().getSuggestionTranslateAnimationDuration();
|
||||
int selectDuration = tt9.getSettings().getSuggestionSelectAnimationDuration();
|
||||
|
||||
animator.setMoveDuration(selectDuration);
|
||||
animator.setChangeDuration(translateDuration);
|
||||
|
|
@ -60,13 +60,14 @@ public class SuggestionsBar {
|
|||
private void initDataAdapter(Context context) {
|
||||
mSuggestionsAdapter = new SuggestionsAdapter(
|
||||
context,
|
||||
R.layout.suggestion_list_view,
|
||||
this,
|
||||
tt9.getSettings().getShowSoftNumpad() ? R.layout.suggestion_list_numpad : R.layout.suggestion_list,
|
||||
R.id.suggestion_list_item,
|
||||
suggestions
|
||||
);
|
||||
mView.setAdapter(mSuggestionsAdapter);
|
||||
|
||||
setDarkTheme(settings.getDarkTheme());
|
||||
setDarkTheme(tt9.getSettings().getDarkTheme());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -213,4 +214,14 @@ public class SuggestionsBar {
|
|||
|
||||
setBackground(newSuggestions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onItemClick
|
||||
* Passes through suggestion selected using the touchscreen.
|
||||
*/
|
||||
public void onItemClick(int position) {
|
||||
selectedIndex = position;
|
||||
tt9.onOK();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue