1
0
Fork 0

automatic language selection on startup

This commit is contained in:
sspanak 2024-05-17 16:44:40 +03:00 committed by Dimo Karaivanov
parent 6226d4c019
commit f98754cf2d
5 changed files with 146 additions and 73 deletions

View file

@ -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());
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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()) {