From 7ed5ae407daca82e31e0ddcc0b43a150fd9c8a5b Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 29 Feb 2024 08:49:00 +0700 Subject: [PATCH 1/2] fix(android): selection range indexing --- .../java/com/keyman/engine/KMKeyboard.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index d17c3316971..8a477a644b0 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -17,6 +17,7 @@ import com.keyman.engine.KMManager.KeyboardType; import com.keyman.engine.KeyboardEventHandler.EventType; import com.keyman.engine.KeyboardEventHandler.OnKeyboardEventListener; +import com.keyman.engine.util.CharSequenceUtil; import com.keyman.engine.util.DependencyUtil; import com.keyman.engine.util.DependencyUtil.LibraryType; import com.keyman.engine.util.FileUtils; @@ -167,9 +168,29 @@ protected boolean updateSelectionRange(int selStart, int selEnd) { InputConnection ic = KMManager.getInputConnection(this.keyboardType); if (ic != null) { ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); + String rawText = icText.text.toString(); if (icText != null) { - updateText(icText.text.toString()); + updateText(rawText.toString()); } + + /* + The values of selStart & selEnd provided by the system are in code units, + not code-points. We need to account for surrogate pairs here. + + Fortunately, it uses UCS-2 encoding... just like JS. + + References: + - https://stackoverflow.com/a/23980211 + - https://android.googlesource.com/platform/frameworks/base/+/152944f/core/java/android/view/inputmethod/InputConnection.java#326 + */ + + // Count the number of characters which are surrogate pairs. + int pairsAtStart = CharSequenceUtil.countSurrogatePairs(rawText.substring(0, selStart), rawText.length()); + String selectedText = rawText.substring(selStart, selEnd); + int pairsSelected = CharSequenceUtil.countSurrogatePairs(selectedText, selectedText.length()); + + selStart -= pairsAtStart; + selEnd -= (pairsAtStart + pairsSelected); } this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); result = true; From 944e668d3710f43b7068513e657920b3c729ebd8 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 29 Feb 2024 13:31:19 +0700 Subject: [PATCH 2/2] fix(android): icText null check --- .../app/src/main/java/com/keyman/engine/KMKeyboard.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index 8a477a644b0..bdd0f4f65c4 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -168,11 +168,13 @@ protected boolean updateSelectionRange(int selStart, int selEnd) { InputConnection ic = KMManager.getInputConnection(this.keyboardType); if (ic != null) { ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); - String rawText = icText.text.toString(); - if (icText != null) { - updateText(rawText.toString()); + if (icText == null) { + return false; } + String rawText = icText.text.toString(); + updateText(rawText.toString()); + /* The values of selStart & selEnd provided by the system are in code units, not code-points. We need to account for surrogate pairs here.