mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-18 21:57:39 +00:00
Merge pull request #6274 from keymanapp/fix/android/5853-selection-management-android-1
fix(android): properly handle selection 🎢
This commit is contained in:
commit
7fd2f9d990
2 changed files with 92 additions and 27 deletions
|
|
@ -1,3 +1,10 @@
|
|||
var _debug = 0;
|
||||
|
||||
// Android harness attachment
|
||||
if(window.parent && window.parent.jsInterface && !window.jsInterface) {
|
||||
window.jsInterface = window.parent.jsInterface;
|
||||
}
|
||||
|
||||
var device = window.jsInterface.getDeviceType();
|
||||
var oskHeight = Math.ceil(window.jsInterface.getKeyboardHeight() / window.devicePixelRatio);
|
||||
var oskWidth = 0;
|
||||
|
|
@ -19,7 +26,7 @@ function init() {
|
|||
//window.console.log('Device type = '+device);
|
||||
//window.console.log('Keyboard height = '+oskHeight);
|
||||
var kmw=com.keyman.singleton;
|
||||
kmw.init({'app':device,'fonts':'packages/'});
|
||||
kmw.init({'app':device,'fonts':'packages/',root:'./'});
|
||||
kmw['util']['setOption']('attachType','manual');
|
||||
kmw['oninserttext'] = insertText;
|
||||
kmw['showKeyboardList'] = showMenu;
|
||||
|
|
@ -48,6 +55,7 @@ function init() {
|
|||
}
|
||||
|
||||
function notifyHost(event, params) {
|
||||
console_debug('notifyHost(event='+event+',params='+params+')');
|
||||
// TODO: Update all other host notifications to use notifyHost instead of directly setting window.location.hash
|
||||
window.setTimeout(function() {
|
||||
// We use a timeout so that the navigation doesn't cause the calling function to abort after the call
|
||||
|
|
@ -142,6 +150,7 @@ function setSpacebarText(mode) {
|
|||
* @param dr Number of post-caret code points to delete. (optional)
|
||||
*/
|
||||
function insertText(dn, s, dr) {
|
||||
console_debug('insertText(dn='+dn+',s='+s+',dr='+dr+')');
|
||||
dr = dr || 0; // Sets a default value of zero when dr is undefined
|
||||
//window.console.log('insertText('+ dn +', ' + s +', ' + dr + ');');
|
||||
window.jsInterface.insertText(dn, s, dr);
|
||||
|
|
@ -189,29 +198,43 @@ function setNumericLayer() {
|
|||
}
|
||||
|
||||
function updateKMText(text) {
|
||||
var ta = document.getElementById('ta');
|
||||
console_debug('updateKMText(text='+text+') ta.value='+ta.value);
|
||||
|
||||
if(text == undefined) {
|
||||
text = '';
|
||||
}
|
||||
var ta = document.getElementById('ta');
|
||||
var kmw = window['keyman'];
|
||||
var resetContext = ta.value != text;
|
||||
ta.value = text;
|
||||
kmw['setActiveElement'](ta);
|
||||
|
||||
if(resetContext) {
|
||||
kmw.resetContext();
|
||||
if(ta.value != text) {
|
||||
ta.value = text;
|
||||
window.resetContext();
|
||||
}
|
||||
}
|
||||
|
||||
function console_debug(s) {
|
||||
if(_debug) {
|
||||
console.debug(s);
|
||||
}
|
||||
}
|
||||
|
||||
function updateKMSelectionRange(start, end) {
|
||||
var ta = document.getElementById('ta');
|
||||
var kmw = window['keyman'];
|
||||
var resetContext = (ta.selectionStart != start || ta.selectionEnd != end);
|
||||
ta.selectionStart = ta._KeymanWebSelectionStart = start;
|
||||
ta.selectionEnd = ta._KeymanWebSelectionEnd = end;
|
||||
kmw['setActiveElement'](ta);
|
||||
if(resetContext) {
|
||||
kmw.resetContext();
|
||||
console_debug('updateKMSelectionRange('+start+','+end+'): ta.selectionStart='+ta.selectionStart+' '+
|
||||
'['+ta._KeymanWebSelectionStart+'] ta.selectionEnd='+ta.selectionEnd+' '+ta._KeymanWebSelectionEnd);
|
||||
|
||||
var selDirection = 'forward';
|
||||
if(start > end) {
|
||||
var e0 = end;
|
||||
end = start;
|
||||
start = e0;
|
||||
selDirection = 'backward';
|
||||
}
|
||||
|
||||
if(ta.selectionStart != start || ta.selectionEnd != end || ta.selectionDirection != selDirection) {
|
||||
ta.selectionStart = ta._KeymanWebSelectionStart = start;
|
||||
ta.selectionEnd = ta._KeymanWebSelectionEnd = end;
|
||||
ta.selectionDirection = selDirection;
|
||||
keyman.resetContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,6 +324,7 @@ function executePopupKey(keyID, keyText) {
|
|||
}
|
||||
|
||||
function executeHardwareKeystroke(code, shift, lstates, eventModifiers) {
|
||||
console_debug('executeHardwareKeystroke(code='+code+',shift='+shift+',lstates='+lstates+',eventModifiers='+eventModifiers+')');
|
||||
var kmw=window['keyman'];
|
||||
//window.console.log('executeHardwareKeystroke:('+code+', ' + shift + ', ' + lstates + ');');
|
||||
try {
|
||||
|
|
@ -342,7 +366,7 @@ function toHex(theString) {
|
|||
* Reference: Issue #5376
|
||||
*/
|
||||
function checkTextArea() {
|
||||
var uaRe = /Chrome\/([0-9]*)./g;
|
||||
var uaRe = /Chrome\/([0-9]*)\./g;
|
||||
var chromeMajorVersion = uaRe.exec(navigator.userAgent);
|
||||
if (chromeMajorVersion && parseInt(chromeMajorVersion[1]) <= 37) {
|
||||
var ta = document.getElementById('ta');
|
||||
|
|
|
|||
|
|
@ -2683,17 +2683,24 @@ public final class KMManager {
|
|||
start = temp;
|
||||
}
|
||||
|
||||
if (dn <= 0) {
|
||||
int deleteLeft = dn;
|
||||
|
||||
if(start != end && dn == 1 && s.length() == 0) {
|
||||
/* Handle backspace with a selection: just delete selection */
|
||||
deleteLeft = 0;
|
||||
}
|
||||
|
||||
if (deleteLeft <= 0) {
|
||||
if (start == end) {
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
textView.keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
} else {
|
||||
// *** TO DO: Try to find a solution to the bug on API < 17, insert overwrites on next line
|
||||
if (s.length() > 0) {
|
||||
} else if (s.length() > 0) {
|
||||
// *** TO DO: Try to find a solution to the bug on API < 17, insert overwrites on next line
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().insert(start, s);
|
||||
}
|
||||
} else {
|
||||
textView.getText().delete(start, end);
|
||||
}
|
||||
} else {
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
|
|
@ -2712,7 +2719,16 @@ public final class KMManager {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < dn; i++) {
|
||||
if(start != end) {
|
||||
// Delete the selection
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().delete(start, end);
|
||||
textView.setSelection(start);
|
||||
end = start;
|
||||
deleteLeft = 0;
|
||||
}
|
||||
for (int i = 0; i < deleteLeft; i++) {
|
||||
CharSequence chars = textView.getText().subSequence(0, start);
|
||||
if (chars != null && chars.length() > 0) {
|
||||
char c = chars.charAt(start - 1);
|
||||
|
|
@ -2736,6 +2752,8 @@ public final class KMManager {
|
|||
}
|
||||
}
|
||||
|
||||
// Collapse the selection
|
||||
textView.setSelection(start + s.length());
|
||||
textView.endBatchEdit();
|
||||
}
|
||||
});
|
||||
|
|
@ -2789,6 +2807,7 @@ public final class KMManager {
|
|||
// This annotation is required in Jelly Bean and later:
|
||||
@JavascriptInterface
|
||||
public void insertText(final int dn, final String s, final int dr) {
|
||||
// TODO: Unify in-app and system insertText
|
||||
Handler mainLoop = new Handler(Looper.getMainLooper());
|
||||
mainLoop.post(new Runnable() {
|
||||
public void run() {
|
||||
|
|
@ -2813,11 +2832,19 @@ public final class KMManager {
|
|||
|
||||
ic.beginBatchEdit();
|
||||
|
||||
int deleteLeft = dn;
|
||||
|
||||
// Delete any existing selected text.
|
||||
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
if (icText != null) { // This can be null if the input connection becomes invalid.
|
||||
int start = icText.startOffset + icText.selectionStart;
|
||||
int end = icText.startOffset + icText.selectionEnd;
|
||||
if (end < start) {
|
||||
// Swap start/end for backward selection
|
||||
int temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
}
|
||||
if (end > start) {
|
||||
if (s.length() == 0) {
|
||||
ic.setSelection(start, start);
|
||||
|
|
@ -2829,6 +2856,10 @@ public final class KMManager {
|
|||
ic.setSelection(start, start);
|
||||
ic.deleteSurroundingText(0, end - start);
|
||||
}
|
||||
|
||||
// KeymanWeb tells us how to delete the selection, but we don't
|
||||
// want to do that twice
|
||||
deleteLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2839,8 +2870,8 @@ public final class KMManager {
|
|||
}
|
||||
|
||||
// Perform left-deletions
|
||||
if (dn > 0) {
|
||||
performLeftDeletions(ic, dn);
|
||||
if (deleteLeft > 0) {
|
||||
performLeftDeletions(ic, deleteLeft);
|
||||
}
|
||||
|
||||
// Perform right-deletions
|
||||
|
|
@ -2875,8 +2906,8 @@ public final class KMManager {
|
|||
}
|
||||
|
||||
/*
|
||||
// TODO: Chromium has a bug where deleteSurroundingText deletes an entire grapheme cluster
|
||||
// instead of one code-point. See Chromium issue #1024738
|
||||
// Chromium up until version M81 had a bug where deleteSurroundingText deletes an entire
|
||||
// grapheme cluster instead of one code-point. See Chromium issue #1024738
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1024738
|
||||
//
|
||||
// We'll retrieve up to (dn*2+16) characters before the cursor to collect enough characters
|
||||
|
|
@ -2894,7 +2925,17 @@ public final class KMManager {
|
|||
return;
|
||||
}
|
||||
|
||||
int numPairs = CharSequenceUtil.countSurrogatePairs(charsBackup, dn);
|
||||
// Count the number of characters which are surrogate pairs
|
||||
int index = lastIndex, dnx = dn, numPairs = 0;
|
||||
while(index > 0 && dnx > 0) {
|
||||
if(Character.isLowSurrogate(charsBackup.charAt(index)) &&
|
||||
Character.isHighSurrogate(charsBackup.charAt(index-1))) {
|
||||
numPairs++;
|
||||
index--;
|
||||
}
|
||||
index--;
|
||||
dnx--;
|
||||
}
|
||||
|
||||
// Chop dn+numPairs code points from the end of charsBackup
|
||||
// subSequence indices are start(inclusive) to end(exclusive)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue