automatic language selection on startup
This commit is contained in:
parent
6226d4c019
commit
f98754cf2d
5 changed files with 146 additions and 73 deletions
|
|
@ -303,10 +303,8 @@ public abstract class HotkeyHandler extends TypingHandler {
|
|||
int next = (previous + 1) % mEnabledLanguages.size();
|
||||
mLanguage = LanguageCollection.getLanguage(getApplicationContext(), mEnabledLanguages.get(next));
|
||||
|
||||
// validate and save it for the next time
|
||||
validateLanguages();
|
||||
|
||||
// save it for the next time
|
||||
settings.saveInputLanguage(mLanguage.getId());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,15 +52,14 @@ public abstract class TypingHandler extends KeyPadHandler {
|
|||
|
||||
setInputField(connection, field);
|
||||
|
||||
// in case we are back from Settings screen, update the language list
|
||||
int oldLang = mLanguage != null ? mLanguage.getId() : -1;
|
||||
mEnabledLanguages = settings.getEnabledLanguageIds();
|
||||
mLanguage = LanguageCollection.getLanguage(getApplicationContext(), settings.getInputLanguage());
|
||||
validateLanguages();
|
||||
// 1. In case we are back from Settings screen, update the language list
|
||||
// 2. If the connected app hints it is in a language different than the current one,
|
||||
// we try to switch.
|
||||
boolean languageChanged = determineLanguage();
|
||||
|
||||
// ignore multiple calls for the same field, caused by requestShowSelf() -> showWindow(),
|
||||
// or weirdly functioning apps, such as the Qin SMS app
|
||||
if (restart && oldLang == mLanguage.getId() && mInputMode.getId() == getInputModeId()) {
|
||||
if (restart && languageChanged && mInputMode.getId() == getInputModeId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -88,9 +87,8 @@ public abstract class TypingHandler extends KeyPadHandler {
|
|||
protected void validateLanguages() {
|
||||
mEnabledLanguages = InputModeValidator.validateEnabledLanguages(getApplicationContext(), mEnabledLanguages);
|
||||
mLanguage = InputModeValidator.validateLanguage(getApplicationContext(), mLanguage, mEnabledLanguages);
|
||||
|
||||
settings.saveEnabledLanguageIds(mEnabledLanguages);
|
||||
settings.saveInputLanguage(mLanguage.getId());
|
||||
settings.saveEnabledLanguageIds(mEnabledLanguages);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -207,6 +205,27 @@ public abstract class TypingHandler extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* determineLanguage
|
||||
* Restore the last language or auto-select a more appropriate one, if the application hints so.
|
||||
* In case the settings are not valid, we will fallback to the default language.
|
||||
*/
|
||||
private boolean determineLanguage() {
|
||||
mEnabledLanguages = settings.getEnabledLanguageIds();
|
||||
|
||||
int oldLang = mLanguage != null ? mLanguage.getId() : -1;
|
||||
mLanguage = LanguageCollection.getLanguage(getApplicationContext(), settings.getInputLanguage());
|
||||
validateLanguages();
|
||||
|
||||
Language appLanguage = textField.getLanguage(getApplicationContext(), mEnabledLanguages);
|
||||
if (appLanguage != null) {
|
||||
mLanguage = appLanguage;
|
||||
}
|
||||
|
||||
return oldLang != mLanguage.getId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* determineTextCase
|
||||
* Restore the last text case or auto-select a new one. If the InputMode supports it, it can change
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package io.github.sspanak.tt9.ime.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
|
||||
public class InputField {
|
||||
public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1;
|
||||
|
||||
protected final InputConnection connection;
|
||||
protected final EditorInfo field;
|
||||
|
||||
|
||||
protected InputField(InputConnection inputConnection, EditorInfo inputField) {
|
||||
connection = inputConnection;
|
||||
field = inputField;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(InputConnection inputConnection, EditorInfo inputField) {
|
||||
return
|
||||
connection != null && connection == inputConnection
|
||||
&& field != null && field == inputField;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getAction
|
||||
* Returns the most appropriate action for the "OK" key. It could be "send", "act as ENTER key", "go (to URL)" and so on.
|
||||
*/
|
||||
public int getAction() {
|
||||
if (field == null) {
|
||||
return EditorInfo.IME_ACTION_NONE;
|
||||
}
|
||||
|
||||
// custom actions handling as in LatinIME. See the example in OpenBoard repo:
|
||||
// https://github.com/openboard-team/openboard/blob/master/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/InputTypeUtils.java#L107
|
||||
if (field.actionId == EditorInfo.IME_ACTION_DONE || field.actionLabel != null) {
|
||||
return IME_ACTION_ENTER;
|
||||
} else if (field.actionId > 0) {
|
||||
return field.actionId;
|
||||
}
|
||||
|
||||
// As in LatinIME, we want to perform an editor action, including in the case of "IME_ACTION_UNSPECIFIED".
|
||||
// Otherwise, we pass through the ENTER or DPAD_CENTER key press and let the app or the system decide what to do.
|
||||
// See the example below:
|
||||
// https://github.com/openboard-team/openboard/blob/c3772cd56e770975ea5570db903f93b199de8b32/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java#L756
|
||||
int standardAction = field.imeOptions & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
|
||||
switch (standardAction) {
|
||||
case EditorInfo.IME_ACTION_DONE:
|
||||
case EditorInfo.IME_ACTION_GO:
|
||||
case EditorInfo.IME_ACTION_NEXT:
|
||||
case EditorInfo.IME_ACTION_PREVIOUS:
|
||||
case EditorInfo.IME_ACTION_SEARCH:
|
||||
case EditorInfo.IME_ACTION_SEND:
|
||||
case EditorInfo.IME_ACTION_UNSPECIFIED:
|
||||
return standardAction;
|
||||
default:
|
||||
return IME_ACTION_ENTER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* performAction
|
||||
* Sends an action ID to the connected application. Usually, the action is determined with "this.getAction()".
|
||||
* Note that it is up to the app to decide what to do or ignore the action ID.
|
||||
*/
|
||||
public boolean performAction(int actionId) {
|
||||
return connection != null && actionId != EditorInfo.IME_ACTION_NONE && connection.performEditorAction(actionId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getLanguage
|
||||
* Detects the language hint of the current field and returns a TT9-friendly Language object.
|
||||
* If the language is not supported, or the field has no hint, for example it's a numeric field or
|
||||
* it's a text field where the language doesn't matter, the function returns null.
|
||||
*/
|
||||
@Nullable
|
||||
public Language getLanguage(Context context, ArrayList<Integer> allowedLanguageIds) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; field.hintLocales != null && i < field.hintLocales.size(); i++) {
|
||||
Language lang = LanguageCollection.getByLanguageCode(context, field.hintLocales.get(i).getLanguage());
|
||||
if (lang != null && allowedLanguageIds.contains(lang.getId())) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,32 +17,19 @@ import io.github.sspanak.tt9.languages.LanguageKind;
|
|||
import io.github.sspanak.tt9.util.Logger;
|
||||
import io.github.sspanak.tt9.util.Text;
|
||||
|
||||
public class TextField {
|
||||
public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1;
|
||||
|
||||
private final InputConnection connection;
|
||||
private final EditorInfo field;
|
||||
|
||||
private final boolean isComposingSupported;
|
||||
public class TextField extends InputField {
|
||||
private CharSequence composingText = "";
|
||||
private final boolean isComposingSupported;
|
||||
|
||||
|
||||
public TextField(InputConnection inputConnection, EditorInfo inputField) {
|
||||
connection = inputConnection;
|
||||
field = inputField;
|
||||
super(inputConnection, inputField);
|
||||
|
||||
InputType inputType = new InputType(inputConnection, inputField);
|
||||
isComposingSupported = !inputType.isNumeric() && !inputType.isLimited();
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(InputConnection inputConnection, EditorInfo inputField) {
|
||||
return
|
||||
connection != null && connection == inputConnection
|
||||
&& field != null && field == inputField;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getStringBeforeCursor(1).isEmpty() && getStringAfterCursor(1).isEmpty();
|
||||
}
|
||||
|
|
@ -246,50 +233,4 @@ public class TextField {
|
|||
|
||||
return styledWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* getAction
|
||||
* Returns the most appropriate action for the "OK" key. It could be "send", "act as ENTER key", "go (to URL)" and so on.
|
||||
*/
|
||||
public int getAction() {
|
||||
if (field == null) {
|
||||
return EditorInfo.IME_ACTION_NONE;
|
||||
}
|
||||
|
||||
// custom actions handling as in LatinIME. See the example in OpenBoard repo:
|
||||
// https://github.com/openboard-team/openboard/blob/master/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/InputTypeUtils.java#L107
|
||||
if (field.actionId == EditorInfo.IME_ACTION_DONE || field.actionLabel != null) {
|
||||
return IME_ACTION_ENTER;
|
||||
} else if (field.actionId > 0) {
|
||||
return field.actionId;
|
||||
}
|
||||
|
||||
// As in LatinIME, we want to perform an editor action, including in the case of "IME_ACTION_UNSPECIFIED".
|
||||
// Otherwise, we pass through the ENTER or DPAD_CENTER key press and let the app or the system decide what to do.
|
||||
// See the example below:
|
||||
// https://github.com/openboard-team/openboard/blob/c3772cd56e770975ea5570db903f93b199de8b32/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java#L756
|
||||
int standardAction = field.imeOptions & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
|
||||
switch (standardAction) {
|
||||
case EditorInfo.IME_ACTION_DONE:
|
||||
case EditorInfo.IME_ACTION_GO:
|
||||
case EditorInfo.IME_ACTION_NEXT:
|
||||
case EditorInfo.IME_ACTION_PREVIOUS:
|
||||
case EditorInfo.IME_ACTION_SEARCH:
|
||||
case EditorInfo.IME_ACTION_SEND:
|
||||
case EditorInfo.IME_ACTION_UNSPECIFIED:
|
||||
return standardAction;
|
||||
default:
|
||||
return IME_ACTION_ENTER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* performAction
|
||||
* Sends an action ID to the connected application. Usually, the action is determined with "this.getAction()".
|
||||
* Note that it is up to the app to decide what to do or ignore the action ID.
|
||||
*/
|
||||
public boolean performAction(int actionId) {
|
||||
return connection != null && actionId != EditorInfo.IME_ACTION_NONE && connection.performEditorAction(actionId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import io.github.sspanak.tt9.util.Logger;
|
||||
import io.github.sspanak.tt9.util.SystemSettings;
|
||||
|
|
@ -52,6 +53,17 @@ public class LanguageCollection {
|
|||
return language == null ? new NullLanguage(context) : language;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NaturalLanguage getByLanguageCode(Context context, String languageCode) {
|
||||
for (NaturalLanguage lang : getInstance(context).languages.values()) {
|
||||
if (lang.getLocale().getLanguage().equals(new Locale(languageCode).getLanguage())) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NaturalLanguage getByLocale(Context context, String locale) {
|
||||
for (NaturalLanguage lang : getInstance(context).languages.values()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue