From 1bd1807a0d8df9e7ee06fdd125cd5ac06741b279 Mon Sep 17 00:00:00 2001 From: sspanak Date: Thu, 25 Apr 2024 15:32:48 +0300 Subject: [PATCH] moved the hacks to their own package --- .../tt9/{ime/helpers => hacks}/AppHacks.java | 113 ++---------------- .../sspanak/tt9/hacks/ConnectedAppInfo.java | 106 ++++++++++++++++ .../tt9/{util => hacks}/DeviceInfo.java | 2 +- .../github/sspanak/tt9/ime/TraditionalT9.java | 2 +- .../github/sspanak/tt9/ime/TypingHandler.java | 4 +- .../screens/debug/DebugScreen.java | 2 +- .../preferences/settings/SettingsHacks.java | 2 +- .../tt9/preferences/settings/SettingsUI.java | 2 +- 8 files changed, 126 insertions(+), 107 deletions(-) rename app/src/main/java/io/github/sspanak/tt9/{ime/helpers => hacks}/AppHacks.java (51%) create mode 100644 app/src/main/java/io/github/sspanak/tt9/hacks/ConnectedAppInfo.java rename app/src/main/java/io/github/sspanak/tt9/{util => hacks}/DeviceInfo.java (96%) diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java b/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java similarity index 51% rename from app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java rename to app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java index d88c40f7..5a1f78d7 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/AppHacks.java +++ b/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9.ime.helpers; +package io.github.sspanak.tt9.hacks; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; @@ -6,122 +6,35 @@ import android.view.inputmethod.InputConnection; import androidx.annotation.NonNull; +import io.github.sspanak.tt9.ime.helpers.TextField; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.preferences.settings.SettingsStore; -import io.github.sspanak.tt9.util.DeviceInfo; public class AppHacks { - private final EditorInfo editorInfo; + private final ConnectedAppInfo appInfo; private final InputConnection inputConnection; private final SettingsStore settings; private final TextField textField; - private final InputType inputType; public AppHacks(SettingsStore settings, InputConnection inputConnection, EditorInfo inputField, TextField textField) { - this.editorInfo = inputField; this.inputConnection = inputConnection; this.settings = settings; this.textField = textField; - this.inputType = new InputType(inputConnection, inputField); + appInfo = new ConnectedAppInfo(inputConnection, inputField); } - /** - * isKindleInvertedTextField - * When sharing a document to the Amazon Kindle app. It displays a screen where one could edit the title and the author of the - * document. These two fields do not support SpannableString, which is used for suggestion highlighting. When they receive one - * weird side effects occur. Nevertheless, all other text fields in the app are fine, so we detect only these two particular ones. - */ - private boolean isKindleInvertedTextField() { - int titleImeOptions = EditorInfo.IME_ACTION_NONE | EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NAVIGATE_NEXT; - int titleAlternativeImeOptions = titleImeOptions | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; // sometimes the title field is different for no reason - int authorImeOptions = EditorInfo.IME_ACTION_SEND | EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; - - return - isAppField("com.amazon.kindle", EditorInfo.TYPE_CLASS_TEXT) - && ( - editorInfo.imeOptions == titleImeOptions - || editorInfo.imeOptions == titleAlternativeImeOptions - || editorInfo.imeOptions == authorImeOptions - ); + public ConnectedAppInfo getAppInfo() { + return appInfo; } - - /** - * isTermux - * Termux is a terminal emulator and it naturally has a text input, but it incorrectly introduces itself as having a NULL input, - * instead of a plain text input. However NULL inputs are usually, buttons and dropdown menus, which indeed can not read text - * and are ignored by TT9 by default. In order not to ignore Termux, we need this. - */ - public boolean isTermux() { - return isAppField("com.termux", EditorInfo.TYPE_NULL) && editorInfo.fieldId > 0; - } - - - /** - * isMessenger - * Facebook Messenger has flaky support for sending messages. To fix that, we detect the chat input field and send the appropriate - * key codes to it. See "onFbMessengerEnter()" for info how the hack works. - */ - private boolean isMessenger() { - return isAppField( - "com.facebook.orca", - EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES - ); - } - - - private boolean isMultilineTextInNonSystemApp() { - return editorInfo != null && !editorInfo.packageName.contains("android") && inputType.isMultilineText(); - } - - - private boolean isGoogleChat() { - return isAppField( - "com.google.android.apps.dynamite", - EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT - ); - } - - - /** - * Simulate the behavior of the Sonim native keyboard. In search fields with integrated lists, - * ENTER is used to select an item from the list. But some of them have actionId = NEXT, instead of NONE, - * which normally means "navigate to the next button or field". This hack correctly allows selection - * of the item, instead of performing navigation. - */ - private boolean isSonimSearchField(int action) { - return - DeviceInfo.isSonim() && - editorInfo != null && (editorInfo.packageName.startsWith("com.android") || editorInfo.packageName.startsWith("com.sonim")) - && (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION) == action - && ( - inputType.isText() - // in some apps, they forgot to set the TEXT type, but fortunately, they did set the NO_SUGGESTIONS flag. - || ((editorInfo.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) == EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) - ); - } - - - /** - * isAppField - * Detects a particular input field of a particular application. - */ - private boolean isAppField(String appPackageName, int fieldSpec) { - return - editorInfo != null - && ((editorInfo.inputType & fieldSpec) == fieldSpec) - && editorInfo.packageName.equals(appPackageName); - } - - /** * setComposingTextWithHighlightedStem * A compatibility function for text fields that do not support SpannableString. Effectively disables highlighting. */ public void setComposingTextWithHighlightedStem(@NonNull String word, InputMode inputMode) { - if (isKindleInvertedTextField()) { + if (appInfo.isKindleInvertedTextField()) { textField.setComposingText(word); } else { textField.setComposingTextWithHighlightedStem(word, inputMode); @@ -135,9 +48,9 @@ public class AppHacks { * returned, you must not attempt to delete text. This function has already done everything necessary. */ public boolean onBackspace(InputMode inputMode) { - if (isKindleInvertedTextField()) { + if (appInfo.isKindleInvertedTextField()) { inputMode.clearWordStem(); - } else if (isTermux()) { + } else if (appInfo.isTermux()) { return false; } @@ -152,7 +65,7 @@ public class AppHacks { * Returns "true" if the action was handled, "false" otherwise. */ public boolean onAction(int action) { - if (isSonimSearchField(action)) { + if (appInfo.isSonimSearchField(action)) { return sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); } @@ -167,11 +80,11 @@ public class AppHacks { * it does nothing and return "false", signaling the system we have ignored the key press. */ public boolean onEnter() { - if (settings.getFbMessengerHack() && isMessenger()) { + if (settings.getFbMessengerHack() && appInfo.isMessenger()) { return onEnterFbMessenger(); - } else if (settings.getGoogleChatHack() && isGoogleChat()) { + } else if (settings.getGoogleChatHack() && appInfo.isGoogleChat()) { return onEnterGoogleChat(); - } else if (isTermux() || isMultilineTextInNonSystemApp()) { + } else if (appInfo.isTermux() || appInfo.isMultilineTextInNonSystemApp()) { // Termux supports only ENTER, so we convert DPAD_CENTER for it. // Any extra installed apps are likely not designed for hardware keypads, so again, // we don't want to send DPAD_CENTER to them. diff --git a/app/src/main/java/io/github/sspanak/tt9/hacks/ConnectedAppInfo.java b/app/src/main/java/io/github/sspanak/tt9/hacks/ConnectedAppInfo.java new file mode 100644 index 00000000..f04382d3 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/hacks/ConnectedAppInfo.java @@ -0,0 +1,106 @@ +package io.github.sspanak.tt9.hacks; + +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +import io.github.sspanak.tt9.ime.helpers.InputType; + +public class ConnectedAppInfo { + final EditorInfo editorInfo; + final InputType inputType; + + + ConnectedAppInfo(InputConnection inputConnection, EditorInfo inputField) { + this.editorInfo = inputField; + this.inputType = new InputType(inputConnection, inputField); + } + + + /** + * isKindleInvertedTextField + * When sharing a document to the Amazon Kindle app. It displays a screen where one could edit the title and the author of the + * document. These two fields do not support SpannableString, which is used for suggestion highlighting. When they receive one + * weird side effects occur. Nevertheless, all other text fields in the app are fine, so we detect only these two particular ones. + */ + boolean isKindleInvertedTextField() { + int titleImeOptions = EditorInfo.IME_ACTION_NONE | EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NAVIGATE_NEXT; + int titleAlternativeImeOptions = titleImeOptions | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; // sometimes the title field is different for no reason + int authorImeOptions = EditorInfo.IME_ACTION_SEND | EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; + + return + isAppField("com.amazon.kindle", EditorInfo.TYPE_CLASS_TEXT) + && ( + editorInfo.imeOptions == titleImeOptions + || editorInfo.imeOptions == titleAlternativeImeOptions + || editorInfo.imeOptions == authorImeOptions + ); + } + + + /** + * isTermux + * Termux is a terminal emulator and it naturally has a text input, but it incorrectly introduces itself as having a NULL input, + * instead of a plain text input. However NULL inputs are usually, buttons and dropdown menus, which indeed can not read text + * and are ignored by TT9 by default. In order not to ignore Termux, we need this. + */ + public boolean isTermux() { + return isAppField("com.termux", EditorInfo.TYPE_NULL) && editorInfo.fieldId > 0; + } + + + /** + * isMessenger + * Facebook Messenger has flaky support for sending messages. To fix that, we detect the chat input field and send the appropriate + * key codes to it. See "onFbMessengerEnter()" for info how the hack works. + */ + boolean isMessenger() { + return isAppField( + "com.facebook.orca", + EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES + ); + } + + + boolean isMultilineTextInNonSystemApp() { + return editorInfo != null && !editorInfo.packageName.contains("android") && inputType.isMultilineText(); + } + + + boolean isGoogleChat() { + return isAppField( + "com.google.android.apps.dynamite", + EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT + ); + } + + + /** + * Simulate the behavior of the Sonim native keyboard. In search fields with integrated lists, + * ENTER is used to select an item from the list. But some of them have actionId = NEXT, instead of NONE, + * which normally means "navigate to the next button or field". This hack correctly allows selection + * of the item, instead of performing navigation. + */ + boolean isSonimSearchField(int action) { + return + DeviceInfo.isSonim() && + editorInfo != null && (editorInfo.packageName.startsWith("com.android") || editorInfo.packageName.startsWith("com.sonim")) + && (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION) == action + && ( + inputType.isText() + // in some apps, they forgot to set the TEXT type, but fortunately, they did set the NO_SUGGESTIONS flag. + || ((editorInfo.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) == EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) + ); + } + + + /** + * isAppField + * Detects a particular input field of a particular application. + */ + boolean isAppField(String appPackageName, int fieldSpec) { + return + editorInfo != null + && ((editorInfo.inputType & fieldSpec) == fieldSpec) + && editorInfo.packageName.equals(appPackageName); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/util/DeviceInfo.java b/app/src/main/java/io/github/sspanak/tt9/hacks/DeviceInfo.java similarity index 96% rename from app/src/main/java/io/github/sspanak/tt9/util/DeviceInfo.java rename to app/src/main/java/io/github/sspanak/tt9/hacks/DeviceInfo.java index 475f077c..1f409afe 100644 --- a/app/src/main/java/io/github/sspanak/tt9/util/DeviceInfo.java +++ b/app/src/main/java/io/github/sspanak/tt9/hacks/DeviceInfo.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9.util; +package io.github.sspanak.tt9.hacks; import android.content.Context; import android.content.pm.PackageManager; diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java index 0d80ca8a..7009f004 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DictionaryLoader; import io.github.sspanak.tt9.db.WordStoreAsync; +import io.github.sspanak.tt9.hacks.DeviceInfo; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.ime.modes.ModePredictive; import io.github.sspanak.tt9.preferences.settings.SettingsStore; @@ -22,7 +23,6 @@ import io.github.sspanak.tt9.ui.UI; import io.github.sspanak.tt9.ui.dialogs.PopupDialog; import io.github.sspanak.tt9.ui.main.MainView; import io.github.sspanak.tt9.ui.tray.StatusBar; -import io.github.sspanak.tt9.util.DeviceInfo; import io.github.sspanak.tt9.util.Logger; public class TraditionalT9 extends MainViewOps { diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java index 55340e5a..c40f55e3 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TypingHandler.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DictionaryLoader; -import io.github.sspanak.tt9.ime.helpers.AppHacks; +import io.github.sspanak.tt9.hacks.AppHacks; import io.github.sspanak.tt9.ime.helpers.InputModeValidator; import io.github.sspanak.tt9.ime.helpers.InputType; import io.github.sspanak.tt9.ime.helpers.TextField; @@ -229,7 +229,7 @@ public abstract class TypingHandler extends KeyPadHandler { * We do not want to handle any of these, hence we pass through all input to the system. */ protected int getInputModeId() { - if (!inputType.isValid() || (inputType.isLimited() && !appHacks.isTermux())) { + if (!inputType.isValid() || (inputType.isLimited() && !appHacks.getAppInfo().isTermux())) { return InputMode.MODE_PASSTHROUGH; } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java index b750b4bc..b0888f24 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java @@ -4,10 +4,10 @@ import androidx.preference.SwitchPreferenceCompat; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.exporter.LogcatExporter; +import io.github.sspanak.tt9.hacks.DeviceInfo; import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.items.ItemText; import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment; -import io.github.sspanak.tt9.util.DeviceInfo; public class DebugScreen extends BaseScreenFragment { public static final String NAME = "Debug"; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsHacks.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsHacks.java index eae962d2..3926be51 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsHacks.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsHacks.java @@ -3,8 +3,8 @@ package io.github.sspanak.tt9.preferences.settings; import android.content.Context; import android.os.Build; +import io.github.sspanak.tt9.hacks.DeviceInfo; import io.github.sspanak.tt9.preferences.screens.debug.ItemInputHandlingMode; -import io.github.sspanak.tt9.util.DeviceInfo; import io.github.sspanak.tt9.util.Logger; class SettingsHacks extends BaseSettings { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java index 380e40fa..46ae4537 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/settings/SettingsUI.java @@ -5,7 +5,7 @@ import android.content.res.Configuration; import androidx.appcompat.app.AppCompatDelegate; -import io.github.sspanak.tt9.util.DeviceInfo; +import io.github.sspanak.tt9.hacks.DeviceInfo; public class SettingsUI extends SettingsTyping { public final static int FONT_SIZE_DEFAULT = 0;