diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 537a9264..30913494 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ diff --git a/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java b/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java index 4cb4070c..7a342128 100644 --- a/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java +++ b/app/src/main/java/io/github/sspanak/tt9/hacks/AppHacks.java @@ -5,6 +5,8 @@ import android.view.inputmethod.InputConnection; import androidx.annotation.NonNull; +import io.github.sspanak.tt9.ime.helpers.CursorOps; +import io.github.sspanak.tt9.ime.helpers.SuggestionOps; import io.github.sspanak.tt9.ime.helpers.TextField; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.preferences.settings.SettingsStore; @@ -68,6 +70,29 @@ public class AppHacks { } + /** + * Performs extra operations when the cursor moves and returns "true" if the selection was handled, "false" otherwise. + */ + public boolean onUpdateSelection( + @NonNull InputMode inputMode, + @NonNull SuggestionOps suggestionOps, + int oldSelStart, + int oldSelEnd, + int newSelStart, + int newSelEnd, + int candidatesStart, + int candidatesEnd + ) { + if (inputType.isViber() && CursorOps.isInputReset(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)) { + inputMode.onAcceptSuggestion(suggestionOps.acceptIncomplete()); + inputMode.reset(); + return true; + } + + return false; + } + + /** * onEnter * Tries to guess and send the correct confirmation key code or sequence of key codes, diff --git a/app/src/main/java/io/github/sspanak/tt9/hacks/InputType.java b/app/src/main/java/io/github/sspanak/tt9/hacks/InputType.java index 009982e1..ed5b318d 100644 --- a/app/src/main/java/io/github/sspanak/tt9/hacks/InputType.java +++ b/app/src/main/java/io/github/sspanak/tt9/hacks/InputType.java @@ -101,6 +101,20 @@ public class InputType extends StandardInputType { return isAppField("com.termux", EditorInfo.TYPE_NULL) && field.fieldId > 0; } + + /** + * isViber + * When sending messages using the Viber's SEND button, it does so and clears the text field, + * but without notifying the keyboard. This means, after sending the message, the InputMode still + * holds the old text, while the text field is empty. Attempting to type a new word then results + * in appending to the old word. We use this hack to detect Viber and reset the InputMode upon + * sending a message. + */ + public boolean isViber() { + return isAppField("com.viber.voip", EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); + } + + public boolean isNotUs(Context context) { return !isAppField(context.getPackageName(), EditorInfo.TYPE_NULL); } 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 4e703651..7694a5bc 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 @@ -13,6 +13,7 @@ import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DictionaryLoader; import io.github.sspanak.tt9.hacks.AppHacks; import io.github.sspanak.tt9.hacks.InputType; +import io.github.sspanak.tt9.ime.helpers.CursorOps; import io.github.sspanak.tt9.ime.helpers.InputModeValidator; import io.github.sspanak.tt9.ime.helpers.SuggestionOps; import io.github.sspanak.tt9.ime.helpers.TextField; @@ -275,12 +276,16 @@ public abstract class TypingHandler extends KeyPadHandler { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); + // in case the app has modified the InputField and moved the cursor without notifiying us... + if (appHacks.onUpdateSelection(mInputMode, suggestionOps, oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)) { + return; + } + // If the cursor moves while composing a word (usually, because the user has touched the screen outside the word), we must // end typing end accept the word. Otherwise, the cursor would jump back at the end of the word, after the next key press. // This is confusing from user perspective, so we want to avoid it. if ( - candidatesStart != -1 && candidatesEnd != -1 - && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd) + CursorOps.isMovedManually(newSelStart, newSelEnd, candidatesStart, candidatesEnd) && !suggestionOps.isEmpty() ) { mInputMode.onAcceptSuggestion(suggestionOps.acceptIncomplete()); diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/CursorOps.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/CursorOps.java new file mode 100644 index 00000000..de486bf5 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/CursorOps.java @@ -0,0 +1,16 @@ +package io.github.sspanak.tt9.ime.helpers; + +public class CursorOps { + public static boolean isMovedManually(int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { + return + candidatesStart != -1 && candidatesEnd != -1 + && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd); + } + + public static boolean isInputReset(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { + return + oldSelStart > 0 && oldSelEnd > 0 + && newSelStart == 0 && newSelEnd == 0 + && candidatesStart == -1 && candidatesEnd == -1; + } +}